Java 自定义注解校验字段唯一性
业务场景
在项目中,某些情景下我们需要验证编码是否重复,账号是否重复,身份证号是否重复等...
那么有没有办法可以解决这类似的重复代码量呢?
我们可以通过自定义注解校验的方式去实现,在实体类上面加上自定义的注解即可
@FieldRepeat(fields = {"account"}, message = "账号已存在!")
实现步骤
1.引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.3.11.RELEASE</version>
</dependency>
2.新增自定义注解
// 注意:实体类必须继承Model,且需要标明表名@TableName("t_test"),校验字段需要加上注解 @TableField("name")
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = FieldRepeatValidator.class)
public @interface FieldRepeat {
/**
* 需要检验的字段
* @return
*/
String[] field() default {};
String message() default "您输入的内容已存在";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
3.新增@FieldRepeat注解接口实现类
public class FieldRepeatValidator implements ConstraintValidator<FieldRepeat, Object> {
@Autowired
private FieldRepeatUtils fieldRepeatUtils;
private String[] fields;
private String message;
@Override
public void initialize(FieldRepeat fieldRepeat) {
this.fields = fieldRepeat.fields();
this.message = fieldRepeat.message();
}
@Override
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
return fieldRepeatUtils.fieldRepeat(fields, o, message);
}
}
4.新增字段内容重复判断处理工具类
@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) throws ValidationException,IllegalAccessException {
try {
// 没有校验的值返回true
if(fields != null && fields.length == 0){
return true;
}
checkUpdateOrSave(o);
checkRepeat(fields,o,message);
return true;
}catch (ValidationException ed){
throw new ValidationException(message);
}catch (IllegalAccessException e){
throw new IllegalAccessException(e.getMessage());
}
}
/**
* 通过传入的实体类中 @TableId 注解的值是否为空,来判断是更新还是保存
* 将值id值和id列名赋值
* id的值不为空 是更新 否则是插入
* @param o 被注解修饰过的实体类
* @return
*/
public void checkUpdateOrSave(Object o) throws IllegalAccessException{
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) throws ValidationException, IllegalAccessException {
Model model = (Model) o;
//Mybatis-plus 3.0以下用EntityWrapper
QueryWrapper<Object> qw = new QueryWrapper<>();
Map<String,Object> queryMap = getColumns(fields,o);
for (Map.Entry<String, Object> entry : queryMap.entrySet()) {
qw.eq(entry.getKey(), entry.getValue());
}
if(idColumnValue != null){
//更新的话,那条件就要排除自身
qw.ne(idColumnName,idColumnValue);
}
List list = model.selectList(qw);
if(list != null && list.size()>0){
throw new ValidationException(message);
}
}
/**
* 多条件判断唯一性,将我们的属性和值组装在map中,方便后续拼接条件
* @param fields
* @param o
* @return
*/
public Map<String,Object> getColumns(String [] fields,Object o) throws IllegalAccessException {
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) throws IllegalAccessException {
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 IllegalAccessException("获取字段的值错误");
}
}
}
测试类
@Data
@TableName("t_test")
@FieldRepeat(fields = {"name"},message = "名称不能重复,请重新输入",groups = {Base.Save.class,Base.Update.class})
public class Test extends Model<Test>{
@TableId(value = "id", type = IdType.AUTO)
@NotNull(message = "id不能为空",groups = {Base.Update.class,Base.Delete.class,Base.Detail.class})
private Long id;
@TableField("name")
@NotNull(message = "名称不能为空",groups = {Base.Update.class,Base.Save.class})
private String name;
}
Java 自定义注解校验字段唯一性的更多相关文章
- Java自定义注解源码+原理解释(使用Java自定义注解校验bean传入参数合法性)
Java自定义注解源码+原理解释(使用Java自定义注解校验bean传入参数合法性) 前言:由于前段时间忙于写接口,在接口中需要做很多的参数校验,本着简洁.高效的原则,便写了这个小工具供自己使用(内容 ...
- Java 自定义注解 校验指定字段对应数据库内容重复
一.前言 在项目中,某些情景下我们需要验证编码是否重复,账号是否重复,身份证号是否重复等... 而像验证这类代码如下: 那么有没有办法可以解决这类似的重复代码量呢? 我们可以通过自定义注解校验的方式去 ...
- java自定义注解实现前后台参数校验
2016.07.26 qq:992591601,欢迎交流 首先介绍些基本概念: Annotations(also known as metadata)provide a formalized way ...
- java自定义注解类
一.前言 今天阅读帆哥代码的时候,看到了之前没有见过的新东西, 比如java自定义注解类,如何获取注解,如何反射内部类,this$0是什么意思? 于是乎,学习并整理了一下. 二.代码示例 import ...
- java自定义注解注解方法、类、属性等等【转】
http://anole1982.iteye.com/blog/1450421 http://www.open-open.com/doc/view/51fe76de67214563b20b385320 ...
- JAVA自定义注解 ------ Annotation
日常开发工作中,合理的使用注解,可以简化代码编写以及使代码结构更加简单,下面记录下,JAVA自定义注解的开发过程. 定义注解声明类. 编写注解处理器(主要起作用部分). 使用注解. 相关知识点介绍, ...
- Java自定义注解和运行时靠反射获取注解
转载:http://blog.csdn.net/bao19901210/article/details/17201173/ java自定义注解 Java注解是附加在代码中的一些元信息,用于一些工具在编 ...
- java自定义注解知识实例及SSH框架下,拦截器中无法获得java注解属性值的问题
一.java自定义注解相关知识 注解这东西是java语言本身就带有的功能特点,于struts,hibernate,spring这三个框架无关.使用得当特别方便.基于注解的xml文件配置方式也受到人们的 ...
- Java自定义注解的实现
Java自定义注解的实现,总共三步(eg.@RandomlyThrowsException): 1.首先编写一个自定义注解@RandomlyThrowsException package com.gi ...
- Java自定义注解开发
一.背景 最近在自己搞一个项目时,遇到可需要开发自定义注解的需求,对于没有怎么关注这些java新特性的来说,比较尴尬,索性就拿出一些时间,来进行研究下自定义注解开发的步骤以及使用方式.今天在这里记下, ...
随机推荐
- 06. C语言指针
[指针] C语言使用数据名调用数据,数据名相当于C语言的直接寻址,直接寻址只能调用固定数据,而指针是间接寻址,指针存储了另一个数据的地址,使用指针调用数据时首先取指针存储的内存地址,之后使用此地址调用 ...
- Sublime Text 3 初试牛刀
每次我在其他视频网站上看学习视频的时候,看着老师用的编辑器高大上档次,而我一般用Notepad,和Dreamweaver去编辑网页,需要每一行代码,打进去,效率低.最近看到sublime编辑器,在网上 ...
- 超级详细的PostgreSQL创建数据库操作并附带图文
首发微信公众号:SQL数据库运维 原文链接:https://mp.weixin.qq.com/s?__biz=MzI1NTQyNzg3MQ==&mid=2247485195&idx=1 ...
- vue3的reactive对象赋值后失去响应式的问题
vue3种对象类型的响应式用reactive实现. 但是reactive对象在赋值后,因为变量代理函数变了,就失去了响应式功能了.示例如下: <template> <div> ...
- 通过ref返回解决C# struct结构体链式调用的问题
通常结构体不能进行链式调用,因为返回值是一个新的值,需要赋回原值.但现在通过ref关键字配合扩展方法,也能进行链式调用了. 结构体: public struct Foo { public int a; ...
- C 语言编程 — 运算符
目录 文章目录 目录 前文列表 运算符 算数运算符 自增.自减运算符 比较运算符 逻辑运算符 位运算符 赋值运算符 逗号运算符 sizeof 运算符 杂项运算符 运算符的优先级 前文列表 <程序 ...
- PageOffice 在线打开 word 文件实现痕迹保留、键盘批注、手写批注
一.痕迹保留 Word中的痕迹一般指的是审阅文档的用户对文档所做的修改(插入和删除)操作.在PageOffice的强制留痕模式下,用户对文档所做的任何修改都会以痕迹的形式保留下来,不同用户对文档做的修 ...
- TypeScript入门介绍
目录 TypeScript入门介绍 什么是 TypeScript? 发展历史 优缺点 应用场景 开发工具 环境依赖 编程IDE vs调试 构建工具/工程化 步骤汇总 第一个项目 TypeScript入 ...
- win10激活方法
slmgr /ipk W269N-WFGWX-YVC9B-4J6C9-T83GX slmgr /skms zh.us.to slmgr /ato
- 2024盘古石取证比赛(APK)
题目列表 使用软件: Notepad++,火眼证据分析软件,雷电分析app,DB browser for SQLCipher 1. 分析伏季雅的手机检材,手机中诈骗APP的包名是:[答案格式:abc. ...