今天在用@Transactional的时候遇到几个很奇怪的问题,一段从旧程序上拷过来的代码结果死活不执行,让我百思不得其解。
旧的代码是这样的,一直运行正常
@Override
public Pager getPager(Map<String, Object> paramMap) {
List<TaSjjhLog> logList = TaSjjhLog.getWaitingList();
if(logList != null && logList.size() > 0){
this.saveList(logList);
}
return this.dao.getPager(paramMap);
}
@Transactional
private void saveList(List<TaSjjhLog> logList) {
for (TaSjjhLog log : logList) {
log.setErrorMessage(this.handleErrorMsg(log.getErrorMessage()));
this.dao.save(log);
}
TaSjjhLog.removeAllFromWaitingList(logList);
}
新的代码是这样的,结果保存操作不起作用
@Override
public List<ChangeLog> getList(Pager<ChangeLog> pager, String mc) {
List<changelog> logList = ChangeLog.getWaitingList();
if(logList != null && logList.size() > 0){
this.service.saveList(logList);
}
return this.dao.getList(pager, mc);
}
@Transactional
private void saveList(List<ChangeLog> logList) {
logList.stream().forEach(log->{
log.setErrorMessage(this.handleErrorMsg(log.getErrorMessage(), log.getRecordCount(), log.getExecutedCount()));
this.dao.save(log);
ChangeLog.removeFromWaitingList(log);
});
}</changelog>
起初我还以为是Lambda表达式的问题,改写了一下,结果是一样的。
然后我就不停的测试,不停的改@transactional的事务级别,结果都没用。
然后我就看我的dao里面的基类,结果发现了一点不同
旧代码在dao的基类上加了@Transactional,而新代码则是加了@Transactional(readOnly=true)
所以dao里面的save方法,旧代码是有事务的,而新代码则是只读的事务。可以问题来了,为什么我在新代码的saveList上加的事务注解不起作用?
我在网上没有找到相应的解释,但是通过我的测试和对spring的认识,我觉得spring的事务是通过切面(或者也可以说是代理)实现的,当你调用一个代理类的接口,如果其标注了事务注解,则会生效,
而我这个saveList是在service里面自己的方法调用的,这个注解不会被处理。
于是我修改代码,结果使用了三种方案全部失败:
1. 在getList上加事务注解,取消saveList上的注解:这种方式可以保存数据成功,但是查询出来的数据在SpringMVC转换结果为json的时候报错,说no session.
2. 在dao基类的save方法上加注解:结果同上
3. service提供两个接口给controller,分别是getList和saveList,都加上事务注解:结果还是同上。
经验告诉我,当你的请求包含了提交事务的时候,你同时又读了数据,并且需要在controller或是view层懒加载数据,则会有问题,因为事务一提交就关闭了。
我唯一不能理解的是我先调用saveList,提交了事务,保存了数据,我再调用getList,按理应该重新生成事务,可是为什么到controller这里还是没有session了。
最后没办法,我把读写两个事情在controller层也分开为两个接口,在jsp里面做两次请求来处理。
总结一下:
1. service里面的方法如果不是对外的接口,加事务注解没有用
2. 一个请求里面如果包含了提交事务(非只读),同时又查询数据的话,查询的数据要立即加载,不能到view层再调用session去懒加载数据。