并发插入数据导致数据库唯一键冲突报DuplicateKeyException,可以使用replace into,insert on duplicate key update来解决,但是会有mysql锁的问题,用代码来解决
public abstract class DbUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(DbUtils.class);
public static <T, KEY> int insertOnDuplicateKeyUpdate(List<T> dataList, Function<T, KEY> getUniqueKey,
Function<List<T>, Integer> doInsert, Function<List<T>, List<T>> queryExists,
BiFunction<List<T>, List<T>, Integer> doUpdate, int tryCount) {
Object savePoint = null;
try {
savePoint = TransactionSynchronizationManager.isActualTransactionActive() ? TransactionAspectSupport.currentTransactionStatus().createSavepoint() : null;
Integer result = doInsert.apply(dataList);
return result;
}
catch (DuplicateKeyException onDuplicate) {
if (tryCount == 0) {
throw onDuplicate;
}
if (savePoint != null) {
TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);
}
List<T> existsList = queryExists.apply(dataList);
Set<KEY> existsKeys = existsList.stream().map(getUniqueKey).collect(Collectors.toSet());
List<T> insertList = dataList.stream().filter(x -> !existsKeys.contains(getUniqueKey.apply(x))).collect(Collectors.toList());
List<T> updateList = dataList.stream().filter(x -> existsKeys.contains(getUniqueKey.apply(x))).collect(Collectors.toList());
int updateResult = doUpdate.apply(existsList, updateList);
int insertResult = CollectionUtils.isNotEmpty(insertList) ? insertOnDuplicateKeyUpdate(insertList, getUniqueKey, doInsert, queryExists, doUpdate, tryCount - 1) : 0;
LOGGER.info("onDuplicateKey insert size {} result {}, update size {} result {} {}",
insertList.size(), insertResult, updateList.size(), updateResult, savePoint == null? "": "transactional");
return insertResult + updateResult;
}
}
}
网友评论