背景介绍
在spring中可以使用@Transactional
注解去控制事务,当出现异常时会进行回滚,在多线程中,这个注解则不会生效,如果主线程需要先执行一些修改数据库的操作,当子线程在进行处理出现异常时,主线程修改的数据则不会回滚,导致数据错误!
示例
package com.smallrig.wms.pick.controller;
import com.smallrig.wms.pick.entity.WmsHeadPicketTicket;
import com.smallrig.wms.pick.entity.WmsHeadPicketTicketLog;
import com.smallrig.wms.pick.mapper.WmsHeadPicketTicketLogMapper;
import com.smallrig.wms.pick.mapper.WmsHeadPicketTicketMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
@Slf4j
@RestController
public class TestController {
@Resource
private SqlSessionTemplate sqlSessionTemplate;
/**
* 测试多线程事务
* @throws SQLException
*/
@GetMapping(value = "/test")
public void saveThread() throws SQLException {
// 获取数据库连接,获取会话(内部自有事务)
SqlSessionFactory sqlSessionFactory = sqlSessionTemplate.getSqlSessionFactory();
SqlSession sqlSession = sqlSessionFactory.openSession();
Connection connection = sqlSession.getConnection();
try {
// 设置手动提交
connection.setAutoCommit(false);
//获取mapper
WmsHeadPicketTicketLogMapper mapper1 = sqlSession.getMapper(WmsHeadPicketTicketLogMapper.class);
WmsHeadPicketTicketMapper mapper2 = sqlSession.getMapper(WmsHeadPicketTicketMapper.class);
//子线程
List<Callable<Integer>> callableList = new ArrayList<>();
for (int i =0;i<5;i++){
WmsHeadPicketTicketLog log = WmsHeadPicketTicketLog.builder()
.wmsHeadPicketTickeId(2L)
.operateType("内部")
.content("内部")
.createBy("系统")
.createTime(new Date())
.build();
Callable<Integer> callable = () -> {
WmsHeadPicketTicket one = WmsHeadPicketTicket.builder()
.headNo("HS22032300004")
.build();
mapper2.insert(one);
//子线程内部异常
if(1 == 1){
throw new RuntimeException("子线程异常!");
}
return mapper1.insert(log);
};
callableList.add(callable);
}
//执行子线程
List<Future<Integer>> futures = new ArrayList<>();
callableList.forEach(e -> {
FutureTask ft=new FutureTask(e);
ft.run();
futures.add(ft);
});
//获取结果处理事务回滚
for (Future<Integer> future:futures) {
if (future.get()<=0){
//事务回滚
connection.rollback();
return;
}
}
WmsHeadPicketTicketLog log = WmsHeadPicketTicketLog.builder()
.wmsHeadPicketTickeId(2L)
.operateType("外部")
.content("外部")
.createBy("系统")
.createTime(new Date())
.build();
int insert = mapper1.insert(log);
if(insert > 0){
//主线程异常
//throw new RuntimeException("主线程异常");
}
//添加完毕
//事务提交
connection.commit();
}catch (Exception e){
//事务回滚
connection.rollback();
throw new RuntimeException("出现异常");
}
}
}