1)spring的事務管理器:java
package org.springframework.transaction; public interface PlatformTransactionManager { TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; void commit(TransactionStatus status) throws TransactionException; void rollback(TransactionStatus status) throws TransactionException; }
2)spring的事務定義類:spring
package org.springframework.transaction; import java.sql.Connection; public interface TransactionDefinition { // 事務的傳播行爲 int PROPAGATION_REQUIRED = 0; // 若是當前存在事務,則加入該事務;若是當前沒有事務,則建立一個新的事務。這是默認值。 int PROPAGATION_SUPPORTS = 1; // 若是當前存在事務,則加入該事務;若是當前沒有事務,則以非事務的方式繼續運行。 int PROPAGATION_MANDATORY = 2; // 若是當前存在事務,則加入該事務;若是當前沒有事務,則拋出異常。 int PROPAGATION_REQUIRES_NEW = 3; // 建立一個新的事務,若是當前存在事務,則把當前事務掛起。 int PROPAGATION_NOT_SUPPORTED = 4; // 以非事務方式運行,若是當前存在事務,則把當前事務掛起。 int PROPAGATION_NEVER = 5; // 以非事務方式運行,若是當前存在事務,則拋出異常。 int PROPAGATION_NESTED = 6; // 若是當前存在事務,則建立一個事務做爲當前事務的嵌套事務來運行;若是當前沒有事務,則等價於PROPAGATION_REQUIRED。 // 事務的隔離級別 int ISOLATION_DEFAULT = -1; // 使用底層數據庫的默認隔離級別。 int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED; int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED; int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ; int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE; /** * Use the default timeout of the underlying transaction system, or none if timeouts are not supported. */ // 事務超時:指一個事務所容許執行的最長時間,若是超過該時間限制但事務尚未完成,則自動回滾事務。 // 默認爲底層事務系統的超時值,若是底層數據庫事務系統沒有設置超時值,那麼就是none,沒有超時限制。 int TIMEOUT_DEFAULT = -1; int getPropagationBehavior(); int getIsolationLevel(); int getTimeout(); boolean isReadOnly(); String getName(); }
3)spring中事務的回滾:sql
1)spring推薦以拋出異常的方式來回滾一個事務。 2)spring事務管理器會捕捉任何未處理的異常,而後根據相應的配置來判斷是否回滾拋出異常的事務。 1>默認配置下,spring只有在拋出的異常爲運行時unchecked異常時纔回滾該事務,也就是拋出的異常爲RuntimeException的子類(Errors也會致使事務回滾),而拋出checked異常則不會致使事務回滾。 2>能夠配置在拋出哪些異常時回滾事務,包括checked異常;也能夠明確設置哪些異常拋出時不回滾事務。
4)@Transactional註解數據庫
屬性 類型 描述 value String 可選的限定描述符,指定使用的事務管理器 propagation enum 可選的事務傳播行爲設置 isolation enum 可選的事務隔離級別設置 readOnly boolean 讀寫或只讀事務,默認讀寫 timeout int (in seconds granularity)事務超時時間設置 rollbackFor (繼承Throwable的)Class數組 致使事務回滾的異常類數組 rollbackForClassName (繼承Throwable的)類名數組 致使事務回滾的異常類名字數組 noRollbackFor (繼承Throwable的)Class數組 不會致使事務回滾的異常類數組 noRollbackForClassName (繼承Throwable的)類名數組 不會致使事務回滾的異常類名字數組 用法:能夠寫在類(接口)上,也能夠方法上。 1)看成用於類上時,該類的全部public方法將都具備該類型的事務屬性。 2)能夠在方法上使用該註解來覆蓋類級別上的設置。 3)Spring建議不要在接口或接口方法上使用該註解,由於只有在使用基於接口的代理時它纔會生效。 4)在private、protected、default的方法上使用該註解,則該註解將被忽略,也不會拋出任何異常。 5)默認狀況下,只有來自外部的方法調用纔會被AOP代理捕獲,也就是說:類內部方法調用本類內部的其它方法並不會引發事務行爲,即便被調用方法使用@Transactional註解進行修飾!
5)原理:express
1)spring的事務管理是經過spring的AOP(動態代理)來實現的。 spring中事務生效的一個前提:調用的方法必須被TransactionInterceptor攔截。 說明:只有經過TransactionInterceptor攔截的方法纔會被加入到spring的事務管理中。 舉例: 在方法A中調用@Transactional修飾的方法B時,若是方法A與方法B在同一個類TestClass中,則加在方法B上的@Transactional註解不起做用,由於此時方法A對方法B的調用是不會被攔截器攔截的! 解決辦法:在方法A中,使用當前的代理對象來調用方法B。eg:((TestClass)AopContext.currentProxy()).B(); 2)對於正常的事務管理,必須關閉數據庫的自動提交模式,spring會將底層鏈接的自動提交特性設置爲false。 package org.springframework.jdbc.datasource; public class DataSourceTransactionManager extends AbstractPlatformTransactionManager implements ResourceTransactionManager, InitializingBean { private DataSource dataSource; /** * This implementation sets the isolation level but ignores the timeout. */ [@Override](https://my.oschina.net/u/1162528) protected void doBegin(Object transaction, TransactionDefinition definition) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; Connection con = null; try { if (txObject.getConnectionHolder() == null || txObject.getConnectionHolder().isSynchronizedWithTransaction()) { Connection newCon = this.dataSource.getConnection(); if (logger.isDebugEnabled()) { logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction"); } txObject.setConnectionHolder(new ConnectionHolder(newCon), true); } txObject.getConnectionHolder().setSynchronizedWithTransaction(true); con = txObject.getConnectionHolder().getConnection(); Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition); txObject.setPreviousIsolationLevel(previousIsolationLevel); // Switch to manual commit if necessary. This is very expensive in some JDBC drivers, // so we don't want to do it unnecessarily (for example if we've explicitly // configured the connection pool to set it already). if (con.getAutoCommit()) { txObject.setMustRestoreAutoCommit(true); if (logger.isDebugEnabled()) { logger.debug("Switching JDBC Connection [" + con + "] to manual commit"); } con.setAutoCommit(false); // 【spring將底層鏈接的自動提交設置爲false】 } txObject.getConnectionHolder().setTransactionActive(true); int timeout = determineTimeout(definition); if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { txObject.getConnectionHolder().setTimeoutInSeconds(timeout); } // Bind the session holder to the thread. if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder()); } } catch (Throwable ex) { DataSourceUtils.releaseConnection(con, this.dataSource); throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex); } } }
6)配置文件:數組
1)註解的形式: spring + mybatis: 說明:mybatis會自動參與到spring事務的管理中,無需額外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的數據源與DataSourceTransactionManager引用的數據源一致便可,不然事務管理不起做用。 <!-- 定義事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 使用註解的形式管理事務 --> <tx:annotation-driven transaction-manager="transactionManager" /> <!-- 定義SqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:/mybatis_advertise.xml" /> </bean> spring + hibernate: <!-- 事務管理器 --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- 使用註解的形式管理事務 --> <tx:annotation-driven transaction-manager="transactionManager"/> <!-- 定義SessionFactory --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> </bean> 2)切面的形式: <!-- 定義事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 事務的處理策略:至關於註解形式中的@Transactional註解 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- 設置事務的屬性,至關於@Transactional註解的屬性 --> <tx:method name="query*" read-only="true" /> <tx:method name="save*" propagation="REQUIRED" /> <tx:method name="*" /> </tx:attributes> </tx:advice> <aop:config> <!--定義須要使用事務的切入點 --> <aop:pointcut id="myPointcut" expression="execution(* com.jxn.mybatis.service.*.*(..))" /> <!--將事務的處理策略應用到指定的切入點 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut" /> </aop:config>