spring事务传播之NESTED

来源:互联网 发布:晨曦计价软件官网 编辑:程序博客网 时间:2024/05/21 10:09

下面是官方的解释

/** * Execute within a nested transaction if a current transaction exists, * behave like PROPAGATION_REQUIRED else. There is no analogous feature in EJB. * <p>Note: Actual creation of a nested transaction will only work on specific * transaction managers. Out of the box, this only applies to the JDBC * DataSourceTransactionManager when working on a JDBC 3.0 driver. * Some JTA providers might support nested transactions as well. * @see org.springframework.jdbc.datasource.DataSourceTransactionManager */NESTED(TransactionDefinition.PROPAGATION_NESTED);

如果事务存在则在一个嵌套的事务中执行,如果没有则像PROPAGATION_REQUIRED表现形式一样,支持嵌套事务的前提条件
*JDBC3+
* 使用DataSourceTransactionManager 事务管理器

实例

spring配置文件

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"    xmlns:aop="http://www.springframework.org/schema/aop"    xsi:schemaLocation="        http://www.springframework.org/schema/beans         http://www.springframework.org/schema/beans/spring-beans-4.2.xsd         http://www.springframework.org/schema/tx         http://www.springframework.org/schema/tx/spring-tx-4.2.xsd        http://www.springframework.org/schema/aop         http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">    <!-- 配置数据源 c3p0 -->    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"        destroy-method="close">        <property name="driverClass" value="${jdbc.driver}" />        <property name="jdbcUrl" value="${jdbc.url}" />        <property name="user" value="${jdbc.username}" />        <property name="password" value="${jdbc.password}" />        <!-- 请求超时时间 -->        <property name="checkoutTimeout" value="30000" />        <!-- 每60秒检查所有连接池中的空闲连接。默认值: 0,不检查 -->        <property name="idleConnectionTestPeriod" value="30" />        <!-- 连接数据库连接池最大空闲时间 -->        <property name="maxIdleTime" value="30" />        <!-- 连接池初始化连接数 -->        <property name="initialPoolSize" value="10" />        <property name="minPoolSize" value="10" />        <property name="maxPoolSize" value="20" />        <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。默认值: 3 -->        <property name="acquireIncrement" value="5" />    </bean>    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">        <property name="dataSource" ref="dataSource" />    </bean>    <!-- 配置事务管理器 -->    <bean id="transactionManager"        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <property name="dataSource" ref="dataSource" />    </bean>    <!-- 开始注解方式事务 -->    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/></beans>

测试代码

package org.bear.bookstore.test.propagation.jdbc;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.stereotype.Repository;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;@Repositorypublic class Nested {    @Autowired JdbcTemplate jdbcTemplate;    @Transactional(            propagation=Propagation.REQUIRED,            rollbackFor={Exception.class}            )    public void a(){        jdbcTemplate.execute("insert into custom(address,cusname,email,phone,sex) values('北京市,朝阳区,十里河村','xxx','xx@qq.com','15555555555','1')");        int x = 1/0;        System.out.println(x);    }    @Transactional(            propagation=Propagation.NESTED,            rollbackFor={Exception.class}            )    public void b(){        jdbcTemplate.execute("insert into custom(address,cusname,email,phone,sex) values('北京市,朝阳区,十里河村','xxx','xxx@qq.com','15555555555','0')");        int x = 1/0;        System.out.println(x);    }}
package org.bear.bookstore.test.propagation.jdbc;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.stereotype.Repository;import org.springframework.transaction.annotation.Propagation;import org.springframework.transaction.annotation.Transactional;@Repositorypublic class Nested2 {    @Autowired Nested nested;    @Autowired JdbcTemplate jdbcTemplate;    @Transactional(            propagation=Propagation.REQUIRED,            rollbackFor={Exception.class}            )    public void a(){        /**         * 如果aa()抛出异常,b未出现异常,则a会回滚b的结果         */        aa();        try {            /**             * 调用b,捕获异常,异常不会冒泡,aa()执行成功             * 也就是nested的用法,在一个类中这么调用没有效果,如调用aaa()             */            nested.b();            /**             * 如果调用a则会出现如下异常,             * org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only                    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:724)                    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:504)                    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292)                    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)                    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)                    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)                    at org.bear.bookstore.test.propagation.jdbc.Nested2$$EnhancerBySpringCGLIB$$567ca65b.a(<generated>)                    at org.bear.bookstore.test.propagation.JdbcTransactionPropagationTest.NestedTest(JdbcTransactionPropagationTest.java:25)                    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)                    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)                    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)                    at java.lang.reflect.Method.invoke(Unknown Source)                    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)                    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)                    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)                    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)                    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)                    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)                    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)                    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)                    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)                    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)                    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)                    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)                    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)                    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)                    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)                    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)                    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)                    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)                    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)                    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)                    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)                    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)                    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)                    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)                    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)             */            //nested.a();        } catch (Exception e) {            e.printStackTrace();        }    }    @Transactional(            propagation=Propagation.REQUIRED,            rollbackFor={Exception.class}            )    public void aa(){        jdbcTemplate.execute("insert into custom(address,cusname,email,phone,sex) values('北京市,朝阳区,十里河村','xxx','xxx@qq.com','15555555555','1')");        /*int x = 1/0;        System.out.println(x);*/    }    /**     * 这样的结果是     *  如果bbb是propagation=Propagation.NESTED,则两个custom都保存成功     *  如果bbb是propagation=Propagation.REQUIRED,则两个custom都保存成功     */    @Transactional(            propagation=Propagation.REQUIRED,            rollbackFor={Exception.class}            )    public void aaa(){        jdbcTemplate.execute("insert into custom(address,cusname,email,phone,sex) values('北京市,朝阳区,十里河村','xxx','xxx@qq.com','15555555555','1')");        try {            bbb();        } catch (Exception e) {            e.printStackTrace();        }    }    @Transactional(            //propagation=Propagation.NESTED,            propagation=Propagation.REQUIRED,            rollbackFor={Exception.class}            )    public void bbb(){        jdbcTemplate.execute("insert into custom(address,cusname,email,phone,sex) values('北京市,朝阳区,十里河村','xxx','xxx@qq.com','15555555555','1')");        int x = 1/0;        System.out.println(x);    }}
package org.bear.bookstore.test.propagation;import org.bear.bookstore.test.propagation.jdbc.Nested2;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(        locations={"file:src/test/resources/spring-app-jdbc.xml","file:src/test/resources/spring-jdbc.xml"}        )public class JdbcTransactionPropagationTest {    /**     * 运行在datasourcemanager中     */    @Autowired Nested2 Nested;    /**     * PROPAGATION_NESTED      *       */    @Test    public void NestedTest(){        Nested.a();        //Nested.aaa();    }}

实验分析

咱们看堆栈信息

Thread [main] (Suspended (breakpoint at line 186 in DataSourceTransactionManager))      DataSourceTransactionManager.doGetTransaction() line: 186       DataSourceTransactionManager(AbstractPlatformTransactionManager).getTransaction(TransactionDefinition) line: 337        TransactionInterceptor(TransactionAspectSupport).createTransactionIfNecessary(PlatformTransactionManager, TransactionAttribute, String) line: 447       TransactionInterceptor(TransactionAspectSupport).invokeWithinTransaction(Method, Class<?>, InvocationCallback) line: 277        TransactionInterceptor.invoke(MethodInvocation) line: 96        CglibAopProxy$CglibMethodInvocation(ReflectiveMethodInvocation).proceed() line: 179        CglibAopProxy$DynamicAdvisedInterceptor.intercept(Object, Method, Object[], MethodProxy) line: 655     Nested$$EnhancerBySpringCGLIB$$cb68a86.b() line: not available      Nested2.a() line: 28        Nested2$$FastClassBySpringCGLIB$$7a769380.invoke(int, Object, Object[]) line: not available      MethodProxy.invoke(Object, Object[]) line: 204      CglibAopProxy$CglibMethodInvocation.invokeJoinpoint() line: 720        CglibAopProxy$CglibMethodInvocation(ReflectiveMethodInvocation).proceed() line: 157        TransactionInterceptor$1.proceedWithInvocation() line: 99      TransactionInterceptor(TransactionAspectSupport).invokeWithinTransaction(Method, Class<?>, InvocationCallback) line: 282        TransactionInterceptor.invoke(MethodInvocation) line: 96        CglibAopProxy$CglibMethodInvocation(ReflectiveMethodInvocation).proceed() line: 179        CglibAopProxy$DynamicAdvisedInterceptor.intercept(Object, Method, Object[], MethodProxy) line: 655     Nested2$$EnhancerBySpringCGLIB$$d54acf0.a() line: not available     JdbcTransactionPropagationTest.NestedTest() line: 25        NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]      NativeMethodAccessorImpl.invoke(Object, Object[]) line: not available       DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: not available       Method.invoke(Object, Object...) line: not available        FrameworkMethod$1.runReflectiveCall() line: 50     FrameworkMethod$1(ReflectiveCallable).run() line: 12       FrameworkMethod.invokeExplosively(Object, Object...) line: 47       InvokeMethod.evaluate() line: 17        RunBeforeTestMethodCallbacks.evaluate() line: 75        RunAfterTestMethodCallbacks.evaluate() line: 86     SpringRepeat.evaluate() line: 84        SpringJUnit4ClassRunner(ParentRunner<T>).runLeaf(Statement, Description, RunNotifier) line: 325     SpringJUnit4ClassRunner.runChild(FrameworkMethod, RunNotifier) line: 252        SpringJUnit4ClassRunner.runChild(Object, RunNotifier) line: 94      ParentRunner$3.run() line: 290     ParentRunner$1.schedule(Runnable) line: 71     SpringJUnit4ClassRunner(ParentRunner<T>).runChildren(RunNotifier) line: 288     ParentRunner<T>.access$000(ParentRunner, RunNotifier) line: 58     ParentRunner$2.evaluate() line: 268        RunBeforeTestClassCallbacks.evaluate() line: 61     RunAfterTestClassCallbacks.evaluate() line: 70      SpringJUnit4ClassRunner(ParentRunner<T>).run(RunNotifier) line: 363     SpringJUnit4ClassRunner.run(RunNotifier) line: 191      JUnit4TestReference.run(TestExecution) line: 86     TestExecution.run(ITestReference[]) line: 38        RemoteTestRunner.runTests(String[], String, TestExecution) line: 459        RemoteTestRunner.runTests(TestExecution) line: 678      RemoteTestRunner.run() line: 382        RemoteTestRunner.main(String[]) line: 192   

具体分析流程

重点日志打印信息2016-11-23 11:03:26.652 [main] DEBUG org.springframework.jdbc.core.JdbcTemplate at 430 - Executing SQL statement [insert into custom(address,cusname,email,phone,sex) values('北京市,朝阳区,十里河村','xxx','xxx@qq.com','15555555555','1')]2016-11-23 11:04:09.012 [main] DEBUG o.s.jdbc.datasource.DataSourceTransactionManager at 450 - Creating nested transaction with name [org.bear.bookstore.test.propagation.jdbc.Nested.b]2016-11-23 11:04:09.102 [main] DEBUG org.springframework.jdbc.core.JdbcTemplate at 430 - Executing SQL statement [insert into custom(address,cusname,email,phone,sex) values('北京市,朝阳区,十里河村','xxx','xxx@qq.com','15555555555','0')]2016-11-23 11:04:09.104 [main] DEBUG o.s.jdbc.datasource.DataSourceTransactionManager at 845 - Rolling back transaction to savepointjava.lang.ArithmeticException: / by zero    at org.bear.bookstore.test.propagation.jdbc.Nested.b(Nested.java:30)    at org.bear.bookstore.test.propagation.jdbc.Nested$$FastClassBySpringCGLIB$$c1e2caf2.invoke(<generated>)    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720)    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)    at org.bear.bookstore.test.propagation.jdbc.Nested$$EnhancerBySpringCGLIB$$cb68a86.b(<generated>)    at org.bear.bookstore.test.propagation.jdbc.Nested2.a(Nested2.java:28)    at org.bear.bookstore.test.propagation.jdbc.Nested2$$FastClassBySpringCGLIB$$7a769380.invoke(<generated>)    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720)    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)    at org.bear.bookstore.test.propagation.jdbc.Nested2$$EnhancerBySpringCGLIB$$d54acf0.a(<generated>)    at org.bear.bookstore.test.propagation.JdbcTransactionPropagationTest.NestedTest(JdbcTransactionPropagationTest.java:25)    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)    at java.lang.reflect.Method.invoke(Unknown Source)    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)2016-11-23 11:04:09.102 [main] DEBUG org.springframework.jdbc.core.JdbcTemplate at 430 - Executing SQL statement [insert into custom(address,cusname,email,phone,sex) values('北京市,朝阳区,十里河村','xxx','xxx@qq.com','15555555555','0')]2016-11-23 11:04:09.111 [main] DEBUG o.s.jdbc.datasource.DataSourceTransactionManager at 759 - Initiating transaction commit    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)2016-11-23 11:04:09.104 [main] DEBUG o.s.jdbc.datasource.DataSourceTransactionManager at 845 - Rolling back transaction to savepoint从堆栈信息和打印信息我们可以清楚的得到以下信息:测试类方法调用JdbcTransactionPropagationTest.NestedTest() line: 25CBLIB字节码增强我的service类Nested2$$EnhancerBySpringCGLIB$$d54acf0.a() line: not available事务拦截器拦截注解了事务的方法Nested2.a() line: 28    Nested2$$FastClassBySpringCGLIB$$7a769380.invoke(int, Object, Object[]) line: not available  MethodProxy.invoke(Object, Object[]) line: 204  CglibAopProxy$CglibMethodInvocation.invokeJoinpoint() line: 720    CglibAopProxy$CglibMethodInvocation(ReflectiveMethodInvocation).proceed() line: 157    TransactionInterceptor$1.proceedWithInvocation() line: 99  TransactionInterceptor(TransactionAspectSupport).invokeWithinTransaction(Method, Class<?>, InvocationCallback) line: 282    TransactionInterceptor.invoke(MethodInvocation) line: 96    CglibAopProxy$CglibMethodInvocation(ReflectiveMethodInvocation).proceed() line: 179    CglibAopProxy$DynamicAdvisedInterceptor.intercept(Object, Method, Object[], MethodProxy) line: 655 发现调用了另一个service的声明了NESTED的方法,创建savepoint,走新的代理拦截流程Nested$$EnhancerBySpringCGLIB$$cb68a86.b() line: not available现在我们解释一些问题了为什么b的执行失败也不会影响aa方法的插入操作,因为b失败后事务,会回滚到我们保存的savepoint,(当然咱们必须捕获异常进行处理)然后继续往下执行,也就是执行a方法的其它流程为什么aa的执行失败会影响b,理由同上

总结

不闻不若闻之,闻之不若见之,见之不若知之,知之不若行之。
不断的学习,不断的交流,不断的实践

0 0
原创粉丝点击