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新特性的来说,比较尴尬,索性就拿出一些时间,来进行研究下自定义注解开发的步骤以及使用方式.今天在这里记下, ...
随机推荐
- ansible(15)--ansible的mount模块
1. mount模块 功能:管理被控端设备挂载: 主要参数如下: 参数 说明 src 本地或远程设备的路径 path 设备挂载至本地的路径 fstype 挂载的文件系统类型,xfs.nfs... op ...
- pageoffice6 实现提取数据区域为子文件(Word拆分)
在实际的开发过程中,有时会遇到希望提取Word文档中部分内容保存为子文件的需求,PageOffice支持提取Word文档数据区域中的内容为一个Word文件流,在服务器端创建PageOffice的Wor ...
- Python:global、local与nonlocal变量
1 local和global变量 先来看一个最简单的Python程序例子: import numpy as np n = 2 def func(a): b = 1 return a + b print ...
- 关于《Java并发编程之线程池十八问》的补充内容
一.写在开头 在上一篇文章我们写<Java并发编程之线程池十八问>的时候,鉴于当时的篇幅已经过长,很多内容就没有扩展了,在这篇文章里对一些关键知识点进行对比补充. 二.Runnable v ...
- Swift 与 Objc Exception
一.背景 Swift具备完善的Error handle机制,对于纯Swift下面的Error,在编码的时候能够正确处理. 在使用try? 处理抛出Error的方法的时候,会忽略Error,直接返回ni ...
- 微信iOS消息拦截插件教程-第一个tweak编译过程
-安装好theos框架之后,下面用一个简单的例子进行说明编译过程 -新建一个代码目录 -进入terminal,cd到刚才的目录中 1.进入目录 2.执行/opt/theos/bin/nic.pl 3. ...
- (JAVA)设计模式-适配器模式
模式的定义和特点: 适配器模式(Adapter)是一种将一个类的接口转换成客户希望的另外一个接口的设计模式,可以提高代码的复用性和灵活性. 结构与实现: 定义一个适配器类来实现业务接口,再继承现有组件 ...
- 大数据平台搭建手册——hadoop
从0开始 超详细搭建hadoop平台手册 创建三台使用centos7操作系统的虚拟机 基础环境配置 ps:不建议使用DHCP,因为ip地址会变动 配置ip 1.master [root@master ...
- CF1900D - Small GCD 题解
1900D - Small GCD 给定序列 \(A\),定义 \(f(a, b, c)\) 为 \(a, b, c\) 中最小的次小的数的 \(\gcd\),求: \[\sum_{i = 1}^n ...
- kettle从入门到精通 第六十五课 ETL之kettle 执行动态SQL语句,轻松实现全量&增量数据同步
本次课程的逻辑是同步t1表数据到t2表,t1和t2表的表机构相同,都有id,name,createtime三个字段. CREATE TABLE `t1` ( `id` bigint NOT NULL ...