转自:https://blog.csdn.net/ProGram_BlackCat/article/details/88230287
spring的事务边界是在调用业务方法之前开始的,业务方法执行完毕之后来执行commit or rollback(spring默认取决于是否抛出runtime异常).如果抛出runtime exception 并在你的业务方法中没有catch到的话,事务会回滚。一般不需要在业务方法中catch异常,如果非要catch,在做完你想做的工作后(比如关闭文件等)一定要抛出runtime exception,否则spring会将你的操作commit,这样就会产生脏数据.所以你的catch代码是画蛇添足。
在项目中 @service层中 我们会经常在做一些增删改操作的方法上看到 spring 的事务注解 @transaction。@transaction 是让spring 帮我们实现事务的控制。但是在项目中会经常看到 有的方法中 会存在trycatch块包括的方法上注解着@transaction
eg:
@Override
@Transactional
public Json addOrder(TOrderAddReq tOrderAddReq) {
try{
//增删改方法
} catch (Exception e) {
.....
e.printStackTrace();}
// }
return json;
}
上述的方法执行后可以看到事务并没有执行,接下来再看一个例子eg:
@Override
@Transactional
public Json addOrder(TOrderAddReq tOrderAddReq) {
try{
//增删改方法
} catch (Exception e) {
// 手动硬编码开启spring事务管理
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
e.printStackTrace();}
// }
return json;
}
上述方法执行后我们可以看到事务最后执行了,但实际上 事务 执行只是因为手动硬编码开启spring事务管理起了作用 而方法上的注解并没有起作用,接下来再看一个例子eg:
@Override
@Transactional
public Json addOrder(TOrderAddReq tOrderAddReq) {
try{
//增删改方法
} catch (Exception e) {
throw new RuntimeException();
}
// }
return json;
}
上述方法执行后我们可以看到事务是执行了的,但这里有个小细节:@Transactional不做任何配置 默认是对抛出的unchecked异常回滚,checked异常不会回滚,为了让所有异常都会让事务启动可以将 @Transactional配置为 @Transactional(rollbackFor = Exception.class)
注解事务@Transactional
@Service
public class PersonService {
@Resource
private PersonMapper personMapper;
@Resource
private CompanyMapper companyMapper;
@Transactional(rollbackFor = {RuntimeException.class, Error.class})
public void saveOne(Person person) {
Company company = new Company();
company.setName("tenmao:" + person.getName());
companyMapper.insertOne(company);
personMapper.insertOne(person);
}
}
注解属性
rollbackFor
:触发回滚的异常,默认是RuntimeException
和Error
isolation
: 事务的隔离级别,默认是Isolation.DEFAULT
也就是数据库自身的默认隔离级别,比如MySQL是ISOLATION_REPEATABLE_READ
可重复读
这样就可以了,不需要其他配置。
ps:网络上还说要在@SpringBootApplication
上添加注解@EnableTransactionManagement
,在springboot2+已经不需要了
spring事务——try{...}catch{...}中事务不回滚的几种处理方式
https://www.cnblogs.com/zjfjava/p/9963958.html
当希望在某个方法中添加事务时,我们常常在方法头上添加@Transactional注解
@ResponseBody
@RequestMapping(value = "/payment", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@Transactional
public Payment paymentJson(@RequestBody PaymentRequestInfo entity) {
//method
}
容易让人忽略的是:方法上未加任何属性的@Transactional注解只能在抛出RuntimeException或者Error时才会触发事务的回滚,常见的非RuntimeException是不会触发事务的回滚的。
如果要在抛出 非RuntimeException时也触发回滚机制,需要我们在注解上添加 rollbackFor = { Exception.class }属性。
@ResponseBody
@RequestMapping(value = "/payment", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
@Transactional(rollbackFor = { Exception.class })
public Payment paymentJson(@RequestBody PaymentRequestInfo entity) {
//method
}
当然,上面事务回滚的前提是添加@Transactional注解的方法中不含有try{...}catch{...}捕获异常,使得程序运行过程中出现异常能顺利抛出,从而触发事务回滚。
在实际开发中,我们往往需要在方法中进行异常的捕获,从而对异常进行判断,为客户端返回提示信息。但是此时由于异常的被捕获,导致事务的回滚没有被触发,导致事务的失败。
下面提供几种解决方法:
1. 使用@Transactional注解,抛出@Transactional注解默认识别的RuntimeException
方法上使用@Transactional注解,在捕获到异常时在catch语句中抛出RuntimeException。
2. 使用@Transactional(rollbackFor = { Exception.class }),抛出捕获的非RuntimeException异常
方法上使用@Transactional(rollbackFor = { Exception.class })注解声明事务回滚级别,在捕获到异常时在catch语句中直接抛出所捕获的异常。
3. 手动回滚
上面两个在catch{...}中抛出异常的方法都有个不足之处,就是不能在catch{...}中存在return子句,所以设置手动回滚,当捕获到异常时,手动回滚,同时返回前台提示信息。
坑点
常见坑点1:遇到检测异常时,事务默认不回滚。
例如下面这段代码,账户余额依旧增加成功,并没有因为后面遇到SQLException(检测异常)而进行事务回滚!!
@Transactional
public void addMoney() throws Exception {
//先增加余额
accountMapper.addMoney();
//然后遇到故障
throw new SQLException("发生异常了..");
}
原因分析:因为Spring的默认的事务规则是遇到运行异常(RuntimeException及其子类)和程序错误(Error)才会进行事务回滚,显然SQLException并不属于这个范围。如果想针对检测异常进行事务回滚,可以在@Transactional 注解里使用
rollbackFor 属性明确指定异常。例如下面这样,就可以正常回滚:
@Transactional(rollbackFor = Exception.class)
public void addMoney() throws Exception {
//先增加余额
accountMapper.addMoney();
//然后遇到故障
throw new SQLException("发生异常了..");
}
常见坑点2: 在业务层捕捉异常后,发现事务不生效。
这是许多新手都会犯的一个错误,在业务层手工捕捉并处理了异常,你都把异常“吃”掉了,Spring自然不知道这里有错,更不会主动去回滚数据。例如:下面这段代码直接导致增加余额的事务回滚没有生效。
@Transactional
public void addMoney() throws Exception {
//先增加余额
accountMapper.addMoney();
//谨慎:尽量不要在业务层捕捉异常并处理
try {
throw new SQLException("发生异常了..");
} catch (Exception e) {
e.printStackTrace();
}
}
不要小瞧了这些细节,往前暴露异常很大程度上很能够帮我们快速定位问题,而不是经常在项目上线后出现问题,却无法刨根知道哪里报错。
推荐做法:在业务层统一抛出异常,然后在控制层统一处理。
@Transactional
public void addMoney() throws Exception {
//先增加余额
accountMapper.addMoney();
//推荐:在业务层将异常抛出
throw new RuntimeException("发生异常了..");
}