日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java事务处理全解析(二)——失败的案例

發布時間:2024/10/12 java 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java事务处理全解析(二)——失败的案例 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在本系列的上一篇文章中,我們講到了Java事務處理的基本問題,并且講到了Service層和DAO層,在本篇文章中,我們將以BankService為例學習一個事務處理失敗的案例。

?

BankService的功能為:某個用戶有兩個賬戶,分別為銀行賬戶和保險賬戶,并且有各自的賬戶號,BankService的transfer方法從該用戶的銀行賬戶向保險賬戶轉帳,兩個DAO分別用于對兩個賬戶表的存取操作。

定義一個BankService接口如下:

package davenkin;public interface BankService {public void transfer(int fromId, int toId, int amount); }

?

在兩個DAO對象中,我們通過傳入的同一個DataSource獲得Connection,然后通過JDBC提供的API直接對數據庫進行操作。

定義操作銀行賬戶表的DAO類如下:

package davenkin.step1_failure;import javax.sql.DataSource; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException;public class FailureBankDao {private DataSource dataSource;public FailureBankDao(DataSource dataSource) {this.dataSource = dataSource;}public void withdraw(int bankId, int amount) throws SQLException {Connection connection = dataSource.getConnection();PreparedStatement selectStatement = connection.prepareStatement("SELECT BANK_AMOUNT FROM BANK_ACCOUNT WHERE BANK_ID = ?");selectStatement.setInt(1, bankId);ResultSet resultSet = selectStatement.executeQuery();resultSet.next();int previousAmount = resultSet.getInt(1);resultSet.close();selectStatement.close();int newAmount = previousAmount - amount;PreparedStatement updateStatement = connection.prepareStatement("UPDATE BANK_ACCOUNT SET BANK_AMOUNT = ? WHERE BANK_ID = ?");updateStatement.setInt(1, newAmount);updateStatement.setInt(2, bankId);updateStatement.execute();updateStatement.close();connection.close();} }

?

FailureBankDao的withdraw方法,從銀行賬戶表(BANK_ACCOUNT)中帳號為bankId的用戶賬戶中取出數量為amount的金額。

采用同樣的方法,定義保險賬戶的DAO類如下:

package davenkin.step1_failure;import javax.sql.DataSource; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException;public class FailureInsuranceDao {private DataSource dataSource;public FailureInsuranceDao(DataSource dataSource){this.dataSource = dataSource;}public void deposit(int insuranceId, int amount) throws SQLException {Connection connection = dataSource.getConnection();PreparedStatement selectStatement = connection.prepareStatement("SELECT INSURANCE_AMOUNT FROM INSURANCE_ACCOUNT WHERE INSURANCE_ID = ?");selectStatement.setInt(1, insuranceId);ResultSet resultSet = selectStatement.executeQuery();resultSet.next();int previousAmount = resultSet.getInt(1);resultSet.close();selectStatement.close();int newAmount = previousAmount + amount;PreparedStatement updateStatement = connection.prepareStatement("UPDATE INSURANCE_ACCOUNT SET INSURANCE_AMOUNT = ? WHERE INSURANCE_ID = ?");updateStatement.setInt(1, newAmount);updateStatement.setInt(2, insuranceId);updateStatement.execute();updateStatement.close();connection.close();} }

?

FailureInsuranceDao類的deposit方法向保險賬戶表(INSURANCE_ACCOUNT)存入amount數量的金額,這樣在BankService中,我們可以先調用FailureBankDao的withdraw方法取出一定金額的存款,再調用FailureInsuranceDao的deposit方法將該筆存款存入保險賬戶表中,一切看似OK,實現BankService接口如下:

package davenkin.step1_failure;import davenkin.BankService;import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException;public class FailureBankService implements BankService{private FailureBankDao failureBankDao;private FailureInsuranceDao failureInsuranceDao;private DataSource dataSource;public FailureBankService(DataSource dataSource) {this.dataSource = dataSource;}public void transfer(int fromId, int toId, int amount) {Connection connection = null;try {connection = dataSource.getConnection();connection.setAutoCommit(false);failureBankDao.withdraw(fromId, amount);failureInsuranceDao.deposit(toId, amount);connection.commit();} catch (Exception e) {try {assert connection != null;connection.rollback();} catch (SQLException e1) {e1.printStackTrace();}} finally {try{assert connection != null;connection.close();} catch (SQLException e){e.printStackTrace();}}}public void setFailureBankDao(FailureBankDao failureBankDao) {this.failureBankDao = failureBankDao;}public void setFailureInsuranceDao(FailureInsuranceDao failureInsuranceDao) {this.failureInsuranceDao = failureInsuranceDao;} }

?

在FailureBankService的transfer方法中,我們首先通過DataSource獲得Connection,然后調用connection.setAutoCommit(false)已開啟手動提交模式,如果一切順利,則commit,如果出現異常,則rollback。 接下來,開始測試我們的BankService吧。

為了準備測試數據,我們定義個BankFixture類,該類負責在每次測試之前準備測試數據,分別向銀行賬戶(1111)和保險賬戶(2222)中均存入1000元。BankFixture還提供了兩個helper方法(getBankAmount和getInsuranceAmount)幫助我們從數據庫中取出數據以做數據驗證。我們使用HSQL數據庫的in-memory模式,這樣不用啟動數據庫server,方便測試。BankFixture類定義如下:

package davenkin;import org.junit.Before; import javax.sql.DataSource; import java.sql.*;public class BankFixture {protected final DataSource dataSource = DataSourceFactory.createDataSource();@Beforepublic void setUp() throws SQLException{Connection connection = dataSource.getConnection();Statement statement = connection.createStatement();statement.execute("DROP TABLE BANK_ACCOUNT IF EXISTS");statement.execute("DROP TABLE INSURANCE_ACCOUNT IF EXISTS");statement.execute("CREATE TABLE BANK_ACCOUNT (\n" +"BANK_ID INT,\n" +"BANK_AMOUNT INT,\n" +"PRIMARY KEY(BANK_ID)\n" +");");statement.execute("CREATE TABLE INSURANCE_ACCOUNT (\n" +"INSURANCE_ID INT,\n" +"INSURANCE_AMOUNT INT,\n" +"PRIMARY KEY(INSURANCE_ID)\n" +");");statement.execute("INSERT INTO BANK_ACCOUNT VALUES (1111, 1000);");statement.execute("INSERT INTO INSURANCE_ACCOUNT VALUES (2222, 1000);");statement.close();connection.close();}protected int getBankAmount(int bankId) throws SQLException{Connection connection = dataSource.getConnection();PreparedStatement selectStatement = connection.prepareStatement("SELECT BANK_AMOUNT FROM BANK_ACCOUNT WHERE BANK_ID = ?");selectStatement.setInt(1, bankId);ResultSet resultSet = selectStatement.executeQuery();resultSet.next();int amount = resultSet.getInt(1);resultSet.close();selectStatement.close();connection.close();return amount;}protected int getInsuranceAmount(int insuranceId) throws SQLException{Connection connection = dataSource.getConnection();PreparedStatement selectStatement = connection.prepareStatement("SELECT INSURANCE_AMOUNT FROM INSURANCE_ACCOUNT WHERE INSURANCE_ID = ?");selectStatement.setInt(1, insuranceId);ResultSet resultSet = selectStatement.executeQuery();resultSet.next();int amount = resultSet.getInt(1);resultSet.close();selectStatement.close();connection.close();return amount;}}

?

編寫的Junit測試繼承自BankFixture類,測試代碼如下:

package davenkin.step1_failure;import davenkin.BankFixture; import org.junit.Test; import java.sql.SQLException; import static junit.framework.Assert.assertEquals;public class FailureBankServiceTest extends BankFixture {@Testpublic void transferSuccess() throws SQLException{FailureBankDao failureBankDao = new FailureBankDao(dataSource);FailureInsuranceDao failureInsuranceDao = new FailureInsuranceDao(dataSource);FailureBankService bankService = new FailureBankService(dataSource);bankService.setFailureBankDao(failureBankDao);bankService.setFailureInsuranceDao(failureInsuranceDao);bankService.transfer(1111, 2222, 200);assertEquals(800, getBankAmount(1111));assertEquals(1200, getInsuranceAmount(2222));}@Testpublic void transferFailure() throws SQLException{FailureBankDao failureBankDao = new FailureBankDao(dataSource);FailureInsuranceDao failureInsuranceDao = new FailureInsuranceDao(dataSource);FailureBankService bankService = new FailureBankService(dataSource);bankService.setFailureBankDao(failureBankDao);bankService.setFailureInsuranceDao(failureInsuranceDao);int toNonExistId = 3333;bankService.transfer(1111, toNonExistId, 200);assertEquals(1000, getInsuranceAmount(2222));assertEquals(1000, getBankAmount(1111));} }

?

運行測試,第一個測試(transferSuccess)成功,第二個測試(transferFailure)失敗。

分析錯誤,原因在于:我們分別從FailureBankService,FailureBankDao和FailureInsuranceDao中調用了三次dataSource.getConnection(),亦即我們創建了三個不同的Connection對象,而Java事務是作用于Connection之上的,所以從在三個地方我們開啟了三個不同的事務,而不是同一個事務。

第一個測試之所以成功,是因為在此過程中沒有任何異常發生。雖然在FailureBankService中將Connection的提交模式改為了手動提交,但是由于兩個DAO使用的是各自的Connection對象,所以兩個DAO中的Connection依然為默認的自動提交模式。

在第二個測試中,我們給出一個不存在的保險賬戶id(toNonExistId),就是為了使程序產生異常,然后在assertion語句中驗證兩張表均沒有任何變化,但是測試在第二個assertion語句處出錯。發生異常時,銀行賬戶中的金額已經減少,而雖然程序發生了rollback,但是調用的是FailureBankService中Connection的rollback,而不是FailureInsuranceDao中Connection的,對保險賬戶的操作根本就沒有執行,所以保險賬戶中依然為1000,而銀行賬戶卻變為了800。

因此,為了使兩個DAO在同一個事務中,我們應該在整個事務處理過程中使用一個Connection對象,在下一篇文章中,我們將講到通過共享Connection對象的方式達到事務處理的目的。

轉載于:https://www.cnblogs.com/fengjian/p/4209651.html

與50位技術專家面對面20年技術見證,附贈技術全景圖

總結

以上是生活随笔為你收集整理的Java事务处理全解析(二)——失败的案例的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 正在播放日韩 | 国产成人无码精品久久久久久 | 黄色片子看看 | 四虎黄色 | 色av一区二区 | 视频在线播| 九九九网站 | 中文字幕乱码亚洲精品一区 | 精品国模 | 97小视频| 成人精品一区二区三区电影 | 久草蜜桃| 国产不卡精品视频 | 成人h动漫精品一区二区无码 | 亚洲午夜无码久久 | 中文字幕av免费在线观看 | 胸网站| 六月丁香av | 黄色欧美一级片 | 久久久久亚洲AV成人无码国产 | 日本加勒比中文字幕 | 欲乱美女 | 日本裸体视频 | 华人永久免费 | 欧美激情15p | www欧美日韩 | 国产精品色在线网站 | 青青青免费视频观看在线 | 国产色91 | 人人草人人爽 | 欧美黄色大片网站 | 99久久久无码国产精品性青椒 | 女人18毛片毛片毛片毛片区二 | 久久av红桃一区二区小说 | 四虎影院色 | 日韩欧美激情在线 | 青青青青青草 | 欧美性一级 | 美女啪啪国产 | 91毛片观看| 超碰在线免费看 | 手机亚洲第一页 | 亚洲午夜精品 | 欧美一区二区三区视频 | 午夜在线你懂的 | 91美女视频在线观看 | 五月天丁香激情 | 国产黄色一级网站 | 国产视频一区二区三区四区 | cao在线 | 成年人午夜视频 | 天天干天天上 | 成人在线免费电影 | 国产精品网站在线观看 | 尤物国产精品 | xx久久 | 精品无码久久久久国产 | 都市激情自拍 | 欧美日韩久 | 成人国产片 | 日韩一区中文字幕 | 91麻豆精品91久久久久同性 | heyzo北岛玲在线播放 | 男女午夜视频在线观看 | 日韩精品久 | aaaa免费视频 | 人妻无码中文字幕免费视频蜜桃 | 日韩一区免费视频 | 美丽的姑娘在线观看免费 | 日韩欧美中文字幕精品 | 少妇无码吹潮 | 97超视频 | 久草视频免费在线播放 | 欧美一及片 | 手机看片久久久 | 亚洲av无码专区在线电影 | 国产不卡在线观看 | 公交顶臀绿裙妇女配视频 | 欧美 日韩 国产 在线 | 国产欧美一区二区三区另类精品 | 国产在线精品一区二区三区 | 国产九色在线播放九色 | 欧美在线一区视频 | 国产精品久久久久久久专区 | 777色 | 国产日批 | 亚洲综合日韩 | 男女视频免费看 | 女人性做爰24姿势视频 | 成人黄色a| 午夜看片在线观看 | 亚洲在线播放 | 天堂а在线中文在线新版 | 成人亚洲国产 | 夫妻淫语绿帽对白 | 久草影视在线 | 亚洲一区二区高清视频 | 色桃av| 波多野结衣视频在线观看 |