Spring 事务
Spring配置文件中,事务配置总是由三个组成部分,分别是DataSource、TransactionManager和 代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。
DataSource、TransactionManager两部分只是会根据数据访问方式有所变化

全注解
<?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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <context:component-scan base-package="com.smart" /> <tx:annotation-driven transaction-manager="transactionManager"/> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定义事务管理器(声明式的事务) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> </beans>
此时在Service上需加上@Transactional注解,如下:
package com.smart.service; @Transactional @Service("userDao") public class UserServiceImpl implements UserService { public List<User> listUsers() { ... } }
Spring声明式事务从复杂的事务处理中得到解脱。使得无需要去处理获得连接、关闭连接、事务提交和回滚等这些操作。也无需要在与事务相关的方法中处理大量的try…catch…finally代码。
在使用Spring声明式事务时,有一个非常重要的概念就是事务属性。事务属性通常由事务的传播行为,事务的隔离级别,事务的超时值和事务只读标志组成。在进行事务划分时,需要进行事务定义,也就是配置事务的属性。
Spring在TransactionDefinition接口中定义这些属性,以供PlatfromTransactionManager使用, PlatfromTransactionManager是spring事务管理的核心接口
public interface TransactionDefinition { int getPropagationBehavior(); int getIsolationLevel(); int getTimeout(); boolean isReadOnly(); }
getTimeout()方法,返回事务必须在多少秒内完成。
isReadOnly(),事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的。
getIsolationLevel()方法返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据。
在TransactionDefinition接口中定义了五个不同的事务隔离级别
ISOLATION_DEFAULT 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.另外四个与JDBC的隔离级别相对应
ISOLATION_READ_UNCOMMITTED 这是事务最低的隔离级别,充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
ISOLATION_READ_COMMITTED 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。
ISOLATION_REPEATABLE_READ 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读
getPropagationBehavior()返回事务的传播行为,由是否有一个活动的事务来决定一个事务调用。
在TransactionDefinition接口中定义了七个事务传播行为。
PROPAGATION_REQUIRED 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务
//事务属性 PROPAGATION_REQUIRED methodA{ …… methodB(); …… } //事务属性 PROPAGATION_REQUIRED methodB{ …… }
使用spring声明式事务,spring使用AOP来支持声明式事务,会根据事务属性,自动在方法调用之前决定是否开启一个事务,并在方法执行之后决定事务提交或回滚事务
调用MethodA时,环境中没有事务,所以开启一个新的事务. 当在MethodA中调用MethodB时,环境中已经有了一个事务,所以methodB就加入当前事务
PROPAGATION_SUPPORTS 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同。
/事务属性 PROPAGATION_REQUIRED methodA(){ methodB(); } //事务属性 PROPAGATION_SUPPORTS methodB(){ …… }
单纯的调用methodB时,methodB方法是非事务的执行的。 当调用methdA时,methodB则加入了methodA的事务中,事务地执行
PROPAGATION_MANDATORY 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
//事务属性 PROPAGATION_REQUIRED methodA(){ methodB(); } //事务属性 PROPAGATION_MANDATORY methodB(){ …… }
当单独调用methodB时,因为当前没有一个活动的事务,则会抛出异常 ,throw new IllegalTransactionStateException("Transactionpropagation 'mandatory' but no existing transaction found");
当调用methodA时,methodB则加入到methodA的事务中,事务地执行
PROPAGATION_REQUIRES_NEW 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。
//事务属性 PROPAGATION_REQUIRED methodA(){ doSomeThingA(); methodB(); doSomeThingB(); } //事务属性 PROPAGATION_REQUIRES_NEW methodB(){ …… }
当单独调用methodB时,相当于把methodB声明为REQUIRED。开启一个新的事务,事务地执行。
PROPAGATION_NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务。
//事务属性 PROPAGATION_REQUIRED methodA(){ doSomeThingA(); methodB(); doSomeThingB(); } //事务属性 PROPAGATION_NOT_SUPPORTED methodB(){ …… }
PROPAGATION_NEVER 总是非事务地执行,如果存在一个活动事务,则抛出异常
//事务属性 PROPAGATION_REQUIRED methodA(){ doSomeThingA(); methodB(); doSomeThingB(); } //事务属性 PROPAGATION_NEVER methodB(){ …… }
单独调用methodB,则非事务的执行。 调用methodA则会抛出异常 throw new IllegalTransactionStateException( "Transaction propagation 'never' butexisting transaction found");
PROPAGATION_NESTED如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
//事务属性 PROPAGATION_REQUIRED methodA(){ doSomeThingA(); methodB(); doSomeThingB(); } //事务属性 PROPAGATION_NESTED methodB(){ …… }
嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。
PROPAGATION_REQUIRED应该是首先的事务传播行为。它能够满足我们大多数的事务需求。
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = { AppBizExeA.class } , noRollbackFor = { AppBizExeB.class })
public void method1() throws Exception {
System.out.println("method1 start");
TPerson per = new TPerson();
per.setAge("24");
per.setId(123);
per.setName("xj");
personDao.add(per);
throw new NullPointerException();
}
虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。
当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性
@Transactional public class MyBatisServiceImpl implements MyBatisService { @Autowired private MyBatisDao dao; @Override public void insert(Test test) { dao.insert(test); //抛出unchecked异常,触发事物,回滚 throw new RuntimeException("test"); }
@Transactional只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.
Spring使用声明式事务处理,默认情况下,如果被注解的数据库操作方法中发生了unchecked异常,所有的数据库操作将rollback;如果发生的异常是checked异常,默认情况下数据库操作还是会提交的。
这种默认的行为是可以改变的。使用@Transactional注解的noRollbackFor和rollbackFor属性
如:@Transactional(rollbackFor=Exception.class)可以使checked异常发生时,数据库操作也rollback、@Transactional(noRollbackFor=RuntimeException.class)可以使unchecked异常发生时也提交数据库操作。也可以使用noRollbackForClassName、rollbackForClassName属性来指定一个异常类名的String数组来改变默认的行为。
读取数据库中的数据时是不需要事务管理的,这种情况下可以使用事务的传播行为来告诉Spring不需要开启事务,如:@Transactional(propagation =Propagation.NOT_SUPPORTED)。
浙公网安备 33010602011771号