Spring提供两种事务管理方式,分为编程式和声明式。
编程式 声明式
需要注意的是,使用的数据库需要支持事务,否则事务将不起作用。如MySql的MyIsam引擎就不支持事务。
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> 复制代码
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 如果使用基于注解的声明式事务,需要配置annotation-driven --> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/> 复制代码
显然,transactionManager的dataSource应该是代码中进行事务操作的dataSource,不然怎么管理呢。
在代码中开始、提交或回滚事务:
// 开始事务 TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); // 异常回滚,并不一定要在try...catch中 transactionManager.rollback(status); // 提交事务 transactionManager.commit(status); 复制代码
通常比较合适的使用方法:
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); try { // 进行需要处在同一事务中的数据库操作 ...... // 正常结束,提交事务 transactionManager.commit(status); } catch (SomeException) { // 错误处理 ...... // 回滚 transactionManager.rollback(status); } 复制代码
直接在类或在方法上添加 @Transaction
注解即可:
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackFor = Exception.class) public int save(Info info) throws Exception { 复制代码
@Transaction
注解的常用参数:
REQUIRED(默认值) SUPPORTS MANDATORY REQUIRES_NEW NOT_SUPPORTED NEVER NESTED
DEFAULT READ_UNCOMMITTED READ_COMMITTED REPEATABLE_READ SERIALIZABLE
表示该事务是否只读取数据但不更新数据,只有读取操作的事务设置为只读可以帮助数据库引擎优化事务。
避免对数据库长时间锁定,单位秒。
默认只对运行时异常进行回滚,参见下文。
Spring的声明式事务是通过AOP的方式动态代理的,因此会产生一个实现了事务代理类,区分于原始类。考虑一种情况:
此时,调用Service的方法B,使用的是代理类Service'的方法B,是实现了事务方法的,出现异常会回滚。
但如果调用的是方法A,在代理类的内部使用的是原始类Service的方法B,没有实现事务,会导致无法异常回滚。
解决方法:将B方法拆分到其他类中,保证调用的是代理类而非原始类的方法。
声明式事务包装的方法在方法抛出异常时会回滚,但是默认只在抛出 RuntimeException
时回滚。
如果异常被catch住吞掉了,或是抛出了非 RuntimeException
,那么默认是不回滚的。
解决方法:不要吞异常,遇到非 RuntimeException
,使用 @Transaction
注解的 rollbackFor
参数进行控制。
Java8中java.lang的部分类分层结构如下(完整结构请查看文章底部参考资料):
java.lang.Object ...... java.lang.Throwable (implements java.io.Serializable) java.lang.Error java.lang.AssertionError java.lang.LinkageError java.lang.BootstrapMethodError java.lang.ClassCircularityError java.lang.ClassFormatError java.lang.UnsupportedClassVersionError java.lang.ExceptionInInitializerError java.lang.IncompatibleClassChangeError java.lang.AbstractMethodError java.lang.IllegalAccessError java.lang.InstantiationError java.lang.NoSuchFieldError java.lang.NoSuchMethodError java.lang.NoClassDefFoundError java.lang.UnsatisfiedLinkError java.lang.VerifyError java.lang.ThreadDeath java.lang.VirtualMachineError java.lang.InternalError java.lang.OutOfMemoryError java.lang.StackOverflowError java.lang.UnknownError java.lang.Exception java.lang.CloneNotSupportedException java.lang.InterruptedException java.lang.ReflectiveOperationException java.lang.ClassNotFoundException java.lang.IllegalAccessException java.lang.InstantiationException java.lang.NoSuchFieldException java.lang.NoSuchMethodException // 这里是RuntimeException java.lang.RuntimeException java.lang.ArithmeticException java.lang.ArrayStoreException java.lang.ClassCastException java.lang.EnumConstantNotPresentException java.lang.IllegalArgumentException java.lang.IllegalThreadStateException java.lang.NumberFormatException java.lang.IllegalMonitorStateException java.lang.IllegalStateException java.lang.IndexOutOfBoundsException java.lang.ArrayIndexOutOfBoundsException java.lang.StringIndexOutOfBoundsException java.lang.NegativeArraySizeException java.lang.NullPointerException java.lang.SecurityException java.lang.TypeNotPresentException java.lang.UnsupportedOperationException ...... 复制代码