1. 程式人生 > 實用技巧 >自定義校驗,多個引數唯一性 springboot+mybatis-plus

自定義校驗,多個引數唯一性 springboot+mybatis-plus

每天進步一點點,幸福自然多一點!

前言:

相信大家在開發羨慕中,新增資料或者修改資料的時候某些唯一性欄位都需要進行重複驗證,一般驗證的情況我們思路大致如下

以驗證 code為例

1.如果是新增操作,我們需要將編碼進行查詢,如果查詢結果不為空,那說明有重複資料。

select * from tablename where code = xxx

2.如果是更新操作 ,我們不僅需要將編碼進行查詢,還要加個id不等於當前編輯資料的id,如果查詢結果不為空,說明有重複資料

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

其實這樣做來,我們重複編碼就不出現很多,很麻煩!

這個時候怎麼辦呢!

對,你應該也想到了,自定義註解校驗,通過在實體類中加入註解,來判斷需要對哪些欄位進行效驗

接下來我們談下實現:

1.建立FileRepeat註解

 註解中有兩個屬性,陣列型別的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 = FileRepeatClass.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface FileRepeat {
/**
* 需要校驗的欄位
* @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 FileRepeatClass implements ConstraintValidator<FileRepeat,Object> {

@Autowired
FileRepeatUtils fileRepeatUtils;

private String [] fileds;
private String message;

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

@Override
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
return fileRepeatUtils.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 FileRepeatUtils {

/**
* 實體類中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值和列名賦值
* true 是更新 false是插入
* @param o 被註解修飾過的實體類
* @return
*/
public void checkUpdateOrSave(Object o) throws Exception{
Field[] fields = o.getClass().getDeclaredFields();
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 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);
}
}

/**
* 多條件判斷唯一性
* @param fields
* @param o
* @return
*/
public Map<String,Object> getColumns(String [] fields,Object o){
Field[] fieldList = o.getClass().getDeclaredFields();
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

很多東西也是借鑑的,但是自己還是要動手寫一遍,瞭解其中的原理,每天進步一點點,我們知識積累得就越多