您的位置:首页 > 编程语言 > Java开发

spring事务使用+常见出错解决方案

2015-09-06 16:20 549 查看

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



/article/3532433.html



/article/3536320.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: