Spring 事务机制默认回滚 RuntimeException
及其异常子类,和 Error
等异常。
主要接口与配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 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 ; }
接口方法中的形参包括 TransactionDefinition
和 TransactionStatus
:
事务传播级别 TransactionDefinition
:定义事务属性,包括隔离级别和传播行为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public interface TransactionDefinition { int getPropagationBehavior () ; int getIsolationLevel () ; String getName () ; int getTimeout () ; boolean isReadOnly () ; }
事物的传播性一般在事务嵌套 时使用。 比如,有一个事务 A 里面调用了另一个使用事务的方法,那么这两个事务各自作为独立的事务 进行提交,或者将内层事务合并到外层事务中,再一并提交。
TransactionDefinition.PROPAGATION_REQUIRED
Spring 默认的事务传播机制
支持当前事务,如果其外层不存在事务,则创建一个新的事务
如外层存在事务,则加入该事务,一起提交一起回滚
TransactionDefinition.PROPAGATION_REQUIRES_NEW
每次创建一个新的事务
如果当前事务的外层存在另一个事务,则先将外层事务挂起,等到新事务执行完毕后再恢复外层事务执行
TransactionDefinition.PROPAGATION_SUPPORTS
支持当前事务
如果当前事务的外层存在另一个事务,则加入该事务;如不存在,则以非事务执行
TransactionDefinition.PROPAGATION_NOT_SUPPORTED
不支持当前事务,总是执行非事务性
如当前外层存在事务,则将当前事务挂起,执行当前逻辑,再恢复外层事务
TransactionDefinition.PROPAGATION_NEVER
TransactionDefinition.PROPAGATION_MANDATORY
支持当前事务,说明该传播性方法只能在已经存在事务 的方法中被调用
如不存在外层事务则抛出异常,如存在则加入该事务
TransactionDefinition.PROPAGATION_NESTED
可以保存事务至状态保存点中
如存在当前事务,则在一个嵌套的事务中执行,如没有事务则创建一个
当事务回滚时,会回滚到某一个保存点上,从而避免所有嵌套事务都回滚
TransactionDefinition.TIMEOUT_DEFAULT
使用默认的底层事务系统,如果不支持超时,则以非事务执行
事务状态 TransactionStatus
:负责事务开始到结束期间的状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public interface TransactionStatus extends SavepointManager { boolean isNewTransaction () ; boolean hasSavepoint () ; void setRollbackOnly () ; boolean isRollbackOnly () ; boolean isCompleted () ; }
Spring 事务管理 Spring 支持编程式 和声明式 两种事务管理方式。
编程式事务管理 通过编程实现的事务管理,胜在操作灵活,但是维护困难,而且属于侵入式编程,与 Spring Framework 耦合程度高。
举个例子,我们有下面一个 POJO 类:
1 2 3 4 5 6 7 8 9 10 11 public class StudentMarks { private String name; private Integer age; private Integer id; private Integer marks; private Integer year; private Integer sid; ... }
对应地,在 DAO 实现类中引入事务管理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 ... private JdbcTemplate jdbcTemplateObject; private PlatformTransactionManager transactionManager; ... @Override public void create (String name, Integer age, Integer marks, Integer year) throws Exception { TransactionDefinition def = new DefaultTransactionDefinition(); TransactionStatus status = transactionManager.getTransaction(def); try { String SQL1 = "insert into Student(name, age) values(?, ?)" ; jdbcTemplateObject.update(SQL1, name, age); String SQL2 = "select max(id) from Student" ; int sid = jdbcTemplateObject.queryForInt(SQL2); String SQL3 = "insert into Marks(sid, marks, year) values(?, ?, ?)" ; jdbcTemplateObject.update(SQL3, sid, marks, year); System.out.println("Created Name = " + name + ", Age = " + age); transactionManager.commit(status); } catch (DataAccessException e) { System.out.println("Error in creating record, rolling back." ); transactionManager.rollback(status); throw e; } } ...
配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <beans ... > <bean id ="dataSource" class ="org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name ="driverClassName" value ="com.mysql.jdbc.Driver" /> <property name ="url" value ="jdbc:mysql://localhost:3306/TEST" /> <property name ="username" value ="root" /> <property name ="password" value ="password" /> </bean > <bean id ="transactionManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name ="dataSource" ref ="dataSource" /> </bean > <bean id ="studentJDBCTemplate" class ="com.xxx.StudentJDBCTemplate" > <property name ="dataSource" ref ="dataSource" /> </bean > </beans >
除了 PlatformTransactionManager,还可以使用 TransactionTemplate 进行编程式事务管理。
虽然说编程式事务管理为侵入式编程,对代码维护会有所挑战,但是对于事务应用较少的业务代码,编程式事务处理不失为一个好选择。
声明式事务管理 声明式事务管理使用注释 或 XML 配置 管理事务,将事务管理的逻辑从业务代码中分离开来,不需要在 JdbcTemplate 等模版类中显式调用 transaction manager 来提交或回滚事务。
声明式事务管理无侵入,业务影响小,是最好的选择。
基于注释 @Transactional
通过 @Transactional 注解开启一个事务。
实现机制:基于 AOP
代码运行时先生成一个代理对象
代理对象根据 @Transactional 配置的属性,决定声明了 @Transactional 的目标方法是否由拦截器 TransactionInterceptor 来拦截
在 TransactionInterceptor 拦截时,会在目标方法开始执行之前创建并加入事务
执行目标方法的逻辑
根据执行情况作后续处理:
执行情况无异常:AbstractPlatformTransactionManager 操作数据源提交事务
执行情况异常:AbstractPlatformTransactionManager 操作数据源回滚事务
程序执行时序图如下:
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Transactional public class TestServiceBean implements TestService { private TestDao dao; public void setDao (TestDao dao) { this .dao = dao; } @Transactional(propagation=Propagation.NOT_SUPPORTED) public List getAll () { return null ; } }
@Transactional 可配置的属性 信息与接口 TransactionDefinition 的方法一一对应:
属性名
描述
name
指定所使用的事务管理器,适用于配置文件中存在多个 TransactionManager 的情况
propagation
设置事务的传播行为,取值同接口 TransactionDefinition 的枚举值
isolation
设置底层数据库的事务隔离级别 通常使用数据库默认的隔离级别即可
timeout
如超过该时间但事务尚未完成:自动回滚事务 默认值为 -1:表示永不超时
readOnly
设置当前事务是否为只读事务,默认为 false
<br/>@Transactional(readOnly="true")
rollbackFor
指定能够触发事务回滚的异常类型,值为 Class 对象数组 如指定多个异常类型:各类型之间通过逗号分隔
rollbackForClassName
指定能够触发事务回滚的异常类名数组
noRollbackFor
抛出其指定的异常类型,不回滚事务
noRollbackForClassName
抛出指定的类名数组中的某一个,不回滚事务
配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <beans ... xmlns:tx ="http://www.springframework.org/schema/tx" > <tx:annotation-driven /> <bean id ="dataSource" class ="org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name ="driverClassName" value ="com.mysql.jdbc.Driver" /> <property name ="url" value ="jdbc:mysql://localhost:3306/TEST" /> <property name ="username" value ="root" /> <property name ="password" value ="password" /> </bean > <bean id ="transactionManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name ="dataSource" ref ="dataSource" /> </bean > </beans >
配置指定回滚的异常:
1 2 @Transactional(rollbackFor = JavastackException.class) @Transactional(rollbackFor = [aException.class, bException.class])
配置指定不回滚的异常:
1 2 @Transactional(noRollbackFor = JavastackException.class) @Transactional(noRollbackFor = [aException.class, bException.class])
需要注意的地方:
1 . @Transactional 只能应用到 public
方法 才有效。
因为 TransactionInterceptor 在进行拦截之前会调用 AbstractFallbackTransactionAttributeSource#computeTransactionAttribute()
:
1 2 3 4 5 6 protected TransactionAttribute computeTransactionAttribute (Method method, Class<?> targetClass) { if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) { return null ; } }
另外,CglibAopProxy 内部的 DynamicAdvisedInterceptor#intercept()
或 JdkDynamicAopProxy#invoke()
也会间接调用以上方法。
所以编程的时候,默认权限或私有代码就不要标记 @Transactional 了。
2 . 避免 AOP 自调用问题
如果一个类中的一个或多个非事务方法中调用自身类的事务方法 ,那么在调用非事务方法时,事务方法的事务会被忽略 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Service public class OrderService { private void insert () { insertOrder(); } @Transactional public void insertOrder () { } }
如上,在执行 insert() 时,insertOrder() 的事务不会被执行;如果抛出了异常,事务也不会被回滚。
因为:Spring 扫描到所有添加了 @Transactional 注解的类之后将它们初始化,对应地会各自生成一个代理对象 ,Spring 事务再去判断代理对象的执行方法是否添加了 @Transactional 注解。
而对象中的非事务方法去调用事务方法属于自调用 ,自调用方法并不是由代理通过反射执行的,因此被调用的事务方法,其注解无效。
@Transactional 源码简析 上面一小节的时序图已经说明了大概情况。
Spring Framework 在为某一操作建立事务之前,先寻找增强器 ,判断这些增强器是否与方法或类相匹配,以完成对应类或方法的事务属性解析。
步骤如下:
首先:初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package org.springframework.transaction.config;public class TxNamespaceHandler extends NamespaceHandlerSupport { ... @Override public void init () { registerBeanDefinitionParser("advice" , new TxAdviceBeanDefinitionParser()); registerBeanDefinitionParser("annotation-driven" , new AnnotationDrivenBeanDefinitionParser()); registerBeanDefinitionParser("jta-transaction-manager" , new JtaTransactionManagerBeanDefinitionParser()); ... } ... }
读到配置中的注解之后,调用 AnnotationDrivenBeanDefinitionParser
解析器的 parse() 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public BeanDefinition parse (Element element, ParserContext parserContext) { registerTransactionalEventListenerFactory(parserContext); String mode = element.getAttribute("mode" ); if ("aspectj" .equals(mode)) { registerTransactionAspect(element, parserContext); ... if (ClassUtils.isPresent("javax.transaction.Transactional" , getClass().getClassLoader())) { registerJtaTransactionAspect(element, parserContext); } } else { AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext); } return null ; }
于是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public static void configureAutoProxyCreator (Element element, ParserContext parserContext) { AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element); String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME; if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) { Object eleSource = parserContext.extractSource(element); RootBeanDefinition sourceDef = new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource" ); String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef); RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class); interceptorDef.getPropertyValues().add("transactionAttributeSource" , new RuntimeBeanReference(sourceName)); String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef); RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class); advisorDef.getPropertyValues().add("transactionAttributeSource" , new RuntimeBeanReference(sourceName)); advisorDef.getPropertyValues().add("adviceBeanName" , interceptorName); if (element.hasAttribute("order" )) { advisorDef.getPropertyValues().add("order" , element.getAttribute("order" )); } parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef); CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource); compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName)); compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName)); compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, txAdvisorBeanName)); parserContext.registerComponent(compositeDef); } }
根据上述源码可知,在创建增强器的时候,注册了三个类:
AnnotationTransactionAttributeSource
:封装了目标方法是否被拦截的逻辑
没有实现 Pointcut 接口
在后面目标方法判断时,委托 AnnotationTransactionAttributeSource.getTransactionAttributeSource(),通过适配器模式返回 Pointcut 切点信息
TransactionInterceptor
:实现 Advice 接口,定义了拦截后执行的增强
TransactionAttributeSourceAdvisor
:实现 Advisor 接口,包装上面两个信息(增强+切入点),即形成切面
切面(增强器)属于 BeanFactoryTransactionAttributeSourceAdvisor
组成环绕型(@Around
)的增强
实例化 bean 时,调用代理器父类 AbstractAutoProxyCreator#postProcessAfterInitialization()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public Object postProcessAfterInitialization (@Nullable Object bean, String beanName) { if (bean != null ) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this .earlyProxyReferences.remove(cacheKey) != bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
创建代理的时候,还需:
1 . 判断目标方法是否适合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static boolean canApply (Advisor advisor, Class<?> targetClass, boolean hasIntroductions) { if (advisor instanceof IntroductionAdvisor) { return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass); } else if (advisor instanceof PointcutAdvisor) { PointcutAdvisor pca = (PointcutAdvisor) advisor; return canApply(pca.getPointcut(), targetClass, hasIntroductions); } else { return true ; } }
根据之前注入的切面类型 AnnotationTransactionAttributeSource,再通过以下方法包装:
1 2 3 4 5 6 7 8 9 private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() { @Override @Nullable protected TransactionAttributeSource getTransactionAttributeSource () { return transactionAttributeSource; } };
2 . 匹配标签:识别 @Transactional 标签
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 @Override public boolean matches (Method method, Class<?> targetClass) { TransactionAttributeSource tas = getTransactionAttributeSource(); return (tas == null || tas.getTransactionAttribute(method, targetClass) != null ); } protected TransactionAttribute determineTransactionAttribute (AnnotatedElement element) { for (TransactionAnnotationParser parser : this .annotationParsers) { TransactionAttribute attr = parser.parseTransactionAnnotation(element); if (attr != null ) { return attr; } } return null ; } public TransactionAttribute parseTransactionAnnotation (AnnotatedElement element) { AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes( element, Transactional.class, false , false ); if (attributes != null ) { return parseTransactionAnnotation(attributes); } else { return null ; } } protected TransactionAttribute parseTransactionAnnotation (AnnotationAttributes attributes) { RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute(); Propagation propagation = attributes.getEnum("propagation" ); rbta.setPropagationBehavior(propagation.value()); Isolation isolation = attributes.getEnum("isolation" ); rbta.setIsolationLevel(isolation.value()); rbta.setTimeout(attributes.getNumber("timeout" ).intValue()); rbta.setReadOnly(attributes.getBoolean("readOnly" )); rbta.setQualifier(attributes.getString("value" )); List<RollbackRuleAttribute> rollbackRules = new ArrayList<>(); for (Class<?> rbRule : attributes.getClassArray("rollbackFor" )) { rollbackRules.add(new RollbackRuleAttribute(rbRule)); } for (String rbRule : attributes.getStringArray("rollbackForClassName" )) { rollbackRules.add(new RollbackRuleAttribute(rbRule)); } for (Class<?> rbRule : attributes.getClassArray("noRollbackFor" )) { rollbackRules.add(new NoRollbackRuleAttribute(rbRule)); } for (String rbRule : attributes.getStringArray("noRollbackForClassName" )) { rollbackRules.add(new NoRollbackRuleAttribute(rbRule)); } rbta.setRollbackRules(rollbackRules); return rbta; }
之后就在运行时执行事务,主要有几个处理步骤:
获取事务属性
加载配置中的 transactionManager
不同事物处理方式使用不同逻辑
在目标方法执行前获取事务并收集事务信息
执行目标方法
出现异常,尝试异常处理
提交事务前的事务信息消除
提交事务
1 . 执行注册到 BeanFactoryTransactionAttributeSourceAdvisor 中的 TransactionInterceptor 进行增强
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 public Object invoke (MethodInvocation invocation) throws Throwable { Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null ); return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed); } protected Object invokeWithinTransaction (Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation) throws Throwable { TransactionAttributeSource tas = getTransactionAttributeSource(); final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null ); final PlatformTransactionManager tm = determineTransactionManager(txAttr); final String joinpointIdentification = methodIdentification(method, targetClass, txAttr); if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) { TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); Object retVal; try { retVal = invocation.proceedWithInvocation(); } catch (Throwable ex) { completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { cleanupTransactionInfo(txInfo); } commitTransactionAfterReturning(txInfo); return retVal; } else { final ThrowableHolder throwableHolder = new ThrowableHolder(); try { Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> { TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); ... return result; } } } protected PlatformTransactionManager determineTransactionManager (@Nullable TransactionAttribute txAttr) { if (txAttr == null || this .beanFactory == null ) { return asPlatformTransactionManager(getTransactionManager()); } String qualifier = txAttr.getQualifier(); if (StringUtils.hasText(qualifier)) { return determineQualifiedTransactionManager(this .beanFactory, qualifier); } else if (StringUtils.hasText(this .transactionManagerBeanName)) { return determineQualifiedTransactionManager(this .beanFactory, this .transactionManagerBeanName); } else { PlatformTransactionManager defaultTransactionManager = asPlatformTransactionManager(getTransactionManager()); ... return defaultTransactionManager; } } protected TransactionInfo createTransactionIfNecessary (PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) { if (txAttr != null && txAttr.getName() == null ) { txAttr = new DelegatingTransactionAttribute(txAttr) { @Override public String getName () { return joinpointIdentification; } }; } TransactionStatus status = null ; if (txAttr != null ) { if (tm != null ) { status = tm.getTransaction(txAttr); } } return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); }
2 . 获取事务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager , Serializable { @Override public final TransactionStatus getTransaction (@Nullable TransactionDefinition definition) throws TransactionException { Object transaction = doGetTransaction(); doBegin(transaction, definition); prepareSynchronization(status, definition); return status; } } protected Object doGetTransaction () { DataSourceTransactionObject txObject = new DataSourceTransactionObject(); txObject.setSavepointAllowed(isNestedTransactionAllowed()); ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource()); txObject.setConnectionHolder(conHolder, false ); return txObject; } private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources" );private static Object doGetResource (Object actualKey) { Map<Object, Object> map = resources.get(); if (map == null ) { return null ; } Object value = map.get(actualKey); if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) { map.remove(actualKey); if (map.isEmpty()) { resources.remove(); } value = null ; } return value; }
判断当前线程是否存在事务:
当前线程记录的连接(ConnectionHolder)不为空
连接中的属性 transactionActive 不为空
3 . 事务创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 protected void doBegin (Object transaction, TransactionDefinition definition) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction; Connection con = null ; if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) { Connection newCon = obtainDataSource().getConnection(); txObject.setConnectionHolder(new ConnectionHolder(newCon), true ); } txObject.getConnectionHolder().setSynchronizedWithTransaction(true ); con = txObject.getConnectionHolder().getConnection(); Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition); txObject.setPreviousIsolationLevel(previousIsolationLevel); if (con.getAutoCommit()) { txObject.setMustRestoreAutoCommit(true ); con.setAutoCommit(false ); } prepareTransactionalConnection(con, definition); txObject.getConnectionHolder().setTransactionActive(true ); int timeout = determineTimeout(definition); if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) { txObject.getConnectionHolder().setTimeoutInSeconds(timeout); } if (txObject.isNewConnectionHolder()) { TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder()); } }
基于 XML 配置文件:添加 transaction 配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 <beans ... xmlns:tx ="http://www.springframework.org/schema/tx" > <bean id ="dataSource" class ="org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name ="driverClassName" value ="com.mysql.jdbc.Driver" /> <property name ="url" value ="jdbc:mysql://localhost:3306/TEST" /> <property name ="username" value ="root" /> <property name ="password" value ="password" /> </bean > <tx:advice id ="txAdvice" transaction-manager ="transactionManager" > <tx:attributes > <tx:method name ="create" /> </tx:attributes > </tx:advice > <aop:config > <aop:pointcut id ="createOperation" expression ="execution(* com.xxx.StudentJDBCTemplate.create(..))" /> <aop:advisor advice-ref ="txAdvice" pointcut-ref ="createOperation" /> </aop:config > <bean id ="transactionManager" class ="org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name ="dataSource" ref ="dataSource" /> </bean > <bean id ="studentJDBCTemplate" class ="com.xxx.StudentJDBCTemplate" > <property name ="dataSource" ref ="dataSource" /> </bean > </beans >