今天在做项目的时候遇到一个问题,和之前的理论知识不符,之前在学习spring transaction事务的时候,如果自己对方法抛出的异常进行了捕获了,事务是不会回滚的,但是今天我测试后发现并不是这样,那个大佬能帮我解决下,万分感谢!
下面是我的业务场景,一个类A加了事务管理,其中一个方法methedA调用了另个一个类B的方法methedB,类B也加了事务管理,类B的methedB抛出了一个异常,methedA对异常进行了捕获,但是数据还是没有插入到数据库,具体代码如下:
类A:
@Service
@Transactional(rollbackFor = Exception.class)
public class InvoiceLoggingService implements IInvoiceLoggingService { private static final Logger logger = LoggerFactory.getLogger(InvoiceLoggingService.class); @Autowired
private InvoiceBatchInputService batchService;
@Autowired
private InvoiceInputRecordDao dao; /**
* 发票录入
*
* @param record
* @return
*/
@Override
public InvoiceBatchLoggingResponse invoiceLogging(InvoiceInputRecord record) {
InvoiceBatchLoggingResponse response = new InvoiceBatchLoggingResponse();
//根据发票代码和发票号码判断该发票是否重复录入,如果重复录入就标记为重复
try {
String invoiceCode = record.getInvoiceCode();
String invoiceNumber = record.getInvoiceNumber();
InvoiceInputRecord checkRecord = this.dao.findByInvoiceCodeAndInvoiceNum(invoiceCode, invoiceNumber); record.setCreateTime(new Date());
record.setUpdateTime(new Date());
if (checkRecord != null) {
logger.debug("发票代码:{},发票号码:{}已经存在,本次为重复录入", invoiceCode, invoiceNumber);
record.setDuplicateState((byte) 1);
} //插入记录
record.setInvoiceInputRecordId(this.idGenHelper.getLongId());
this.dao.insertRecord(record); //调用后续接口
com.kingxunlian.caf.modules.logging.dto.InvoiceLoggingResponse res = this.batchService.invoiceLogging(record);
CommonUtils.beanCopy(res, response); } catch (Exception e) {
e.printStackTrace();
logger.error("发票查验异常,信息为:{}", e); }
return response;
}
}类B:
@Service
@Transactional(rollbackFor = Exception.class)
public class InvoiceBatchInputService implements IInvoiceBatchInputService { private static final Logger logger = LoggerFactory.getLogger(InvoiceBatchInputService.class); /**
* 发票批次录入dao
*/
@Resource
private InvoiceInputRecordDao invoiceInputRecordDao; /**
* 发票批次录入
*
* @param invoiceInputRecord
* @return
*/
@Override
public InvoiceLoggingResponse invoiceLogging(InvoiceInputRecord invoiceInputRecord) { logger.debug("进入发票批次录入");
InvoiceLoggingResponse loggingResponse = new InvoiceLoggingResponse();
CommonUtils.beanCopy(invoiceInputRecord, loggingResponse);
String userId = invoiceInputRecord.getUserId();
// 根据userId获取用户名
UserInfoResponse userResponse = this.userFeignClient.getUserById(userId).getBody();
if (userResponse == null) {
logger.error("根据userId获取用户信息失败!");
throw new Exception("当前用户不存在!");
} logger.debug("根据用户id:{}获取用户名:{}", userId, userResponse.getNickName());
invoiceInputRecord.setUserName(userResponse.getNickName()); this.invoiceInputRecordDao.updateByInvoiceInputRecordId(invoiceInputRecord.getInvoiceInputRecordId(), invoiceInputRecord); // 1.真假验证
this.validateReal(invoiceInputRecord); loggingResponse.setSuccess(true);
loggingResponse.setDescription("发票录入操作成功");
return loggingResponse;
}当B类方法中userResponse 为null的时候,类A中的方法insertRecord()并没有保存数据到数据库,不知道这个是为什么?但是我将类B的@Transactional(rollbackFor = Exception.class)去掉后就可以保存到数据库中
下面是我的业务场景,一个类A加了事务管理,其中一个方法methedA调用了另个一个类B的方法methedB,类B也加了事务管理,类B的methedB抛出了一个异常,methedA对异常进行了捕获,但是数据还是没有插入到数据库,具体代码如下:
类A:
@Service
@Transactional(rollbackFor = Exception.class)
public class InvoiceLoggingService implements IInvoiceLoggingService { private static final Logger logger = LoggerFactory.getLogger(InvoiceLoggingService.class); @Autowired
private InvoiceBatchInputService batchService;
@Autowired
private InvoiceInputRecordDao dao; /**
* 发票录入
*
* @param record
* @return
*/
@Override
public InvoiceBatchLoggingResponse invoiceLogging(InvoiceInputRecord record) {
InvoiceBatchLoggingResponse response = new InvoiceBatchLoggingResponse();
//根据发票代码和发票号码判断该发票是否重复录入,如果重复录入就标记为重复
try {
String invoiceCode = record.getInvoiceCode();
String invoiceNumber = record.getInvoiceNumber();
InvoiceInputRecord checkRecord = this.dao.findByInvoiceCodeAndInvoiceNum(invoiceCode, invoiceNumber); record.setCreateTime(new Date());
record.setUpdateTime(new Date());
if (checkRecord != null) {
logger.debug("发票代码:{},发票号码:{}已经存在,本次为重复录入", invoiceCode, invoiceNumber);
record.setDuplicateState((byte) 1);
} //插入记录
record.setInvoiceInputRecordId(this.idGenHelper.getLongId());
this.dao.insertRecord(record); //调用后续接口
com.kingxunlian.caf.modules.logging.dto.InvoiceLoggingResponse res = this.batchService.invoiceLogging(record);
CommonUtils.beanCopy(res, response); } catch (Exception e) {
e.printStackTrace();
logger.error("发票查验异常,信息为:{}", e); }
return response;
}
}类B:
@Service
@Transactional(rollbackFor = Exception.class)
public class InvoiceBatchInputService implements IInvoiceBatchInputService { private static final Logger logger = LoggerFactory.getLogger(InvoiceBatchInputService.class); /**
* 发票批次录入dao
*/
@Resource
private InvoiceInputRecordDao invoiceInputRecordDao; /**
* 发票批次录入
*
* @param invoiceInputRecord
* @return
*/
@Override
public InvoiceLoggingResponse invoiceLogging(InvoiceInputRecord invoiceInputRecord) { logger.debug("进入发票批次录入");
InvoiceLoggingResponse loggingResponse = new InvoiceLoggingResponse();
CommonUtils.beanCopy(invoiceInputRecord, loggingResponse);
String userId = invoiceInputRecord.getUserId();
// 根据userId获取用户名
UserInfoResponse userResponse = this.userFeignClient.getUserById(userId).getBody();
if (userResponse == null) {
logger.error("根据userId获取用户信息失败!");
throw new Exception("当前用户不存在!");
} logger.debug("根据用户id:{}获取用户名:{}", userId, userResponse.getNickName());
invoiceInputRecord.setUserName(userResponse.getNickName()); this.invoiceInputRecordDao.updateByInvoiceInputRecordId(invoiceInputRecord.getInvoiceInputRecordId(), invoiceInputRecord); // 1.真假验证
this.validateReal(invoiceInputRecord); loggingResponse.setSuccess(true);
loggingResponse.setDescription("发票录入操作成功");
return loggingResponse;
}当B类方法中userResponse 为null的时候,类A中的方法insertRecord()并没有保存数据到数据库,不知道这个是为什么?但是我将类B的@Transactional(rollbackFor = Exception.class)去掉后就可以保存到数据库中
解决方案 »
- struts2 传fatal异常 大家帮我看看 3Q
- 一个jni问题。急!!!!!!!!
- JavaMail
- struts1中页面之间迁移时 历史页面数据的保存
- 为什么每次启动tomcat,在停止的时候,都不能完全停止?
- java调用IIS发布的WEB SERVICES问题
- 关于开发性能的问题,Collection,Vector,Arraylist,List的问题,高手请进
- jdk1.4.2+tomcat5.0.28的jdbc for sql server配置问题
- 我使用log4j的时候怎么总是报找不到 log4j:ERROR Could not read configuration file [log4j.properties].啊?
- 怎样培训新人JSPStruts相关技术?请junnef(光明圣堂武士)、syuhans(S玉涵S)赶快过来领分
- 急等,html和servlet之间的值相互传递
- org.apache.tomcat.websocket.server.WsSci cannot be cast to javax.servlet.Servlet
事务是aop实现的,aop是基于动态代理,代理即代理模式,是基于类的,你B类方法执行完就会标记事务回滚,在A方法捕获异常毫无意义
你去掉@Transactional(rollbackFor = Exception.class)那B类方法没有代理事务咯,那就肯定是A来管事务咯
如果你在A捕获异常,然后再插入一条记录,会抛出异常的
你这个结果是正确的,刚开始两个方法都加了事务,那么会根据@Transactional传播行为默认是Propagation.REQUIRED 处于同一个事务中,所以你插入的数据被回滚到,因此没有保存到数据库中;第二次你将类B的注解去掉,那么B不是一个事务方法,抛出的异常不会被A监听到,这其实和事务的传播属性没有关系,应该去看看Spring的事务管理器是怎么玩的,它是分管理器和拦截器两部分,你要理解为什么加了注解,就能保证事务。
我上面不是已经解释了么?B类方法抛出异常,只要出这个方法就会标记事务回滚(只是标记回滚,还没有回滚)
我上面不是已经解释了么?B类方法抛出异常,只要出这个方法就会标记事务回滚(只是标记回滚,还没有回滚)那为什么有的地方说对异常进行捕获后就不会回滚,我这里也进行了捕获,为什么事务还是进行了回滚,麻烦您帮我讲解下,实在是没有理解
我感觉我上面白说了,我已经给你解释了,为啥B会回滚的问题
这个东西的确不是很容易理解,你需要了解spring如何实现事务控制
如果这个代码还看不懂,那就真没法说了package cn.diege.facade;public class TransactionTest { static ThreadLocal<TransactionManager> t = new ThreadLocal<>(); public static void main(String[] args) {
//这个简单的模拟aop控制事务的过程,实际上更复杂
A a = new ProxyA();
a.a();
} public static class A {
B b = new ProxyB(); public void a() {
try{
b.b();
}catch(Exception e){
e.printStackTrace();
}
}
} public static class B {
public void b() {
throw new RuntimeException("抛出异常了");
}
} public static class ProxyA extends A {
A a = new A(); public void a() {
getManager().begin();
try {
a.a();
getManager().commit();
} catch (Exception e) {
getManager().rollback();
}
}
} public static class ProxyB extends B {
B b = new B(); public void b() {
getManager().begin();
try {
b.b();
} catch (Exception e) {
//实际代码并非这么简单,他要判断当前是否事务最外层,这里简单的标记回滚
getManager().setRollbackOnly();
throw e;
}
}
} public static class TransactionManager { private boolean flag = false; public void begin() { }; public void commit() {
if(!flag){
System.out.println("事务提交了");
}else{
//实际此时会抛出异常,你可以尝试在你的a方法里加一个增删改操作,估计也会抛异常
System.out.println("事务已经标记回滚");
}
}; public void rollback() {
System.out.println("事务回滚了");
}; public void setRollbackOnly() {
this.flag = true;
}
} public static TransactionManager getManager() {
TransactionManager manager = t.get();
if (manager == null) {
t.set(new TransactionManager());
}
return t.get();
}
}
然后想知道为什么,就是补补。
spring事务怎么实现的,怎么管理的,就知道了。
简而言之为什么会这样:
a方法调用b方法(不同类),会在b方法的时候启动代理,然后在执行b的方法。代理发现已经有事务了就把b的事务标记为当前同一个事务。b抛出异常了就把事务标记为异常,然后抛出异常。a的try catch这个时候捕获了异常,也没有用应为事务已经是异常了。
如果去掉注解,就不会有这个代理,try catch就是你想要的效果了。当然你可以b上面每次new一个事务也是可以的。,
教你一步步搭建ssm框架,第三步数据库事务验证及ssm常见事务不起作用排除 - 2018
当B类上的事务注解没去掉的时候,A事务方法调用B事务方法,当B抛出异常后,虽然catch住了,但是在A中会出现事务重复提交异常,所以事务都会回滚的。