天温习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