TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(
最近开发,有地方需要用到多线程,每个线程里面处理多个方法,过程中遇到了一个问题,我们使用平时的@Transactional注解,就是当前一个方法执行完成(比如插入操作),后一个方法是不会事务回滚的。当时觉得很不可思议,后来经过半天时间,终于挖出原因,并成功解决。
我这里先说明原因:多线程底层连接数据库的时候,时使用的线程变量(TheadLocal),所以,开多少线程理论上就会建立多少个连接,每个线程有自己的连接,事务肯定不是同一个了。
解决办法:我强制手动把每个线程的事务状态放到一个同步集合里面。然后如果有单个异常,循环回滚每个线程。
代码如下:
1、注入
@Autowired
private PlatformTransactionManager transactionManager;
2、多线程操作
@Override
public String importBatch(String url,String taskId) {
if (StringUtils.isEmpty(taskId)){
return "请选择外呼任务";
}
Task task = taskService.selectById(taskId);
if (task == null){
return "外呼任务不存在,Id为:"+taskId;
}
String filePath = tmpFilePath + url;
List<Customer> customers = ExcelTemplateExportUtil.importExcel(filePath, 0, 1, Customer.class);
if (CollectionUtils.isEmpty(customers)){
return "Excel数据不能为空";
}
int totalCustomerCnt = customers.size();
if (totalCustomerCnt > 5000){
return "最多支持5000条,请分批导入";
}
//过滤掉空属性
List<String> phones = new ArrayList<>();
List<Customer> customersNotNull = new ArrayList<>();
String corpCode = ExecutionContext.getCorpCode();
String userId = ExecutionContext.getUserId();
for (Customer customer : customers) {
String phone = customer.getPhone();
if (StringUtils.isEmpty(customer.getName()) || StringUtils.isEmpty(phone)){
continue;
}
customersNotNull.add(customer);
phones.add(phone);
}
String result = "";
int notNullSize = customersNotNull.size();
int size = customers.size();
if (notNullSize < size){
result += "存在客户名称或手机号码为空:"+(size - notNullSize)+"条记录";
}
//过滤手机号重复的
List<Customer> validCustomers = new ArrayList<>();
List<Customer> repeatCustomers = this.selectList(new EntityWrapper<Customer>().eq("corp_code", corpCode).in("phone", phones));
if (CollectionUtils.isNotEmpty(repeatCustomers)){
result += "<br/>存在手机号码重复:"+(repeatCustomers.size())+"条记录";
List<String> repeatPhones = new ArrayList<>(repeatCustomers.size());
for (Customer repeatCustomer : repeatCustomers) {
repeatPhones.add(repeatCustomer.getPhone());
}
for (Customer customer : customersNotNull) {
if (repeatPhones.contains(customer.getPhone())){
continue;
}
validCustomers.add(customer);
}
}else {
validCustomers = customersNotNull;
}
for (Customer validCustomer : validCustomers) {
validCustomer.setCustomerType(UkConstant.CUSTOMER_TYPE_INDIVIDUALITY);
validCustomer.setChannelType(UkConstant.CHANNEL_TYPE_CALL_OUT);
validCustomer.setCalloutTaskStatus(0);
validCustomer.setCalloutDistributeStatus(0);
validCustomer.setCalloutTaskId(taskId);
validCustomer.setCorpCode(corpCode);
validCustomer.setCreater(userId);
}
if (CollectionUtils.isEmpty(validCustomers)){
return "成功导入:"+ 0 +"条记录<br/>"+result;
}
List<List<Customer>> customersList = new ArrayList<>();
int total = validCustomers.size();
int threads = 5;
int oneSize = total/5 +1;
int start = 0;
int end = 0;
for (int i = 0; i <threads ; i++) {
start = i * oneSize;
end = (i+1)*oneSize;
if (i<threads-1){
customersList.add(validCustomers.subList(start,end));
}else {
customersList.add(validCustomers.subList(start,validCustomers.size()));
}
}
//先在开启多线程外面,定义一个同步集合:
List<TransactionStatus> transactionStatuses = Collections.synchronizedList(new ArrayList<TransactionStatus>());
CountDownLatch latch= new CountDownLatch(threads);
for (int i = 0; i < threads; i++) {
int finalI = i;
ThreadPoolUtils.fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); // 事物隔离级别,开启新事务,这样会比较安全些。
TransactionStatus status = transactionManager.getTransaction(def); // 获得事务状态
transactionStatuses.add(status);
try{
//执行业务逻辑 插入或更新操作
improtInsertBath(corpCode,userId,customersList.get(finalI));
}catch(Exception e){
e.printStackTrace();
//异常 回滚所有事务
for (TransactionStatus transactionStatus:transactionStatuses) {
transactionStatus.setRollbackOnly();
}
}
latch.countDown();
}
});
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//更新外呼任务客户总数 customerCnt、未分配客户数 notDistributeCnt、未拨打数 not_call_cnt
task.setCustomerCnt(task.getCustomerCnt()+total);
task.setNotDistributeCnt(task.getNotDistributeCnt()+total);
task.setNotCallCnt(task.getNotCallCnt()+total);
taskService.updateById(task);
return "成功导入:"+ total +"条记录<br/>"+result;
}
3、整体类
package com.ps.uzkefu.apps.crm.service.impl;
import com.baomidou.mybatisplus.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.ps.uzkefu.apps.callout.entity.Task;
import com.ps.uzkefu.apps.callout.service.TaskService;
import com.ps.uzkefu.apps.crm.entity.Customer;
import com.ps.uzkefu.apps.crm.entity.CustomerRecord;
import com.ps.uzkefu.apps.crm.mapper.CustomerDao;
import com.ps.uzkefu.apps.crm.service.CustomerRecordService;
import com.ps.uzkefu.apps.crm.service.CustomerService;
import com.ps.uzkefu.apps.oms.account.entity.DefinedField;
import com.ps.uzkefu.apps.oms.account.entity.DefinedFieldOption;
import com.ps.uzkefu.apps.oms.account.entity.DefinedFieldValue;
import com.ps.uzkefu.apps.oms.account.service.DefinedFieldOptionService;
import com.ps.uzkefu.apps.oms.account.service.DefinedFieldService;
import com.ps.uzkefu.apps.oms.account.service.DefinedFieldValueService;
import com.ps.uzkefu.apps.oms.account.service.UserService;
import com.ps.uzkefu.base.BaseServiceImpl;
import com.ps.uzkefu.common.ExecutionContext;
import com.ps.uzkefu.common.UkConstant;
import com.ps.uzkefu.util.DateUtil;
import com.ps.uzkefu.util.ExcelTemplateExportUtil;
import com.ps.uzkefu.util.ThreadPoolUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.executor.BatchExecutorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import java.sql.BatchUpdateException;
import java.util.*;
import java.util.concurrent.CountDownLatch;
/**
* <p>
* 客户 服务实现类
* </p>
*
* @author WuZhiWei
* @since 2018-07-06
*/
@Transactional(rollbackFor = {Exception.class})
@Service
public class CustomerServiceImpl extends BaseServiceImpl<CustomerDao, Customer> implements CustomerService {
@Autowired
private DefinedFieldValueService definedFieldValueService;
@Autowired
private DefinedFieldService definedFieldService;
@Autowired
private DefinedFieldOptionService definedFieldOptionService;
@Autowired
private CustomerRecordService customerRecordService;
@Autowired
private UserService userService;
@Autowired
private TaskService taskService;
@