天溫習spring的事務處理機制,總結以下javascript
- 對於SQL事務的概念以及ACID性質,能夠參見個人另外一篇博文 http://kingj.iteye.com/admin/blogs/1675011
- spring的管理的事務能夠分爲以下2類:
- 邏輯事務 在spring中定義的事務一般指邏輯事務,提供比物理事務更抽象,方便的事務配置管理,但也基於物理事務
- 物理事務 特定於數據庫的事務
- spring中支持一下2中事務聲明方式
- 編程式事務 當系統須要明確的,細粒度的控制各個事務的邊界,應選擇編程式事務
- 聲明式事務 當系統對於事務的控制粒度較粗時,應該選擇申明式事務
- 不管你選擇上述何種事務方式去實現事務控制,spring都提供基於門面設計模式的事務管理器供選擇,以下是spring事務中支持的事務管理器
-
事務管理器實現(org.springframework.*) |
使用時機 |
jdbc.datasource.DataSourceTransactionManager |
使用jdbc的抽象以及ibatis支持 |
orm.hibernate.HibernateTransactionManager |
使用hibernate支持(默認3.0如下版本) |
orm.hibernate3.HibernateTransactionManager |
使用hibernate3支持 |
transaction.jta.JtaTransactionManager |
使用分佈式事務(分佈式數據庫支持) |
orm.jpa.JpaTransactionManager |
使用jpa作爲持久化工具 |
orm.toplink.TopLinkTransactionManager |
使用TopLink持久化工具 |
orm.jdo.JdoTransactionManager |
使用Jdo持久化工具 |
jms.connection.JmsTransactionManager |
使用JMS 1.1+ |
jms.connection.JmsTransactionManager102 |
使用JMS 1.0.2 |
transaction.jta.OC4JJtaTransactionManager |
使用oracle的OC4J JEE容器 |
transaction.jta.WebLogicJtaTransactionManager |
在weblogic中使用分佈式數據庫 |
jca.cci.connection.CciLocalTransactionManager |
使用jrping對J2EE Connector Architecture (JCA)和Common Client Interface (CCI)的支持 |
UML結構圖以下java
四、各類事務管理器的定義以下 web
- 一 :咱們來對單個方法的事務傳播機制進行一個瞭解
- REQUIRED, REQUIRES_NEW
- junit代碼
-
- @Test
- public void testAddBook()throws Exception{
- BookService bs=(BookService)this.getBean("bookService");
- bs.addBook();
- }
- addBook()代碼
-
- public void addBook() throws Exception{
- this.jdbcTemplate.execute(ADD_BOOK);
- throw new RuntimeException("throw runtime exception in outter transaction");
- }
- 執行junit後,控制檯以下
-
- 一、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]
- 二、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback
- 三、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@a2b392]
- 能夠知道,當addBook()方法的事務傳播機制爲REQUIRED, REQUIRES_NEW ,而且拋出運行時異常時,將會回滾事務。
- 當addBook()方法拋出受檢查的異常時,將不會回滾事務。
- addBook()方法以下:
-
- public void addBook() throws Exception{
- this.jdbcTemplate.execute(ADD_BOOK);
- throw new Exception("throw runtime exception in outter transaction");
- }
-
- 一、[DEBUG,DataSourceTransactionManager,main] Creating new transaction with name [com.zx.spring.BookService.addBook]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
- 二、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]
- 三、[DEBUG,DataSourceTransactionManager,main] Initiating transaction commit
- 四、[DEBUG,DataSourceTransactionManager,main] Committing JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@4310d0]
- 第3行可知,事務提交,不回滾
- MANDATORY
- 代碼同上所示,只是事務傳播機制改成 MANDATORY
-
- <tx:advice id="txAdvice" transaction-manager="txManager">
- <tx:attributes>
- <tx:method name="addUser" propagation="REQUIRED" />
- <tx:method name="addBook" propagation="MANDATORY"/>
- </tx:attributes>
- </tx:advice>
- 運行junit後,因爲單個方法執行沒有指定任何事務傳播機制,所以拋出異常。
-
- org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
- at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:357)
- NESTED
- NESTED內嵌事務,若是沒有外層事務,則新建一個事務,行爲同REQUIRED同樣
- NEVER
- NEVER不會以事務方式運行,執行junit代碼後,控制檯以下
-
- 一、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]
- 二、[DEBUG,DataSourceUtils,main] Registering transaction synchronization for JDBC Connection
- 三、[DEBUG,DataSourceTransactionManager,main] Should roll back transaction but cannot - no transaction available
- 因爲發生運行時異常,事務本應該回滾,可是在第三行能夠知道,因爲事務傳播機制爲NEVER,所以找不到事務進行回滾,數據庫只添加了一條記錄。
- SUPPORTS
- 單個方法 調用時supports行爲同NEVER同樣,不會建立事務,只是若是有事務,則會加入到當前事務中去,具體的行爲下面有分析。
- NOT_SUPPORTED
- 單個方法被執行時,不會建立事務。若是當前有事務,將封裝事務掛起,知道該方法執行完成再恢復封裝事務,繼續執行。
二:在瞭解了單個方法的事務傳播機制後,咱們來配置多個方法調用之間的傳播機制的行爲
- 一、咱們將UserService和BookService中的addUser(),addBook()方法分別配置爲REQUIRED,並在addBook()方法中拋出運行時異常。
-
- <tx:advice id="txAdvice" transaction-manager="txManager">
- <tx:attributes>
- <tx:method name="addUser" propagation="REQUIRED"/>
- <tx:method name="addBook" propagation="REQUIRED"/>
- </tx:attributes>
- </tx:advice>
-
- 一、public void addUser()throws Exception {
- 二、 this.bs.addBook();
- 三、 this.jdbcTemplate.execute(ADD_USER);
- }
-
- 四、public void addBook() throws Exception{
- 五、 this.jdbcTemplate.execute(ADD_BOOK);
- 六、 throw new RuntimeException("跳出執行");
- }
- 測試用例以下
-
- @Test
- public void testAddUser()throws Exception{
- UserService us=(UserService)this.getBean("userService");
- us.addUser();
- }
- 執行前數據庫以下
- 執行後,console控制檯輸出以下:
-
- [DEBUG,DataSourceTransactionManager,main]
-
- Creating new transaction with name [com.zx.spring.UserService.addUser]:
- PROPAGATION_REQUIRED,ISOLATION_DEFAULT
上面輸出能夠知道,spring自動給addUser()方法切入了事務,事務隔離級別爲數據庫默認級別。
- 咱們再觀察控制檯輸出
-
- 一、[DEBUG,DataSourceTransactionManager,main] Acquired Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1973fc5] for JDBC transaction
- 二、[DEBUG,DataSourceTransactionManager,main] Switching JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1973fc5] to manual commit
- 三、[DEBUG,DataSourceTransactionManager,main] Participating in existing transaction
- 四、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]
- 五、[DEBUG,DataSourceTransactionManager,main] Participating transaction failed - marking existing transaction as rollback-only
- 六、[DEBUG,DataSourceTransactionManager,main] Setting JDBC transaction [com.mchange.v2.c3p0.impl.NewProxyConnection@1973fc5] rollback-only
- 七、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback
- 八、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1973fc5]
- 第一行jdbc事務管理器從c3p0鏈接池中獲取一個連接
- 第二行設置jdbc事務提交方式爲手動提交
- 代碼執行到方法addUser()中第一行時,因爲addUser()方法的事務級別爲REQUIRED的所以,事務管理器開始了一個事務。執行到第二行addBook()時,因爲addBook()方法的事務傳播行爲爲REQUIRED的,咱們知道REQUIRED方式是若是有一個事務,則加入事務中,若是沒有,則新建一個事務。由控制檯輸出的第3行能夠知道,addBook方法加入到了addUser方法的事務當中去,接着第4行執行了插入t_book語句,因爲addBook()方法在第6行時,拋出了運行時異常,所以當前事務失敗,可從控制檯輸出第5行得知。
- 因爲addUser()和addBook()方法共享了一個事務,在addBook()方法中又拋出了運行時異常,所以事務必須回滾,這由數據庫查詢可知。
- 若是咱們將addBook()方法中拋出的運行時異常改成checked異常的話,會是什麼結果呢?
-
- 七、public void addBook() throws Exception{
- 八、 this.jdbcTemplate.execute(ADD_BOOK);
- 九、 throw new Exception("跳出執行");
- }
-
- 九、[DEBUG,DataSourceTransactionManager,main] Acquired Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1973fc5] for JDBC transaction
- 十、[DEBUG,DataSourceTransactionManager,main] Switching JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1973fc5] to manual commit
- 十一、[DEBUG,DataSourceTransactionManager,main] Participating in existing transaction
- 十二、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]
- 1三、[DEBUG,DataSourceTransactionManager,main] Initiating transaction commit
- 1四、[DEBUG,DataSourceTransactionManager,main] Releasing JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@457d21] after transaction
- 由控制檯輸出第13行能夠知道,除了addBook()方法中拋出的檢查異常被忽略以外,其它的同上面的一致。再看數據庫能夠知道,addBook()方法和被執行了,addUser()方法被拋出的檢查異常終止調用。
- 若是咱們給addUser()方法指定rollback-for屬性,那麼addBook()方法的事務回回滾嗎?
-
- public void addBook() throws Exception{
- this.jdbcTemplate.execute(ADD_BOOK);
- throw new Exception("跳出執行");
- }
-
- <tx:advice id="txAdvice" transaction-manager="txManager">
- <tx:attributes>
- <tx:method name="addUser" propagation="REQUIRED" <span style="color: #ff0000;"><span style="color: #888888;">rollback-for="Exception"</span></span>/>
- <tx:method name="addBook" propagation="REQUIRED"/>
- </tx:attributes>
- </tx:advice>
- 控制檯輸出以下
-
- 1五、[DEBUG,DataSourceTransactionManager,main] Participating in existing transaction
- 1六、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]
- 1七、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback
- 1八、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@121d383]
可見,當指定了rollback-for屬性時,只要拋出了指定的異常,事務就會回滾。
- 二、上面咱們討論了兩個方法都指定爲事務傳播機制爲REQUIRED,那麼咱們來改變如下addBook()方的事務傳播機制改成NEVER ,來看看它們的效果
-
- <tx:advice id="txAdvice" transaction-manager="txManager">
- <tx:attributes>
- <tx:method name="addUser" propagation="REQUIRED" />
- <tx:method name="addBook" propagation="NEVER"/>
- </tx:attributes>
- </tx:advice>
執行Junit測試後發現,控制檯輸出以下:
-
- org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
- at org.springframework.transaction.support.AbstractPlatformTransactionManager.handleExistingTransaction(AbstractPlatformTransactionManager.java:399)
- at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:347)
- at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:335)
Existing transaction found for transaction marked with propagation 'never',也就是說addBook不該該在事務中運行,可是addUser這個客戶端調用者卻有一個事務,所以報錯。
- 三、咱們接着將addBook()方法的事務傳播機制改成MANDATORY
-
- <tx:advice id="txAdvice" transaction-manager="txManager">
- <tx:attributes>
- <tx:method name="addUser" propagation="REQUIRED" />
- <tx:method name="addBook" propagation="MANDATORY"/>
- </tx:attributes>
- </tx:advice>
執行junit後,控制檯輸出以下
-
- [DEBUG,DataSourceTransactionManager,main] Participating in existing transaction
- [DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]
- [DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_user(id,name) values(1,'duck')]
- [DEBUG,DataSourceTransactionManager,main] Initiating transaction commit
能夠知道當前和REQUIRED同樣的傳播行爲。
- 四、咱們將addBook()方法的事務傳播機制改成NESTED-內嵌事務,那麼傳播機制之間會怎麼互相影響呢?
-
- <tx:advice id="txAdvice" transaction-manager="txManager">
- <tx:attributes>
- <tx:method name="addUser" propagation="REQUIRED" />
- <tx:method name="addBook" propagation="NESTED"/>
- </tx:attributes>
- </tx:advice>
- addBook()方法以下,依然拋出(checked-exception)檢查異常。
- public void addBook() throws Exception{
- this.jdbcTemplate.execute(ADD_BOOK);
- throw new RuntimeException("跳出執行");
- }
- addUser()方法以下,在方法體中捕獲addBook()拋出的異常。若是不捕獲異常,addUser()方法將會被終止。
- public void addUser()throws Exception {
- try {
- this.bs.addBook();
- }catch(Exception e) {
- e.printStackTrace();
- }
- this.jdbcTemplate.execute(ADD_USER);
- }
執行junit後,控制檯輸出以下
-
- 一、[DEBUG,DataSourceTransactionManager,main] Creating new transaction with name [com.zx.spring.UserService.addUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
- 二、[DEBUG,DataSourceTransactionManager,main] Creating nested transaction with name [com.zx.spring.BookService.addBook]
- 三、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]
- 四、[DEBUG,DataSourceTransactionManager,main] Rolling back transaction to savepoint
- 五、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_user(id,name) values(1,'duck')]
- 六、[DEBUG,DataSourceTransactionManager,main] Initiating transaction commit
- 七、[DEBUG,DataSourceTransactionManager,main] Committing JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@10e164e]
- 由上面的輸出能夠,第一行爲執行addUser()方法,事務管理器開始了一個事務,
- 第二行執行到addBook()方法,因爲addBook()方法的事務傳播機制爲NESTED內嵌事務,所以,開始一個新的事務。
- 第三行能夠知道插入語句,因爲addBook()方法內部拋出RuntimeException,所以內部嵌套事務回滾到外層事務建立的保存點。
- 注意這個地方,咱們拋出的是運行時異常,若是咱們拋出受檢查的異常,那麼spring會默認的忽略此異常。下面我會詳細闡述。
- 若是內層事務拋出檢查異常,那麼外層事務將忽略此異常,可是會產生一個問題。那就是:外層事務使用jdbc的保存點API來實現嵌套事務,
- 可是數據庫不必定支持。我作測試的是oracle數據庫,jdbc事務管理器在內層事務拋出檢查異常後,將會在內層事務結束後,釋放外層事務
- 建立的保存點,這是時候數據庫不必定支持。所以可能會拋出以下異常:
-
- java.sql.SQLException: 不支持的特性
- at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112)
- at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:146)
- at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:208)
- 第五行能夠知道,外層事務開始執行,第六行可知外層事務提交。
- 總結可知:對於NESTED內層事務而言,內層事務獨立於外層事務,能夠獨立遞交或者回滾。
- 若是咱們在addUser方法內部拋出一個運行時異常,那麼會怎麼樣呢?
-
- public void addUser()throws Exception {
- this.bs.addBook();
- this.jdbcTemplate.execute(ADD_USER);
- throw new RuntimeException("throw runtime exception in outter transaction");
- }
-
- public void addBook() throws Exception{
- this.jdbcTemplate.execute(ADD_BOOK);
- }
- 執行junit後,控制檯輸入以下
-
- 一、[DEBUG,DataSourceTransactionManager,main] Creating new transaction with name [com.zx.spring.UserService.addUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
- 二、[DEBUG,DataSourceTransactionManager,main] Creating nested transaction with name [com.zx.spring.BookService.addBook]
- 三、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]
- 四、[DEBUG,DataSourceTransactionManager,main] Releasing transaction savepoint
- 五、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_user(id,name) values(1,'duck')]
- 六、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback
- 七、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1622a94]
- 第一行在addUser()方法執行後,事務管理器建立一個新的事務。
- 第二上和上面同樣,因爲addBook()方法是NETSTED的內嵌傳播機制,所以新建一個事務。
- 執行插入,釋放保存點。
- 執行插入t_user插入,可是此時拋出了一個運行時異常,外層事務回滾,那麼內層事務是否回滾呢?咱們看如下數據庫記錄。
- t_user表數據爲空,事務回滾了
t_book表數據也爲空,證實內層事務回滾了
- 由上述結果可知,若是對於一個內嵌事務來講,外層事務的回滾必將致使內層事務回滾。
- 五、咱們再將addBook()方法的事務傳播機制該爲REQUIRES_NEW,來看看會有什麼有趣的事情發生?
-
- <tx:advice id="txAdvice" transaction-manager="txManager">
- <tx:attributes>
- <tx:method name="addUser" propagation="REQUIRED" />
- <tx:method name="addBook" propagation="REQUIRES_NEW"/>
- </tx:attributes>
- </tx:advice>
-
- public void addUser()throws Exception {
- this.bs.addBook();
- this.jdbcTemplate.execute(ADD_USER);
- }
-
- public void addBook() throws Exception{
- this.jdbcTemplate.execute(ADD_BOOK);
-
- }
- 執行junit後,控制檯輸出以下
-
- 一、[DEBUG,DataSourceTransactionManager,main] Creating new transaction with name [com.zx.spring.UserService.addUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
- 二、[DEBUG,DataSourceTransactionManager,main] Suspending current transaction, creating new transaction with name [com.zx.spring.BookService.addBook]
- 三、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]
- 四、[DEBUG,DataSourceTransactionManager,main] Initiating transaction commit
- 五、[DEBUG,DataSourceTransactionManager,main] Committing JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@5b96c2]
- 由上可知,第一行執行addUser()方法建立一個事務,
- 第二行阻塞addUser()方法,並建立一個新的事務,執行插入t_book表,提交內層事務和外層事務。
- 或許有的讀者會問,在下面addUser()方法中因爲第一行和第二行是順序執行,所以不能說明說明問題,那麼咱們將addUser()方法中的一、2行代碼調換,在看效果:
-
- public void addUser()throws Exception {
- 一、 this.bs.addBook();
- 二、 this.jdbcTemplate.execute(ADD_USER);
- }
- 兌換後的代碼
-
- public void addUser()throws Exception {
- this.jdbcTemplate.execute(ADD_USER);
- this.bs.addBook();
- }
- 在來看控制檯輸出
-
- 一、[DEBUG,DataSourceTransactionManager,main] Creating new transaction with name [com.zx.spring.UserService.addUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
- 二、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_user(id,name) values(1,'duck')]
-
- 三、[DEBUG,DataSourceTransactionManager,main] Suspending current transaction, creating new transaction with name [com.zx.spring.BookService.addBook]
- 四、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]
- 五、[DEBUG,DataSourceTransactionManager,main] Initiating transaction commit
- 六、[DEBUG,DataSourceTransactionManager,main] Committing JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1b7ae22]
-
- 七、[DEBUG,DataSourceTransactionManager,main] Resuming suspended transaction after completion of inner transaction
- 八、[DEBUG,DataSourceTransactionManager,main] Initiating transaction commit
- 九、[DEBUG,DataSourceTransactionManager,main] Committing JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1fa681c]
- 由第1、二行可知道正在執行插入t_user表操做,而到第3行中咱們能夠知道,插入t_user表的事務被掛起,而且新建了一個事務來插入t_book表
- t_book表插入事務提交後,到第7行可知,前一個事務t_user插入操做被恢復,並提交前一個操做。
- 若是咱們在addBook()方法中拋出運行時異常,來看看會有什麼有趣的事情發生?
- addBook()方法代碼以下
-
- public void addBook() throws Exception{
- this.jdbcTemplate.execute(ADD_BOOK);
- throw new RuntimeException("throw runtime exception in outter transaction");
- }
- addUser()方法代碼以下
-
- public void addUser()throws Exception {
- this.jdbcTemplate.execute(ADD_USER);
- this.bs.addBook();
- }
- 執行junit後,控制檯輸出以下
-
- 一、[DEBUG,DataSourceTransactionManager,main] Creating new transaction with name [com.zx.spring.UserService.addUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
- 二、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_user(id,name) values(1,'duck')]
- 三、[DEBUG,DataSourceTransactionManager,main] Suspending current transaction, creating new transaction with name [com.zx.spring.BookService.addBook]
- 四、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]
- 五、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback
- 六、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@10d0b72]
- 七、[DEBUG,DataSourceTransactionManager,main] Releasing JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@10d0b72] after transaction
-
- 八、[DEBUG,DataSourceTransactionManager,main] Resuming suspended transaction after completion of inner transaction八、
- 九、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback
- 十、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@44b361]
- 十一、[DEBUG,DataSourceTransactionManager,main] Releasing JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@44b361] after transaction
- 第一行可知,執行addUser()方法時,從連接池獲取一個新連接,建立一個封裝事務,執行t_user表插入。
- 第三行可知,t_user插入事務被掛起,一直到第7行,插入t_book表事務被回滾
- 第8行可知,t_user事務恢復,可是此時該封裝事務被回滾。咱們再看數據庫.
- t_user表數據和t_book表數據均爲空
- 由此咱們能夠知道,對於REQUIRES_NEW事務傳播機制,若是被調用端拋出運行時異常,則被調用端事務回滾,那麼調用端的事務究竟是回滾仍是提交呢?
- 若是調用段代碼捕獲了被調用端拋出的運行時異常,那麼調用端事務提交,不回滾
- 咱們將addUser()調用端代碼該成以下(捕獲addBook()拋出的運行時異常)
-
- public void addUser()throws Exception {
- this.jdbcTemplate.execute(ADD_USER);
- try {
- this.bs.addBook();
- }catch(Exception e) {
- e.printStackTrace();
- }
- }
- 執行junit後,控制檯信息以下
-
- 一、[DEBUG,DataSourceTransactionManager,main] Creating new transaction with name [com.zx.spring.UserService.addUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
- 二、[DEBUG,DataSourceTransactionManager,main] Acquired Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1bcec19] for JDBC transaction
- 三、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_user(id,name) values(1,'duck')]
-
-
- 四、[DEBUG,DataSourceTransactionManager,main] Suspending current transaction, creating new transaction with name [com.zx.spring.BookService.addBook]
- 五、[DEBUG,DataSourceTransactionManager,main] Acquired Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@ba507b] for JDBC transaction
- 六、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]
- 七、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback
- 八、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@ba507b]
-
-
- 九、[DEBUG,DataSourceTransactionManager,main] Resuming suspended transaction after completion of inner transaction
- 十、java.lang.RuntimeException: throw runtime exception in outter transaction
- at com.zx.spring.BookService.addBook(BookService.java:11)
- 十一、[DEBUG,DataSourceTransactionManager,main] Initiating transaction commit
- 十二、[DEBUG,DataSourceTransactionManager,main] Committing JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1bcec19]
- 由上面的輸出能夠知道,1-3行從鏈接池獲取一個連接,開始執行插入事務
- 執行addBook()方法時,因其事務傳播屬性爲REQUIRES_NEW,則將上一個事務阻塞
- 第6-8行可知,addBook()方法拋出運行時異常,新事務被回滾
- 第9行恢復執行上一個插入t_user表事務,並捕獲到addBook()拋出的異常,自此addUser()方法未拋出任何運行時異常,提交事務。
- 若是調用端未捕獲被調用端拋出的運行時異常,那麼調用端事務回滾,不提交
- 咱們將addUser()方法調用端改爲以下(不捕獲addBook()拋出的運行時異常,直接拋出)
-
- public void addUser()throws Exception {
- this.jdbcTemplate.execute(ADD_USER);
- this.bs.addBook();
- }
- 執行junit後,控制檯輸出以下:
-
- 一、[DEBUG,DataSourceTransactionManager,main] Creating new transaction with name [com.zx.spring.UserService.addUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
- 二、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_user(id,name) values(1,'duck')]
- 三、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]
- 四、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback
- 五、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@63a721]
-
-
- 六、[DEBUG,DataSourceTransactionManager,main] Resuming suspended transaction after completion of inner transaction
- 七、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback
- 八、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@1706da8]
- 由上述第1-5行可知,插入t_user表事務被掛起,同時插入t_book事務被回滾,由於拋出了運行時異常。
- 6-8行插入t_user事務被回滾,由於addUser()方法的事務傳播界別爲REQUIRED,所以在拋出了運行時異常的狀況下,會回滾事務。
- 那麼,爲何會形成上面兩種大相徑庭的結果呢?由於addUser()方法被聲明爲REQUIRED傳播機制,只要它拋出運行時異常,均會回滾事務。
- 六、 咱們再將addBook()方法的事務傳播機制該爲SUPPORTS,來看看會有什麼有趣的事情發生?
- 將addBook()方法的事務機制改成SUPPORTS
-
- <tx:advice id="txAdvice" transaction-manager="txManager">
- <tx:attributes>
- <tx:method name="addUser" propagation="REQUIRED" />
- <tx:method name="addBook" propagation="SUPPORTS"/>
- </tx:attributes>
- </tx:advice>
- addUser()方法
-
- public void addUser()throws Exception {
- this.bs.addBook();
- this.jdbcTemplate.execute(ADD_USER);
- }
- addBook()方法
-
- public void addBook() throws Exception{
- this.jdbcTemplate.execute(ADD_BOOK);
- throw new RuntimeException("throw runtime exception in outter transaction");
- }
- 執行junit後,控制檯輸出以下
-
- 一、[DEBUG,DataSourceTransactionManager,main] Creating new transaction with name [com.zx.spring.UserService.addUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
- 二、[DEBUG,DataSourceTransactionManager,main] Participating in existing transaction
- 三、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]
- 四、[DEBUG,DataSourceTransactionManager,main] Participating transaction failed - marking existing transaction as rollback-only
- 五、[DEBUG,DataSourceTransactionManager,main] Setting JDBC transaction [com.mchange.v2.c3p0.impl.NewProxyConnection@19e09a4] rollback-only
- 六、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback
- 七、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@19e09a4]
- 由第二行可知,addBook()方法的加入到了當前addUser()方法的事務中,第4行可知,addBook()方法拋出運行時異常,此時addUser()方法的事務被標記爲rollback,整個事務都
- 將回滾。若是addUser()方法沒有任何事務,那麼addBook()方法也不會在事務環境中執行。無論是否拋出異常,sql都將執行。
- 若是addBook()方法拋出受檢查的異常,那麼此異常將忽略,整個addUser()方法的事務將提交。addBook()方法也不會嘗試去回滾事務
- 七、 咱們再將addBook()方法的事務傳播機制該爲NOT_SUPPORTED,會怎麼樣呢?
- 將addBook()方法的事務機制該爲NOT_SUPPORTED
-
- <tx:advice id="txAdvice" transaction-manager="txManager">
- <tx:attributes>
- <tx:method name="addUser" propagation="REQUIRED" />
- <tx:method name="addBook" propagation="NOT_SUPPORTED"/>
- </tx:attributes>
- </tx:advice>
- addBook()方法
-
- public void addBook() throws Exception{
- this.jdbcTemplate.execute(ADD_BOOK);
- throw new RuntimeException("throw runtime exception in outter transaction");
- }
- addUser()方法
-
- public void addUser()throws Exception {
- this.bs.addBook();
- this.jdbcTemplate.execute(ADD_USER);
- }
- 執行junit後,控制檯輸出以下
-
- 一、[DEBUG,DataSourceTransactionManager,main] Creating new transaction with name [com.zx.spring.UserService.addUser]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
- 二、[DEBUG,DataSourceTransactionManager,main] Suspending current transaction
- 三、[DEBUG,JdbcTemplate,main] Executing SQL statement [insert into t_book(id,name) values(1,'duck-j2ee')]
- 四、[DEBUG,DataSourceUtils,main] Fetching JDBC Connection from DataSource
- 五、[DEBUG,DataSourceTransactionManager,main] Should roll back transaction but cannot - no transaction available
-
-
- 六、[DEBUG,DataSourceTransactionManager,main] Resuming suspended transaction after completion of inner transaction
- 七、[DEBUG,DataSourceTransactionManager,main] Initiating transaction rollback
- 八、[DEBUG,DataSourceTransactionManager,main] Rolling back JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@137d4a4]
- 由上可知,第二行是將addUser()方法的事務掛起,開始執行addBook()代碼,
- 第5行可知,addBook()方法拋出運行時異常,須要回滾事務,可是又沒有事務來回滾,所以t_book表數據被插入
- 因爲addBook()拋出異常,在addUser()方法中未捕獲該異常,所以對addUser()方法的事務傳播機制REQUIRED來講,拋出了運行時異常,addUser()方法回滾
- 若是將addUser()方法該爲以下:
-
- public void addUser()throws Exception {
- try {
- this.bs.addBook();
- }catch(Exception e) {
- e.printStackTrace();
- }
- this.jdbcTemplate.execute(ADD_USER);
- }
- 那麼addUser()方法將會提交,addBook()方法將插入一條數據到t_book表中,
- 若是addBook()方法拋出了受檢查異常,addBook()方法將忽略此異常,不嘗試任何事務回滾,一樣即便在addUser()方法中不捕獲addBook()方法拋出的受檢查異常,addUser()方法也會提交事務,而忽略此異常。
今天對spring事務7中傳播機制之間的做用進行了一個回顧,這裏只涉及了jdbc事務管理器的特性,可能會有各類疏忽,但願各位讀者拍磚。spring