spring的事务
今天学习了以下spring boot的事务,发现真他娘的方便阿。我可能就是典型的那种打死也不吃,最后来一句真香的那种人了。越看spring boot越觉得好用。
在spring boot使用事物非常简单。
前提:你的数据引擎是支持事务的。
首先在启动类要加上
@EnableTransactionManagement,从而支持事物。
然后在Service层使用
@Transactional
该注解可以使用在类上,这样就是所有方法都是使用事务。同时也可以使用在方法上。
只是有一点要注意,使用了事务注解,就不要在使用try.....catch捕获异常了,否则事物是不起作用的。
@Transactional
public boolean insert2Db(Order order){
insert1;
insert2;
update3;
......
}
如果其中一个失败,就会回滚。
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。分割线。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
今天在测试事务的时候,遇到了几个坑,当我显示添加一个错误的时候,事务并没有回滚。看例子:
@Service
public class SAPServiceImpl implements SAPService {
public static Logger logger = LoggerFactory.getLogger(SAPServiceImpl.class.getName());
public static final String post_url = "http://test.com";
@Autowired
FinSAPBatchMapper finSAPBatchMapper;
@Autowired
FinSAPMainMapper finSAPMainMapper;
@Autowired
FinSAPDetailMapper finSAPDetailMapper;
public boolean dealRespFromSAP(SAPData sapData,Map<String,String> response){
logger.info("reponse from miora",response);
System.out.println(Arrays.toString(response.entrySet().toArray()));
if (response == null){
System.out.print("Response from miora is null");
return false;
}
switch (response.get("code")){
case "":
System.out.print("Response from mioracle is empty");
return false;
case "200":
//TODO 处理响应,更新数据库状态
Integer status = 1;
try {
// if (this.updateDbStatus(sapData, status)) {
if (this.updateDbStatus(sapData, status)) {
System.out.print("Update Ok!");
return true;
} else {
System.out.print("Updat 失败");
return false;
}
}catch (Exception e){
logger.error(e.getMessage() + "testtransction");
}
default:
String code = response.get("code");
String desc = response.get("desc");
System.out.print("Code:" + code + " desc:"+desc);
return false;
}
}
@Transactional
public boolean updateDbStatus(SAPData sapData,Integer status) {
System.out.println("Start to update status in database");
String cur_date_time = "";
// try {
// finSAPBatchMapper.updateStatus(sapData.getBatchId(), status, cur_date_time);
// finSAPMainMapper.updateStatus(sapData.getBatchId(), status, cur_date_time);
// return true;
// }catch (Exception e){
// System.out.println("update error :" + e.getMessage());
// return false;
// }
}
这是第一版本的代码,我发现当我第二个数据库操作错误的时候,事务并不起作用。后来查阅代码首先发现了一个错误:
1、不能用catch捕获异常,否则检测不到异常,事务也不会回滚。
然后我就把代码改了。
@Transactional(rollbackFor=Exception.class)
public boolean updateDbStatus(SAPData sapData,Integer status) {
System.out.println("Start to update status in database");
String cur_date_time = "";
finSAPBatchMapper.updateStatus(sapData.getBatchId(), status, cur_date_time);
finSAPMainMapper.updateStatus(sapData.getBatchId(), status, cur_date_time);
return true;
但时,发现还是不起作用,接着查资料,发现了不能在同一个service类中一个非事务方法调用事务方法,这样不起作用。具体原理可见: service内部类的调用 。 另一篇好的总结: spring boot的坑
在网上有很多办法,但我试了一下都不好使,比如在service标签上添加Transactional,这样就会每调用一个,都会开启一个事务,但不知道怎么回事,就是不管用啊。
后来,查到一个方法:不要使用实例调用,应该直接使用接口调用。即不要用this,methoA,应该用 service.methodA(),当然,前提是必须是接口方法啊。你得首先定义好接口方法。
首先自动注入一个service.
@Autowired
private SAPService sapService;
然后,在调用的时候是这样:
try {
if (sapService.updateDbStatus(sapData, status)) {
System.out.print("Update Ok!");
return true;
} else {
System.out.print("Updat 失败");
return false;
}
}catch (Exception e){
logger.error(e.getMessage() + "testtransction");
}
改好之后,再测试,事务生效了。
如果想在事务方法中catch异常,一定要手动rollback:
@Transactional(rollbackFor = Exception.class)
public boolean updateDbStatus(SAPData sapData,Integer status) {
System.out.println("Start to update status in database");
String cur_date_time = "";
try {
finSAPBatchMapper.updateStatus(sapData.getBatchId(), status, cur_date_time);
finSAPMainMapper.updateStatus(sapData.getBatchId(), status, cur_date_time);
return true;
}catch (Exception e){
System.out.println("update error :" + e.getMessage());
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return false;
}
}
此外,有一些还要注意:比如,使用事务的方法必须是public。
其实主要原因是spring的事务通过AOP代理来实现的,使用Transaction注解的,就会进行事务拦截。
如果在类内部调用不会走代理,也就导致代理失效,事务无法使用。
我去,这一下来,这个框架限制还挺多,使用php的laravel的框架时候,事务随便用啊!静态语言适合大型项目,更系统化,但灵活度和php,python等动态语言比,还是有劣势的。所以说,语言只是工具,工具就注定了每一种语言都不是万能的,都会有各自的优势和劣势。
参考资料:
微信分享/微信扫码阅读