要求:通过excel导入一批数据,通过线程池处理,统一成功或失败,失败的时候立即退出,未消费的数据全部作废
比如有一个学生管理系统,教导专员可以通过excel批量导入学生,学生表如下
字段 | 类型 |
---|---|
id | int |
name | varchar(2) |
注意了,这里的name长度为2 ,此时班里来了一个王二狗,批量导入会失败,这时候需求是批量导入的学生只能一起成功或者失败,网上说通过手工开启线程池能实现事务统一提交和回滚,于是我这么写
public void addStudentsV2(List<Student> students){
TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
for (Student student : students) {
try {
studentMapper.addStudent(student);
} catch (Exception e) {
dataSourceTransactionManager.rollback(transactionStatus);
return;
}
}
dataSourceTransactionManager.commit(transactionStatus);
}
事实证明是可以的,因为springboot底层用的是threadlocal 控制事务,但是我要的是通过线程池批量处理,因为导入的数据量很大,于是我改成如下实现,发现压根没生效,求正确写法
@Override
public void addStudents(List<Student> students){
TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
List<Future<Student>> futures=new ArrayList<>();
for (Student student : students) {
Future<Student> future = executorService.submit(() -> {
studentMapper.addStudent(student);
log.info("任务结束,{}",student);
return student;
});
futures.add(future);
}
for (Future<Student> future : futures) {
Student student = null;
try {
student = future.get();
} catch (Exception e) {
e.printStackTrace();
dataSourceTransactionManager.rollback(transactionStatus);
return;
}
}
dataSourceTransactionManager.commit(transactionStatus);
}
这个不能直接用@Transactional(rollbackFor=Exception.class) 解决吗?
JDBC连接以启用自动提交模式开始,其中每个 SQL 语句都隐式地与一个事务划界。
希望每个事务执行多个语句的用户必须关闭自动提交。
con.setAutoCommit(false);//其中con是活动连接
con.commit();//数据提交,如果没有执行,则没有任何数据提交
更改自动提交模式会触发当前事务的提交(如果一个处于活动状态)。
代码改成如下试试:
@Transactional(rollbackFor = Exception.class)
public void addStudents(List<Student> students){
List<Future<Student>> futures=new ArrayList<>();
for (Student student : students) {
Future<Student> future = executorService.submit(() -> {
studentMapper.addStudent(student);
log.info("任务结束,{}",student);
return student;
});
futures.add(future);
}
for (Future<Student> future : futures) {
Student student = null;
try {
student = future.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return;
}
}
}
我的理解是这样的,一个事务对应一个数据库连接,如果你想多线程使用同一事务,实际上就是多线程使用同一个数据库连接。
这样的话,数据的提交还是分先后的,多线程执行就没意义了。
不如换成单线程批量提交。
可以尝试通过mapper拿到connection,然后自己控制事务
如果想控制事务,你就要为每个任务生成一个TransactionStatus 而你的Future 接受的不应该是Student 而是TransactionStatus
可以参考下https://blog.csdn.net/babing18258840900/article/details/105120935?spm=1001.2014.3001.5501
线程池可以等所有线程执行成功的机制
所有线程执行成功后再提交事务
JDBC连接以启用自动提交模式开始,其中每个 SQL 语句都隐式地与一个事务划界。
希望每个事务执行多个语句的用户必须关闭自动提交。
con.setAutoCommit(false);//其中con是活动连接
con.commit();//数据提交,如果没有执行,则没有任何数据提交
更改自动提交模式会触发当前事务的提交(如果一个处于活动状态)。
为每个任务生成一个TransactionStatus