spring事务使用
1 spring事务处理
spring事务配置有多种方式,这里以全注解方式进行介绍。
1.1 前提
spring项目已正常跑通;maven项目;
1.2 spring配置文件修改
增加事务管理器:
<!--TransactionManager定义 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource1" />
</bean>
<bean id="transactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager" />
</bean>
<!-- enables scanning for @Transactionalannotations -->
<tx:annotation-driven transaction-manager="transactionManager" />
<tx:annotation-driven>一共有四个属性如下,
Ø mode:指定Spring事务管理框架创建通知bean的方式。可用的值有proxy和aspectj。前者是默认值,表示通知对象是个JDK代理;后者表示Spring AOP会使用AspectJ创建代理
Ø proxy-target-class:如果为true,Spring将创建子类来代理业务类;如果为false,则使用基于接口的代理。(如果使用子类代理,需要在类路径中添加CGLib.jar类库)
Ø order:如果业务类除事务切面外,还需要织入其他的切面,通过该属性可以控制事务切面在目标连接点的织入顺序。
Ø transaction-manager:指定到现有的PlatformTransaction Manager bean的引用,通知会使用该引用
1.3 增加@Transactional注解
在需要的函数上增加:
1. @Transactional
2. public void test() throws Exception {
3. doDbStuff1();
4. doDbStuff2();//假如这个操作数据库的方法会抛出runtimeexception,现在方法doDbStuff1()对数据库的操作会回滚。
5. }
1.4 注意事项
1. 在需要事务管理的地方加@Transactional注解。@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。
2. @Transactional注解只能应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。
3. 注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅是一种元数据。必须在配置文件中使用配置元素,才真正开启了事务行为。
4. Spring团队建议在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。在接口上使用 @Transactional 注解,只能当你设置了基于接口的代理时它才生效。因为注解是 不能继承 的,这就意味着如果正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装。
5. @Transactional 的事务开启,是基于接口的或者是基于类的代理被创建。所以在同一个类中一个方法调用另一个方法是有事务的方法,事务是不会起作用的。
6. Spring使用声明式事务处理,默认情况下,如果被注解的数据库操作方法中发生了unchecked异常,所有的数据库操作将rollback;如果发生的异常是checked异常,默认情况下数据库操作还是会提交的。
1.5 常用参数说明
参 数 名 称 |
功 能 描 述 |
readOnly |
该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true) |
rollbackFor |
该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如: 指定单一异常类:@Transactional(rollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class}) |
参 数 名 称 |
功 能 描 述 |
rollbackForClassName |
该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如: 指定单一异常类名称:@Transactional(rollbackForClassName="RuntimeException") 指定多个异常类名称:@Transactional(rollbackForClassName={"RuntimeException","Exception"}) |
noRollbackFor |
该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如: 指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class) 指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class}) |
noRollbackForClassName |
该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如: 指定单一异常类名称:@Transactional(noRollbackForClassName="RuntimeException") 指定多个异常类名称: @Transactional(noRollbackForClassName={"RuntimeException","Exception"}) |
propagation |
该属性用于设置事务的传播行为,具体取值可参考表6-7。 例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true) |
isolation |
该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置 |
timeout |
该属性用于设置事务的超时秒数,默认值为-1表示永不超时 |
1.6 exception不回滚解决方案
1.6.1 原因
1. Checked异常必须被显式地捕获或者传递,如Basic try-catch-finally Exception Handling一文中所说。而unchecked异常则可以不必捕获或抛出。
2. Checked异常继承Java.lang.Exception类。Unchecked异常继承自java.lang.RuntimeException类。
3. Runtime Exception: 在定义方法时不需要声明会抛出runtime exception;在调用这个方法时不需要捕获这个runtime exception; runtime exception是从java.lang.RuntimeException或java.lang.Error类衍生出来的。例如:nullpointexception,IndexOutOfBoundsException就属于runtime exception
4. Exception:定义方法时必须声明所有可能会抛出的exception;在调用这个方法时,必须捕获它的checked exception,不然就得把它的exception传递下去;exception是从java.lang.Exception类衍生出来的。例如:IOException,SQLException就属于Exception
默认情况下,如果被注解的数据库操作方法中发生了unchecked异常,所有的数据库操作将rollback;如果发生的异常是checked异常,默认情况下数据库操作还是会提交的。而Exception是checked异常,所以不会回滚。
1.6.2 解决方案
参数增加如下,即可:
6. @Transactional(rollbackFor = { Exception.class })
7. public void test() throws Exception {
8. doDbStuff1();
9. doDbStuff2();//假如这个操作数据库的方法会抛出异常,现在方法doDbStuff1()对数据库的操作会回滚。
10. }
1.7 spring +springmvc 注解事务无效解决方案
1.7.1 原因
SpringMVC启动时的配置文件,包含组件扫描、url映射以及设置freemarker参数,让spring不扫描带有@Service注解的类。
为什么要这样设置?因为servlet-context.xml与service-context.xml不是同时加载,如果不进行这样的设置,那么,spring就会将所有带@Service注解的类都扫描到容器中,等到加载service-context.xml的时候,会因为容器已经存在Service类,使得cglib将不对Service进行代理,直接导致的结果就是在service-context中的事务配置不起作用,发生异常时,无法对数据进行回滚。
1.7.2 解决方案
1. spring mvc 自动扫描注解的时候,不去扫描@Service
1. <context:component-scanbase-package= "org.cn.xxx">
2. <context:exclude-filtertype ="annotation" expression="org.springframework.stereotype.Service" />
3. </context:component-scan>
2. spring 自动扫描注解的时候,不去扫描@Controller
1. <context:component-scanbase-package ="org.cn.xxx>
2. <context:exclude-filtertype ="annotation" expression="org.springframework.stereotype.Controller" />
3. </context:component-scan>
1.8 try catch后事务不回滚解决方案
在Spring的配置文件中,如果数据源的defaultAutoCommit设置为True了,那么方法中如果自己捕获了异常,事务是不会回滚的,如果没有自己捕获异常则事务会回滚。
情况1:如果没有在程序中手动捕获异常,正常回滚
1. @Transactional(rollbackFor = { Exception.class })
2. public void test() throws Exception {
3. doDbStuff1();
4. doDbStuff2();//假如这个操作数据库的方法会抛出异常,现在方法doDbStuff1()对数据库的操作 会回滚。
5. }
情况2:如果在程序中自己捕获了异常,不会回滚
1. @Transactional(rollbackFor = { Exception.class })
2. public void test() {
3. try {
4. doDbStuff1();
5. doDbStuff2();//假如这个操作数据库的方法会抛出异常,现在方法doDbStuff1()对数据库的操作 不会回滚。
6. } catch (Exception e) {
7. e.printStackTrace();
8. }
9. }
1.8.1 原因
springaop异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚
现在如果我们需要手动捕获异常,并且也希望抛异常的时候能回滚肿么办呢?以下给出3种解决方案,供大家参考,项目中使用解决方案3。
1.8.2 解决方案1-不使用try catch
@Transactional所在函数不进行try catch捕获,而是放到上层函数进行异常捕获。
比如@Transactional放在service层,我们在service层不进行异常处理,只抛出,而在controller层进行异常捕获。
1.8.3 解决方案2-在catch中throw
catch后再throw,显示回滚
1. @Transactional(rollbackFor = { Exception.class })
2. public void test() {
3. try {
4. doDbStuff1();
5. doDbStuff2();//假如这个操作数据库的方法会抛出异常,现在方法doDbStuff1()对数据库的操作 不会回滚。
6. } catch (Exception e) {
7. e.printStackTrace();
8. throw new Exception(“error”);
9. }
10. }
1.8.4 解决方案3-手动回滚
TransactionAspectSupport手动回滚事务:
1. @Transactional(rollbackFor = { Exception.class })
2. public void test() {
3. try {
4. doDbStuff1();
5. doDbStuff2();
6. } catch (Exception e) {
7. e.printStackTrace();
8. TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//就是这一句了,加上之后,如果doDbStuff2()抛了异常, //doDbStuff1()是会回滚的
9. }
10. }
1.9 参考文献
http://blog.csdn.net/andyxuq/article/details/7982143
http://www.360doc.com/content/12/1109/18/6161903_246870991.shtml
http://my.oschina.net/guanzhenxing/blog/214228