資料庫事務(二): java事務處理
在平時我們做業務開發的時候,資料庫操作一般都需要使用到事務。如果是基於spring的專案,很簡單,有兩種方法:
1.配置一個DataSource給spring容器託管,再配置一個TransactionManager事務管理器,然後在需要事務的方法上加上Transactional註解就ok,這樣的話方法結束後事務才提交。
2. 配置好TransactionManager事務管理器後,可以在配置一個程式設計式事務模版,資料庫操作時直接呼叫程式設計式事務就ok,而這樣的話執行完所有sql語句後事務就提交,無需等待整個方法跑完。
這些簡單操作的背後,框架給我們做了太多的工作,作為一個有技術追求的程式設計師,應該瞭解Java事務的底層工作原理。
JDBC提供的事務處理api
java通過jdbc與資料庫進行互動,現在一般不直接使用jdbc,大多采用mybatis、hibernate等orm框架。但這些框架的底層,還是繞不開jdbc,事務處理同樣如此。
JDBC提供的事務處理API非常少,請不要被Spring中事務處理的那一堆原始碼所打擊得信心盡失,這些框架提供的事務處理功能歸根結底主要通過以java.sql.Connection類的方法完成:
Connection.setAutoCommit(boolean);
Connection.commit();
Connection.rollback();
在javax.sql.DataSource中,通過我們自己配置的資料庫DataSource,可以獲得Connection物件:
public interface DataSource extends CommonDataSource, Wrapper {
Connection getConnection() throws SQLException;
...
}
java底層的事務處理就是依靠上訴的兩個物件和幾個方法實現的。
舉一個例子,銀行轉賬方法transfer,將A賬號下的一筆前轉到B賬號下,分兩步,從A中扣除一筆錢,再在B中扣除一筆錢,程式碼如下:
public class BankServiceImpl implements BankService{
private DataSource dataSource;
//建構函式,傳如DataSource物件
public BankServiceImpl(DataSource dataSource){
this.dataSource=dataSource;
}
//轉賬方法
public void transfer(int fromId, int toId, int amount){
Connection connection = dataSource.getConnection();
//自動提交設為false
connection.setAutoCommit(false);
//呼叫倉儲層方法扣款和加款
bankDao.withdraw(fromId,amount);
bankDao.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();
}
}
}
}
在上訴transfer方法中,要想保證資料的一致性,也就是事務起作用,必須保證整個轉賬流程都使用的是同一個Connection物件。也就是說,BankService.transfer()方法、BankDao.withdraw()、BankDao.deposit()方法,需呼叫同一個Connection物件。
因此,上訴的事務處理是錯誤的,無法保證Service和Dao層呼叫的是同一個Connection物件。
實際上,java事務處理,包括其它的一些框架spring,最關鍵的地方其實也是如何保證呼叫同一個Connection物件。
下一篇我們探討如何自己構建一個TransactionManager,保證獲得同一個Connection物件,從而實現可靠的事務處理,保證資料一致性。