當項目的數據須要持久化存儲時,不可避免要和數據庫交互。在交互過程當中,對事務的支持則是尤其重要。JDBC規範支持對事務的操做,在深刻淺出JDBC(一) - Connection與事務介紹一章中簡要介紹了JDBC事務相關的概念。JDBC將對不一樣數據庫的交互規範化,包括事務的操做,讓開發者能夠屏蔽不一樣數據庫的差別使用接口編程。但事務的開啓和關閉,以及事務的控制和配置仍是須要手動編碼控制,未免繁瑣且易出錯。Spring基於此之上,開放出一套事務管理機制,將開發者從繁瑣的事務控制中解脫出來,能夠便捷地執行事務控制。然而做爲開發者,便捷以後的原理也須要了解,才能更好地把控程序。接下來,我將從Spring事務管理的配置到原理逐步介紹其運行機制,本篇先介紹三種從原始到簡化的配置方式。mysql
以mybatis+mysql爲基礎,基本的xml配置以下spring
<!-- 數據源 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://192.168.1.160:3306/lichao"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> <property name="connectionProperties" value="useUnicode=true;autoReconnect=true;failOverReadOnly=false;characterEncoding=utf8;zeroDateTimeBehavior=convertToNull;allowMultiQueries=true"></property> </bean> <!-- mybatis的Session工廠 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="configLocation" value="mybatis-config.xml"></property> <property name="dataSource" ref="dataSource"></property> </bean> <!-- UserMapper代理 --> <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="com.lcifn.spring.transaction.mapper.UserMapper"/> <property name="sqlSessionFactory" ref="sqlSessionFactory"></property> </bean> <!-- userManager實例 --> <bean id="userManager" class="com.lcifn.spring.transaction.manager.UserManager"> <property name="userMapper" ref="userMapper"></property> </bean>
這裏對mybatis的配置就不過多介紹了,事務定義在UserManager層,UserManager中定義一個批量操做的方法,來驗證事務。sql
@Slf4j public class UserManager { @Getter @Setter private UserMapper userMapper; public void batchOperator(){ User user = new User("lily", 25); // 插入一條user記錄 int insertRows = userMapper.insert(user); if(insertRows > 0){ user = userMapper.getUser(user.getId()); log.info(user.toString()); user.setName("jack"); user.setAge(28); // 更新user記錄 userMapper.update(user); } } }
使用TransactionProxyFactoryBean直接代理UserManager數據庫
<!-- userManager事務代理類 --> <bean id="userManagerTransactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!-- 原始對象 --> <property name="target" ref="userManager"/> <!-- 事務屬性 --> <property name="transactionAttributes"> <props> <prop key="batchOperator">PROPAGATION_REQUIRED</prop> </props> </property> <!-- 事務管理器 --> <property name="transactionManager"> <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> </property> </bean>
TransactionProxyFactoryBean使用ProxyFactory生成代理類完成對事務的處理。transactionAttributes接收一個properties對象,key爲方法名,value爲方法對應的事務配置,配置規則的解析類是TransactionAttributeEditor,配置規則以下express
配置名稱 | 前綴 | 配置選項 | 默認配置 |
---|---|---|---|
傳播屬性 | PROPAGATION_ | REQUIRED|MANDATORY|REQUIRES_NEW|NESTED等 | REQUIRED |
隔離級別 | ISOLATION_ | READ_UNCOMMITTED|READ_COMMITTED|REPEATABLE_READ|SERIALIZABLE | DEFAULT |
超時時間 | timeout_ | 單位:秒 | -1 |
只讀 | readOnly | 不配置表示可讀可寫,配置了便可讀 | 可讀可寫 |
不回滾異常規則(noRollbackFor) | + | 異常類包路徑 | 無 |
回滾異常規則(rollbackFor | - | 異常類包路徑 | 運行時異常及Error |
事務管理器則是PlatformTransactionManager的子類,實現其獲取事務,提交事務和回滾事務三個方法,這裏使用jdbc的事務管理器DataSourceTransactionManager。apache
寫一個測試類編程
public class TransactionProxyFactoryBeanTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("TransactionProxyFactoryBean.xml"); UserManager userManager = context.getBean("userManagerTransactionProxy", UserManager.class); userManager.batchOperator(); } }
經過上下文獲取userManager的代理對象,並執行操做方法,能夠在日誌中看到事務的運行。mybatis
上面的配置方式只能對一個類代理,用來研究Spring事務管理的機制很好,但在項目中,須要批量地配置事務管理。使用Spring AOP中的Advice和Pointcut的方式就能完成,並且Spring定義了tx:advice標籤來支持配置。app
<!-- 事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 事務Advice加強配置 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="get*" read-only="true" /> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- 配置事務aop --> <aop:config> <aop:pointcut id="userPointcut" expression="execution(* com.lcifn.spring.transaction.manager.UserManager.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="userPointcut" /> </aop:config>
在tx:advice中,須要配置事務管理器,在其子標籤中tx:attributes中配置方法和事務配置的關係。方法配置支持通配符匹配多個方法,事務的配置同上面的基本一致,只是Spring提供了屬性標籤來方便配置。配置aop:pointcut切入點,決定哪些類哪些方法須要被代理。測試
寫一個測試類,這裏直接獲取userManager,由於aop:config自動代理的緣由,返回的已是代理對象了。
public class TransactionTxAdvice { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("transaction-tx-advice.xml"); UserManager userManager = context.getBean("userManager", UserManager.class); userManager.batchOperator(); } }
XML配置的方式已經能大大地節約配置操做,但若是要對每一個方法細粒度地配置,xml也會變得很繁瑣。怎麼辦?這時咱們想到了註解。Spring提供了超級便捷的方式,經過註解在類或方法上完成事務的控制和配置。
<!-- 事務註解驅動 --> <tx:annotation-driven transaction-manager="transactionManager"/> <!-- 事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean>
xml中的就是上面簡單的配置,而在類或方法上使用@Transactional註解,好比上面的batchOperator方法能夠配置以下
// 事務傳播屬性爲必須,超時時間爲2秒 @Transactional(propagation = Propagation.REQUIRED, timeout = 2)
每一個方法是否開啓事務,以及事務配置什麼屬性均可以經過這種方式精細化地控制。
Spring的事務隔離級別支持sql標準定義的四個級別:Read Uncommitted(未受權讀取),Read Committed(受權讀取),Repeatable Read(可重複讀取),Serializable(序列化)。可在深刻淺出JDBC(一) - Connection與事務介紹中瞭解詳細。
Spring定義了事務傳播方式,指的是多個層次(非同一個類)的事務方法執行時的規則,即事務在方法中的傳播方式。Spring定義了七種傳播行爲(包括事務的新建和回滾行爲):
對於事務的建立,有一點須要着重強調。JDBC默認鏈接的提交方式爲自動,若是開啓事務,即將自動提交改成手動。所以開啓一個新的事務,便是獲取一個新的鏈接, ,具體實現也會在以後的源碼解析裏提示。下一章咱們來解析TransactionProxyFactoryBean的實現原理。