每天都在进步,说明你有上进心

如果你记忆力差,那可以多写来弥补

每天进步一点点,幸福自然多一点!

前言:

相信大家在开发项目中,添加数据或者修改数据的时候某些唯一性字段都需要进行重复验证,一般验证的情况我们思路大致如下

以验证 code为例

1.如果是添加操作,我们需要将编码进行查询,如果查询结果不为空,那说明有重复数据。

select * from tablename where code = xxx

2.如果是更新操作 ,我们不仅需要将编码进行查询,还要加个id不等于当前编辑数据的id,如果查询结果不为空,说明有重复数据

select * from tablename where code = xxx and id != xx

其实这样做来,我们重复编码就不出现很多,很麻烦!

这个时候怎么办呢!

对,你应该也想到了,自定义注解校验,通过在实体类中加入注解,来判断需要对哪些字段进行校验。

下面是我们对字典类型为device_model中字典名称进行唯一校验

 

 

 

 

 

 

 

 接下来我们谈下实现:

1.创建FieldRepeat注解

 注解中有两个属性,

  ①数组类型的fields(我们需要验证的字段,因为有可能多个条件才能确定唯一,所以我们设计成数组类型)

  ②message(出现重复数据返回到前端的消息)

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

/**
* @author xiaokang
* @description
* @date 2020/12/29 14:52
*/
@Documented
/**
* 指定注解运用的地方:
* ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
* ElementType.CONSTRUCTOR 可以给构造方法进行注解
* ElementType.FIELD 可以给属性进行注解
* ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
* ElementType.METHOD 可以给方法进行注解
* ElementType.PACKAGE 可以给一个包进行注解
* ElementType.PARAMETER 可以给一个方法内的参数进行注解
* ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举
*/
@Target({ElementType.TYPE})
@Constraint(validatedBy = FieldRepeatClass.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldRepeat {
/**
* 需要校验的字段
* @return
*/
String [] fields() default {};

String message() default "你所输入的内容已存在";

Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

2.注解接口实现类
import org.springframework.beans.factory.annotation.Autowired;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

/**
* @author xiaokang
* @description
* @date 2020/12/29 14:55
*/
public class FieldRepeatClass implements ConstraintValidator<FieldRepeat,Object> {

@Autowired
FieldRepeatUtils fieldRepeatUtils;

private String [] fileds;
private String message;

@Override
public void initialize(FieldRepeat validator) {
this.fileds = validator.fields();
this.message = validator.message();
}

@Override
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
return fieldRepeatUtils.fieldRepeat(fileds,message,o);
}
}

3.这里贴上FileRepeatUtils 类,主要处理业务逻辑
import com.baomidou.mybatisplus.activerecord.Model;
import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.orisdom.utils.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.*;

/**
* @author xiaokang
* @description
* @date 2020/12/29 15:07
*/
@Component
@Slf4j
public class FieldRepeatUtils {

/**
* 实体类中id字段
*/
private String idColumnName;

/**
* 实体类中id的值
*/
private Object idColumnValue;
/**
*
* @param fields 验证的字段数组
* @param message 如果不满足返回的消息
* @param o 实体类
* @return
*/
public boolean fieldRepeat(String [] fields,String message,Object o){
try {
// 没有校验的值返回true
if(fields != null && fields.length == 0){
return true;
}
checkUpdateOrSave(o);
checkRepeat(fields,o,message);
return true;
}catch (Exception e){
String msg = "验证字段是否重复报错";
log.error(msg,e);
throw new BusinessException(e.getMessage());
}
}

/**
* 通过传入的实体类中 @TableId 注解的值是否为空,来判断是更新还是保存
* 将值id值和id列名赋值
* id的值不为空 是更新 否则是插入
* @param o 被注解修饰过的实体类
* @return
*/
public void checkUpdateOrSave(Object o) throws Exception{
    Field[] fields = getAllFields(o.getClass());
        for (Field f:fields) {
// 设置私有属性可读
f.setAccessible(true);
if(f.isAnnotationPresent(TableId.class)){
TableId tableId = f.getAnnotation(TableId.class);
idColumnName = tableId.value();
idColumnValue = f.get(o);
}
}
}
/**
* 获取本类及其父类的属性的方法
* @param clazz 当前类对象
* @return 字段数组
*/
private static Field[] getAllFields(Class<?> clazz) {
List<Field> fieldList = new ArrayList<>();
while (clazz != null){
fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
clazz = clazz.getSuperclass();
}
Field[] fields = new Field[fieldList.size()];
return fieldList.toArray(fields);
}


/**
* 通过传入的字段值获取数据是否重复
* @param fields
* @param o
* @param message
* @return
*/
public void checkRepeat(String [] fields,Object o,String message){
Model model = (Model) o;
EntityWrapper entityWrapper = new EntityWrapper();
Map<String,Object> queryMap = getColumns(fields,o);
Iterator<Map.Entry<String, Object>> it = queryMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Object> entry = it.next();
entityWrapper.eq(entry.getKey(),entry.getValue());
}
if(idColumnValue != null){
//更新的话,那条件就要排除自身
entityWrapper.ne(idColumnName,idColumnValue);
}
List list = model.selectList(entityWrapper);
if(list != null && list.size()>0){
throw new BusinessException(message);
}
}

/**
* 多条件判断唯一性,将我们的属性和值组装在map中,方便后续拼接条件
* @param fields
* @param o
* @return
*/
public Map<String,Object> getColumns(String [] fields,Object o){
Field[] fieldList = getAllFields(o.getClass());
        Map<String,Object> map = new HashMap<>();
for (Field f : fieldList) {
// ② 设置对象中成员 属性private为可读
f.setAccessible(true);
// 判断字段是否包含在数组中,如果存在,则将它对应的列字段放入map中
if(ArrayUtils.contains(fields,f.getName())){
getMapData(map,f,o);
}
}
return map;
}

/**
* 得到查询条件
* @param map 列字段
* @param f 字段
* @param o 传入的对象
*/
private void getMapData( Map<String,Object> map,Field f,Object o){
try {
if(f.isAnnotationPresent(TableField.class)){
TableField tableField = f.getAnnotation(TableField.class);
Object val = f.get(o);
map.put(tableField.value(),val);
}
}catch (IllegalAccessException i){
throw new BusinessException("获取字段的值报错");
}
}
}
4.全局异常

import com.orisdom.dto.ApiResult;
import com.orisdom.utils.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;

/**
* 统一异常处理
*/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

private static final Logger LOG = LoggerFactory.getLogger(GlobalExceptionHandler.class);

@ExceptionHandler(value = BusinessException.class)
@ResponseBody
public ApiResult handleBusinessException(BusinessException be) {
if (be.getStatus() == -1) {
return ApiResult.expired(be.getMessage());
}
return ApiResult.fail(be.getMessage());
}

@ExceptionHandler(value = RuntimeException.class)
@ResponseBody
public ApiResult handleRuntimeException(RuntimeException e) {
LOG.error("系统异常", e);
return ApiResult.fail("系统异常,操作失败");
}

// 参数校验异常处理 ===========================================================================
// MethodArgumentNotValidException是springBoot中进行绑定参数校验时的异常,需要在springBoot中处理,其他需要处理ConstraintViolationException异常进行处理.

/**
* 方法参数校验
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public ApiResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.error("方法参数校验:{}", e.getMessage());
return ApiResult.fail(e.getBindingResult().getFieldError().getDefaultMessage());
}

/**
* ValidationException
*/
@ExceptionHandler(ValidationException.class)
@ResponseBody
public ApiResult handleValidationException(ValidationException e) {
log.error("ValidationException:", e);
return ApiResult.fail(e.getCause().getMessage());
}

/**
* ConstraintViolationException
*/
@ExceptionHandler(ConstraintViolationException.class)
@ResponseBody
public ApiResult handleConstraintViolationException(ConstraintViolationException e) {
log.error("ValidationException:" + e.getMessage(), e);
return ApiResult.fail(e.getMessage());
}

@ExceptionHandler(NoHandlerFoundException.class)
@ResponseBody
public ApiResult handlerNoFoundException(Exception e) {
return ApiResult.fail("路径不存在,请检查路径是否正确");
}

@ExceptionHandler(DuplicateKeyException.class)
@ResponseBody
public ApiResult handleDuplicateKeyException(DuplicateKeyException e) {
return ApiResult.fail("数据重复,请检查后提交");
}

}
demo运用示例 在实体类中加入注解,然后在controller中加入@Validated

 


 

 

 

 

 很多东西也是借鉴的,但是自己还是要动手写一遍,了解其中的原理,每天进步一点点,我们知识积累得就越多!相信有了这注解后会对我们验证重复性有了很大的代码简化!

加油!奥利给。。

 
posted on 2020-12-30 09:46  代码中透露着杀气  阅读(7913)  评论(49编辑  收藏  举报

如果你记忆力差,那可以多写来弥补