拓展mybatisPlus实现类方法,批量插入时,唯一索引冲突后更新
唯一索引注解
package com.zk.fahai.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author ZHAIKE
* @date 2022/01/04 9:28
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FhId {}
拓展服务类,使用时extend即可
package com.zk.fahai.util;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zk.fahai.common.annotation.FhId;
import com.zk.fx.common.util.JsonUtils;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.transaction.annotation.Transactional;
/** 批量插入,冲突更新 @param entityList 数据 */
@Transactional(rollbackFor = Exception.class)
public void saveOrUpdateBatchByFhId(Collection<T> entityList) throws IOException {
TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);
Assert.notNull(
tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");
List<String> ukFieldNameList =
tableInfo.getFieldList().stream()
.filter(c -> c.getField().isAnnotationPresent(FhId.class))
.map(tableFieldInfo -> tableFieldInfo.getField().getName())
.collect(Collectors.toList());
if (ukFieldNameList.size() < 1) {
logger.error("未找到唯一索引,class:{}", tableInfo.getEntityType());
return;
}
for (List<T> list : this.cutList(entityList, IService.DEFAULT_BATCH_SIZE)) {
try {
super.saveBatch(list);
} catch (DuplicateKeyException duplicateKeyException) {
for (T entity : list) {
try {
super.save(entity);
} catch (DuplicateKeyException e2) {
logger.info("数据更新触发唯一索引,对象:{}", JsonUtils.object2JsonString(entity));
UpdateWrapper<T> updateWrapper = new UpdateWrapper<>();
ukFieldNameList.forEach(
uk -> updateWrapper.eq(uk, ReflectionKit.getFieldValue(entity, uk)));
super.update(entity, updateWrapper);
}
}
}
}
}
/** 切分集合 */
public <C> List<List<C>> cutList(Collection<C> list, int maxNum) {
int step = (list.size() + maxNum - 1) / maxNum;
return Stream.iterate(0, n -> n + 1)
.limit(step)
.parallel()
.map(
a ->
list.stream()
.skip(a * maxNum)
.limit(maxNum)
.parallel()
.collect(Collectors.toList()))
.collect(Collectors.toList());
}
}
不使用saveBatch版,拓展服务类,使用时extend即可
发现oracle使用savebatch时有问题
package com.zk.fahai.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 法海数据唯一索引
*
* @author ZHAIKE
* @date 2022/01/04 9:28
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FhUniqueIndex {}
package com.zk.fahai.service.fh;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zk.fahai.common.annotation.FhUniqueIndex;
import com.zk.fx.common.util.JsonUtils;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.transaction.annotation.Transactional;
/**
* 拓展 mybatis-plus方法,数据落库使用
*
* @author ZHAIKE
* @date 2022/01/17 16:15
*/
public class DataStorageExpandService<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> {
private static final Logger logger = LoggerFactory.getLogger(DataStorageExpandService.class);
/** 插入,冲突更新 @param entityList 数据 */
@Transactional(rollbackFor = Exception.class)
public void saveOrUpdateByFhUk(List<T> entityList) throws IOException {
List<String> fhUkFieldNameList = this.getFhUkFieldNameList();
for (T entity : entityList) {
this.saveOrUpdateByFhUniqueIndex(fhUkFieldNameList, entity);
}
}
/** 插入,冲突更新 @param entity 数据 */
@Transactional(rollbackFor = Exception.class)
public void saveOrUpdateByFhUk(T entity) throws IOException {
this.saveOrUpdateByFhUniqueIndex(this.getFhUkFieldNameList(), entity);
}
private void saveOrUpdateByFhUniqueIndex(List<String> fhUkFieldNameList, T entity)
throws IOException {
try {
super.save(entity);
} catch (DuplicateKeyException e2) {
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
fhUkFieldNameList.forEach(
ukFieldName -> {
Object ukFieldValue = ReflectionKit.getFieldValue(entity, ukFieldName);
queryWrapper.eq(ukFieldName, ukFieldValue);
});
this.fillId(entity, queryWrapper);
logger.info("数据保存触发唯一索引,进行更新,数据 ->{}", JsonUtils.object2JsonString(entity));
super.updateById(entity);
}
}
/** 获取主键回填 */
private void fillId(T entity, QueryWrapper<T> queryWrapper) {
TableInfo tableInfo = TableInfoHelper.getTableInfo(super.entityClass);
Assert.notNull(
tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");
String keyProperty = tableInfo.getKeyProperty();
Assert.notEmpty(
keyProperty, "error: can not execute. because can not find column for id from entity!");
Long idVal = (Long) ReflectionKit.getFieldValue(super.getOne(queryWrapper), keyProperty);
try {
Field idField = entity.getClass().getDeclaredField(keyProperty);
idField.setAccessible(true);
idField.set(entity, idVal);
} catch (NoSuchFieldException | IllegalAccessException e) {
logger.error("获取主键回填异常", e);
}
}
private List<String> getFhUkFieldNameList() {
TableInfo tableInfo = TableInfoHelper.getTableInfo(super.entityClass);
Assert.notNull(
tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!");
List<TableFieldInfo> tableFieldInfoList =
tableInfo.getFieldList().stream()
.filter(c -> c.getField().isAnnotationPresent(FhUniqueIndex.class))
.collect(Collectors.toList());
Assert.notEmpty(
tableFieldInfoList,
"error: can not execute. because can not find @FhUniqueIndex for entity!");
List<String> fhUkFieldNameList =
tableFieldInfoList.stream()
.map(tableFieldInfo -> tableFieldInfo.getField().getName())
.collect(Collectors.toList());
Assert.notEmpty(
fhUkFieldNameList,
"error: can not execute. because can not find column for @FhUniqueIndex from entity!");
return fhUkFieldNameList;
}
}