1. Mybatis-Plus根据其他字段批量保存或更新
业务背景:经常要同步其他系统的数据过来,而且需要使用雪花id替代别人的表中的主键,所以增量同步的时候需要考虑其他字段来判断是更新还是保存
2. 谈谈mybatis-plus的saveOrUpdateBatch方法
#com.baomidou.mybatisplus.extension.service.impl.ServiceImpl#saveOrUpdateBatch
@Transactional(rollbackFor = Exception.class)
@Override
public boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) {
TableInfo tableInfo = TableInfoHelper.getTableInfo(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!");
return SqlHelper.saveOrUpdateBatch(this.entityClass, this.mapperClass, this.log, entityList, batchSize, (sqlSession, entity) -> {
// 这里是获取表对象的主键值
Object idVal = tableInfo.getPropertyValue(entity, keyProperty);
// 这里的lambda表达是就是SqlHelper#saveOrUpdateBatch方法中的BiPredicate<SqlSession, E> predicate
// 判断id值为空,或者根据id查询不到数据的情况下,就执行插入操作,否则就执行下面的更新操作
return StringUtils.checkValNull(idVal)
|| CollectionUtils.isEmpty(sqlSession.selectList(getSqlStatement(SqlMethod.SELECT_BY_ID), entity));
}, (sqlSession, entity) -> {
// 这里的lambda表达是就是SqlHelper#saveOrUpdateBatch方法中的BiPredicate<SqlSession, E> predicate, BiConsumer<SqlSession, E> consumer
// 执行更新操作
MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
param.put(Constants.ENTITY, entity);
sqlSession.update(getSqlStatement(SqlMethod.UPDATE_BY_ID), param);
});
}
# com.baomidou.mybatisplus.extension.toolkit.SqlHelper#saveOrUpdateBatch
public static <E> boolean saveOrUpdateBatch(Class<?> entityClass, Class<?> mapper, Log log, Collection<E> list, int batchSize, BiPredicate<SqlSession, E> predicate, BiConsumer<SqlSession, E> consumer) {
String sqlStatement = getSqlStatement(mapper, SqlMethod.INSERT_ONE);
return executeBatch(entityClass, log, list, batchSize, (sqlSession, entity) -> {
if (predicate.test(sqlSession, entity)) {
sqlSession.insert(sqlStatement, entity);
} else {
consumer.accept(sqlSession, entity);
}
});
}
所以要想换成根据其他字段保存更新或者保存,
保存的话就只需要将lambda中的判断条件改成根据其他字段查询是否有数据,有就保存。没有就更新
更新这里这里还有个细节,因为是根据其他字段更新,所以原来的方法就不能直接使用了,因为原来的方法是已根据id更新。
3. 重写IService和ServiceImpl
public interface EnhancedService<T> extends IService<T> {
boolean saveOrUpdateBatchByColumn(Collection<T> entityList, Function<T, LambdaQueryWrapper<T>> function);
}
public class EnhancedServiceImpl<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> {
/**
* 可以根据其他字段批量更新或新增
*
* @param entityList 数据集合
* @param function 新增或者更新判断条件
* @return 操作结果
*/
public boolean saveOrUpdateBatchByColumn(Collection<T> entityList, Function<T, LambdaQueryWrapper<T>> function) {
return SqlHelper.saveOrUpdateBatch(this.entityClass, this.mapperClass, this.log, entityList, DEFAULT_BATCH_SIZE, (sqlSession, entity) -> {
Map<String, Object> param = Maps.newHashMap();
param.put(Constants.ENTITY, entity);
param.put(Constants.WRAPPER, function.apply(entity));
return CollectionUtils.isEmpty(sqlSession.selectList(this.getSqlStatement(SqlMethod.SELECT_MAPS), param));
}, (sqlSession, entity) -> {
Map<String, Object> param = Maps.newHashMap();
param.put(Constants.ENTITY, entity);
param.put(Constants.WRAPPER, function.apply(entity));
sqlSession.update(this.getSqlStatement(SqlMethod.UPDATE), param);
});
}
}
4. 使用如下
实体类如下
public class User implements Serializable {
private static final long serialVersionUID = 1L;
//雪花id
@TableId
private Long id;
private String age;
private String code;
private String name;
private String other1;
private String other2;
private String other3;
private Integer fillType;
@TableField(fill = FieldFill.INSERT)
private Date createTime;
}
比如你想根据 name,code,age三个字段来更新。
首先你得userService要实现EnhancedServiceImpl
public interface UserService extends EnhancedService<User> {
@Service
public class UserServiceImpl extends EnhancedServiceImpl<UserMapper, User> implements UserService {
}
示例代码如下:
userService.saveOrUpdateBatchByColumn(Arrays.asList(user1, user2),
item -> Wrappers.lambdaQuery(User.class)
.eq(User::getName, item.getName())
.eq(User::getCode, item.getCode())
.eq(User::getAge, item.getAge())
);