Java修改接口 校验一个或多个字段不可重复(自定义注解)

|---------------------|
| 🎈边走、边悟🎈迟早会好 |

根据业务需求,将修改校验服务,使其支持动态校验一个或多个字段的组合唯一性。以下是改进后的代码实现:

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@Service
public class UniqueValidatorService {

    @PersistenceContext
    private EntityManager entityManager;

    /**
     * 校验多个字段的组合在数据库中是否唯一
     * @param tableName 表名
     * @param uniqueFields 需校验唯一性的字段名和值的映射
     * @return 如果唯一返回true,否则返回false
     */
    @Transactional(readOnly = true)
    public boolean validateUnique(String tableName, Map<String, Object> uniqueFields) {
        if (!StringUtils.hasText(tableName) || uniqueFields == null || uniqueFields.isEmpty()) {
            throw new IllegalArgumentException("表名和校验字段不能为空");
        }

        // 构建动态查询语句
        StringBuilder sql = new StringBuilder("SELECT COUNT(*) FROM ").append(tableName);
        StringBuilder whereClause = new StringBuilder(" WHERE ");
        Map<String, Object> parameters = new HashMap<>();

        int index = 0;
        for (Map.Entry<String, Object> entry : uniqueFields.entrySet()) {
            String field = entry.getKey();
            Object value = entry.getValue();

            if (!StringUtils.hasText(field) || value == null) {
                throw new IllegalArgumentException("字段名和值不能为空");
            }

            if (index > 0) {
                whereClause.append(" AND ");
            }
            whereClause.append(field).append(" = :").append(field.replace(".", "_"));
            parameters.put(field.replace(".", "_"), value);
            index++;
        }

        sql.append(whereClause);

        try {
            Long count = entityManager.createNativeQuery(sql.toString(), Long.class)
                    .unwrap(org.hibernate.query.NativeQuery.class)
                    .setParameterMapping(parameters)
                    .getSingleResult();
            return count == 0;
        } catch (NoResultException e) {
            return true; // 没有结果表示唯一
        } catch (Exception e) {
            // 记录日志并处理异常
            throw new RuntimeException("唯一性校验失败", e);
        }
    }
}

使用示例

java 复制代码
@RestController
@RequestMapping("/api")
public class MyController {

    @Autowired
    private UniqueValidatorService validatorService;

    @PostMapping("/data")
    public ResponseEntity<?> createData(@RequestBody MyData data) {
        // 校验字段A和字段B的组合是否唯一
        Map<String, Object> uniqueFields = new HashMap<>();
        uniqueFields.put("field_a", data.getFieldA());
        uniqueFields.put("field_b", data.getFieldB());
        
        // 可根据需要添加更多字段
        // uniqueFields.put("field_c", data.getFieldC());

        boolean isUnique = validatorService.validateUnique("my_table", uniqueFields);

        if (!isUnique) {
            return ResponseEntity.badRequest().body("字段组合已存在");
        }

        // 保存数据的逻辑
        return ResponseEntity.ok().build();
    }
}

自定义注解增强版(可选)

为了让校验更加简洁优雅,可以创建支持多字段的自定义注解:

java 复制代码
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueFieldsValidator.class)
@Documented
public @interface UniqueFields {
    String message() default "字段组合不唯一";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    
    String tableName();
    String[] fields();
}
java 复制代码
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class UniqueFieldsValidator implements ConstraintValidator<UniqueFields, Object> {

    @Autowired
    private UniqueValidatorService validatorService;
    
    private String tableName;
    private String[] fields;

    @Override
    public void initialize(UniqueFields constraintAnnotation) {
        this.tableName = constraintAnnotation.tableName();
        this.fields = constraintAnnotation.fields();
    }

    @Override
    public boolean isValid(Object obj, ConstraintValidatorContext context) {
        if (obj == null) {
            return true;
        }

        Map<String, Object> uniqueFields = new HashMap<>();
        for (String fieldName : fields) {
            try {
                Field field = getField(obj.getClass(), fieldName);
                if (field != null) {
                    field.setAccessible(true);
                    uniqueFields.put(fieldName, field.get(obj));
                }
            } catch (Exception e) {
                throw new RuntimeException("获取字段值失败", e);
            }
        }

        return validatorService.validateUnique(tableName, uniqueFields);
    }

    private Field getField(Class<?> clazz, String fieldName) {
        try {
            return clazz.getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            Class<?> superClass = clazz.getSuperclass();
            if (superClass != null && !superClass.equals(Object.class)) {
                return getField(superClass, fieldName);
            }
            return null;
        }
    }
}

实体类使用注解

java 复制代码
@Entity
@Table(name = "my_table")
@UniqueFields(tableName = "my_table", fields = {"fieldA", "fieldB"})
public class MyData {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "field_a")
    private String fieldA;

    @Column(name = "field_b")
    private String fieldB;

    // getters and setters
}

这个改进后的实现可以灵活支持校验一个或多个字段的组合唯一性,通过 Map 参数动态指定需要校验的字段,同时提供了自定义注解的高级用法,使代码更加简洁和类型安全。

🌟感谢支持 听忆.-CSDN博客

|--------------------|
| 🎈众口难调🎈从心就好 |

相关推荐
wWYy.41 分钟前
Mysql:一行数据是怎么存储的?
android·数据库·mysql
维吉斯蔡7 小时前
【VS Code / Cursor】文件夹右键快捷打开与文件类型自动关联
开发语言·程序人生·学习方法
一米阳光86617 小时前
软考(中级)软件设计师核心笔记(3)数据库系统——概念、数据库设计
数据库·笔记·职场发展·软考·软件设计师
蓝斯4978 小时前
[原创]《C#高级GDI+实战:从零开发一个流程图》第章:增加贝塞尔曲线,上、下、左、右连接点
java·c#·流程图
前端工作日常8 小时前
我学习到的Java 的 Service 分层:itf 和 impl 到底是什么?
java·后端
奈斯先生Vector8 小时前
告别工具碎片化:基于 Nano Banana 全模态 AI 聚合架构搭建“文本-图像-视频”自动化协同生产线
运维·数据库·人工智能·架构·自动化·aigc·音视频
都叫我大帅哥9 小时前
Spring @Transactional 注解完全指南
java·spring
Java成神之路-9 小时前
G1 垃圾回收器 :SATB + TAMS核心机制深度解析
java·jvm
前端工作日常9 小时前
我学习到的JIT即时编译与机器码缓存失效
java·后端
带刺的坐椅9 小时前
Solon 的 10 种 HTTP 服务器:改一行依赖,换一个引擎
java·solon·jetty·undertow·mcp-server·htttp