spring編程式事務,spring編程
先看一下設定檔(簡單配置)
<pre name="code" class="html"><!-- 配置資料來源 --><bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"destroy-method="close"><property name="driverClass"><value>${jdbc.driverClassName}</value></property><property name="jdbcUrl"><value>${jdbc.url}</value></property><property name="user"><value>${jdbc.username}</value></property><property name="password"><value>${jdbc.password}</value></property><span style="white-space:pre"></span></bean>
spring交易管理員
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource"><ref bean="dataSource"/> </property> </bean>
這裡沒用spring注入transactionManager,當然你可以使用spring幫你注入,擷取到transactionManager就可以開啟事務,這裡使用編程式事務的,是因為我要用Connection
/** * 隨機擷取的串連,用完就釋放 */private Connection queryConnection;/** * 事務串連,有事務期間保留,事務提交後釋放 */private Connection transactionConn ;private DataSourceTransactionManager transactionManager = (DataSourceTransactionManager) ContextLoaderListener.getCurrentWebApplicationContext().getBean("transactionManager");/** * 事務狀態 */private TransactionStatus transactionStatus;
開啟事務和提交事務
public void closeQueryConn() {try {if (this.queryConnection!=null && !this.queryConnection.isClosed() && !this.queryConnection.equals(this.transactionConn)) {this.queryConnection.close();}} catch (SQLException e) {e.printStackTrace();} finally {this.queryConnection = null;}} /** * 釋放當前線程的session */public void releaseSession(){if(SessionFactory.getThreadSession() == this){SessionFactory.releaseSession();}else{this.closeConn();}}/** * 開啟事務 * @throws SQLException */public void beginTransaction() throws SQLException{if(this.transactionStatus == null|| this.transactionStatus.isCompleted()){//如果有事務串連就把普通查詢串連關閉,並把事務串連作為查詢串連this.closeQueryConn();DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();//spring事務的傳播行為 PROPAGATION_REQUIRED:如果當前存在事務,則加入該事務;如果當前沒有事務,則建立一個新的事務。transactionDefinition.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED);//事務的隔離等級 ISOLATION_READ_COMMITTED:表示一個事務只能讀取另一個事務已經提交的資料。該層級可以防止髒讀,這也是大多數情況下的推薦值。transactionDefinition.setIsolationLevel(DefaultTransactionDefinition.ISOLATION_READ_COMMITTED);//開啟事務this.transactionStatus = this.transactionManager.getTransaction(transactionDefinition);DataSource dataSourcel = this.transactionManager.getDataSource();this.transactionConn = DataSourceUtils.getConnection(dataSourcel);this.queryConnection = this.transactionConn;}}/** * 提交事務 * @throws SQLException */public void commitTransaction(){closeQueryConn();try {if (this.transactionConn != null && !this.transactionConn.isClosed()) {if (!this.transactionConn.getAutoCommit()) {try {//conn.commit();this.transactionManager.commit(transactionStatus);} catch (TransactionException e) {//conn.rollback();this.transactionManager.rollback(transactionStatus);} }this.transactionConn.close();}} catch (SQLException e) {e.printStackTrace();} finally{this.transactionConn=null;}}
針對以上的代碼,這裡做一些說明:
this.transactionStatus = this.transactionManager.getTransaction(transactionDefinition);開啟事務之後this.transactionConn = DataSourceUtils.getConnection(dataSourcel);
擷取的串連已經開啟事務,this.transactionConn.getAutoCommit()是false
(如果你開啟了一個事務沒有提交,又開啟一個事務即:
transactionStatus1 = this.transactionManager.getTransaction(transactionDefinition);
transactionStatus2 = this.transactionManager.getTransaction(transactionDefinition);
這兩個transactionStatus對象不是同一個對象,但是
transactionConn1 = DataSourceUtils.getConnection(dataSourcel);
transactionConn2 = DataSourceUtils.getConnection(dataSourcel);
transactionConn是同一個串連,
transactionManager.commit(transactionStatus2);不會導致transactionStatus1提交,但是transactionManager.commit(transactionStatus1)會提交transactionStatus2的內容,不過transactionStatus2依然顯示沒有提交,即transactionStatus2.isCompleted()為false)
提交事務之後,this.transactionManager.commit(transactionStatus);this.transactionConn.getAutoCommit()是true
交易隔離等級
隔離等級是指若干個並發的事務之間的隔離程度。TransactionDefinition 介面中定義了五個表示隔離等級的常量:
- TransactionDefinition.ISOLATION_DEFAULT:這是預設值,表示使用底層資料庫的預設隔離等級。對大部分資料庫而言,通常這值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED:該隔離等級表示一個事務可以讀取另一個事務修改但還沒有提交的資料。該層級不能防止髒讀和不可重複讀取,因此很少使用該隔離等級。
- TransactionDefinition.ISOLATION_READ_COMMITTED:該隔離等級表示一個事務只能讀取另一個事務已經提交的資料。該層級可以防止髒讀,這也是大多數情況下的推薦值。
- TransactionDefinition.ISOLATION_REPEATABLE_READ:該隔離等級表示一個事務在整個過程中可以多次重複執行某個查詢,並且每次返回的記錄都相同。即使在多次查詢之間有新增的資料滿足該查詢,這些新增的記錄也會被忽略。該層級可以防止髒讀和不可重複讀取。
- TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事務依次逐個執行,這樣事務之間就完全不可能產生幹擾,也就是說,該層級可以防止髒讀、不可重複讀取以及幻讀。但是這將嚴重影響程式的效能。通常情況下也不會用到該層級。
事務傳播行為
所謂事務的傳播行為是指,如果在開始當前事務之前,一個事務上下文已經存在,此時有若干選項可以指定一個事務性方法的執行行為。在TransactionDefinition定義中包括了如下幾個表示傳播行為的常量:
- TransactionDefinition.PROPAGATION_REQUIRED:如果當前存在事務,則加入該事務;如果當前沒有事務,則建立一個新的事務。
- TransactionDefinition.PROPAGATION_REQUIRES_NEW:建立一個新的事務,如果當前存在事務,則把當前事務掛起。
- TransactionDefinition.PROPAGATION_SUPPORTS:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式運行,如果當前存在事務,則把當前事務掛起。
- TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,如果當前存在事務,則拋出異常。
- TransactionDefinition.PROPAGATION_MANDATORY:如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。
- TransactionDefinition.PROPAGATION_NESTED:如果當前存在事務,則建立一個事務作為當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價於TransactionDefinition.PROPAGATION_REQUIRED。
這裡需要指出的是,前面的六種事務傳播行為是 Spring 從 EJB 中引入的,他們共用相同的概念。而 PROPAGATION_NESTED是 Spring 所特有的。以 PROPAGATION_NESTED 啟動的事務內嵌於外部事務中(如果存在外部事務的話),此時,內嵌事務並不是一個獨立的事務,它依賴於外部事務的存在,只有通過外部的事務提交,才能引起內部事務的提交,嵌套的子事務不能單獨提交。如果熟悉 JDBC 中的儲存點(SavePoint)的概念,那嵌套事務就很容易理解了,其實嵌套的子事務就是儲存點的一個應用,一個事務中可以包括多個儲存點,每一個嵌套子事務。另外,外部事務的復原也會導致嵌套子事務的復原。