需求
经常碰到这样的需求,在某个表里添加一条数据,但是名称不能重复,编码不能重复。其实做法无非就是去数据库里查询是否有重复数据。
V1.0
最原始的写法就是每个表都写自己的校验。类似
每个表都有一份差不多的代码,可能字段不一样。那么这个时候就可以想着怎么去把方法抽象成公共方法了。因为我们是根据字段名去判断,那么就把字段名,和要判断的值拿出来当做参数。
V2.0
所以我基于mybatis-plus 写了一个通用的方法。
给ServiceImpl再包装一层:
public class StandardBaseServiceImpl<M extends BaseMapper<T>, T extends BasicEntity> extends ServiceImpl<BaseMapper<T>, T> implements IBasicService<T> {
/**
* 全表校验字段是否重复
* @param id 数据id
* @param column 字段
* @param target 判断的目标值
* @throws
* @return boolean true 校验通过;false 未通过
* @author zhy
* @date 2021/1/27 10:36
*/
protected boolean validateFieldDuplicate(@Nullable String id, String column, Object target) {
//根据字段获取数据库列表
QueryWrapper<T> wrapper = new QueryWrapper<>();
return validateFieldDuplicate(id,wrapper,column,target);
}
/**
* 有条件校验字段是否重复
* @param id 数据id
* @param wrapper 具体条件
* @param column 字段名
* @param target 目标值
* @throws
* @return boolean true 校验通过;false 未通过
* @author zhy
* @date 2021/1/28 11:32
*/
protected boolean validateFieldDuplicate(String id,QueryWrapper<T> wrapper,String column,Object target){
wrapper.eq(column,target);
List<T> list = baseMapper.selectList(wrapper);
//列表为空证明不重复,返回true
if (CollectionUtils.isEmpty(list)){
return true;
}
//id为空则为新增,如果有列表则代表重复
if (StringUtils.isBlank(id) && !CollectionUtils.isEmpty(list)){
return false;
}
//修改数据逻辑
for (T t : list) {
//id一样的,证明未修改属性,忽略
if (t.getId().equals(id)){
continue;
}
//属性重复
return false;
}
return true;
}
}
BasicEntity 是我们表对象一定要继承的基类
@Data
public class BasicEntity implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(
type = IdType.ID_WORKER_STR
)
protected String id;
protected Integer sort;
@TableField(
fill = FieldFill.INSERT
)
protected String createBy;
@TableField(
fill = FieldFill.INSERT_UPDATE
)
protected String updateBy;
@TableField(
fill = FieldFill.INSERT
)
protected Date createTime;
@TableField(
fill = FieldFill.INSERT_UPDATE
)
protected Date updateTime;
}
然后具体业务service写法可以是这样
@Service
public class StandardAssetsBrandModelServiceImpl extends StandardBaseServiceImpl<StandardAssetsBrandModelMapper, StandardAssetsBrandModel> implements StandardAssetsBrandModelService {
@Override
public boolean save(StandardAssetsBrandModel entity) {
if (!validateFieldDuplicate(null,"model",entity.getModel())){
throw new CecClientException(CecClientCodeMessage.error("型号已存在"));
}
return super.save(entity);
}
@Override
public boolean updateById(StandardAssetsBrandModel entity) {
if (!validateFieldDuplicate(entity.getId(),"model",entity.getModel())){
throw new CecClientException(CecClientCodeMessage.error("型号已存在"));
}
return super.updateById(entity);
}
}
其实这种判重,代码会一直重复写,每次写的都差不多,当你看到这种每次代码写的差不多,就证明这些代码可以抽象出来,封装成通用的,这样使得代码美观,易维护。
但是,这样的写法会有个问题代码里会出现魔数值,也就是未经定义的变量 “model”。虽然model对应的是字段名。但是我们无法保证它不被修改。一旦哪一天被修改了。编译器是不会报错的,但是代码运行却会出现异常,因为找不到这个字段。所以我为了避免魔数值的出现,就写了一个通过lambda表达式获取字段名的工具类。
V3.0
先参考
语雀内容
然后改造一下方法
/**
* @author zhy
* @date 2021/1/2710:28
*/
@Slf4j
public class StandardBaseServiceImpl<M extends BaseMapper<T>, T extends BasicEntity> extends BasicServiceImpl<BaseMapper<T>, T> implements IBasicService<T> {
/**
* 全表校验字段是否重复
* @param id 数据id
* @param func 字段属性的get方法lambda表达式
* @param target 目标值
* @throws
* @return boolean
* @author zhy
* @date 2021/1/28 15:42
*/
protected boolean validateFieldDuplicate(@Nullable String id, Func<T,Object> func, Object target) {
String column = LambdaUtil.getSqlColumn(func);
QueryWrapper<T> wrapper = new QueryWrapper<>();
return validateFieldDuplicate(id,wrapper,column,target);
}
/**
* 全表校验字段是否重复
* @param id 数据id
* @param column 字段
* @param target 判断的目标值
* @throws
* @return boolean true 校验通过;false 未通过
* @author zhy
* @date 2021/1/27 10:36
*/
protected boolean validateFieldDuplicate(@Nullable String id, String column, Object target) {
//根据字段获取数据库列表
QueryWrapper<T> wrapper = new QueryWrapper<>();
return validateFieldDuplicate(id,wrapper,column,target);
}
/**
* 有条件校验字段是否重复
* @param id 数据id
* @param wrapper 具体条件
* @param column 字段名
* @param target 目标值
* @throws
* @return boolean true 校验通过;false 未通过
* @author zhy
* @date 2021/1/28 11:32
*/
protected boolean validateFieldDuplicate(String id,QueryWrapper<T> wrapper,String column,Object target){
wrapper.eq(column,target);
List<T> list = baseMapper.selectList(wrapper);
//列表为空证明不重复,返回true
if (CollectionUtils.isEmpty(list)){
return true;
}
//id为空则为新增,如果有列表则代表重复
if (StringUtils.isBlank(id) && !CollectionUtils.isEmpty(list)){
return false;
}
//修改数据逻辑
for (T t : list) {
//id一样的,证明未修改属性,忽略
if (t.getId().equals(id)){
continue;
}
//属性重复
return false;
}
return true;
}
}
用法改为:
@Override
@Transactional(rollbackFor = Exception.class)
public boolean updateById(StandardAssetsBrandModel entity) {
if (!validateFieldDuplicate(entity.getId(),StandardAssetsBrandModel::getModel,entity.getModel())){
throw new CecClientException(CecClientCodeMessage.error("型号已存在"));
}
return super.updateById(entity);
}