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博客

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

相关推荐
夏婵语冰1 小时前
实用R语言机器学习指南:从数据预处理到模型实战(附配套学习资源)
开发语言·学习·r语言
好望角雾眠1 小时前
第三阶段数据库-7:sql中函数,运算符,常用关键字
数据库·笔记·sql·学习·sqlserver·c#
cyforkk2 小时前
ArrayList vs LinkedList:底层原理与实战选择指南
java
孟婆来包棒棒糖~4 小时前
泛型与反射
java·反射·javase·泛型
牛角上的男孩4 小时前
apt update Ign and 404 Not Found
开发语言·数据库
A尘埃4 小时前
Spring Event 企业级应用
java·spring·event
yzzzzzzzzzzzzzzzzz5 小时前
JavaScript 操作 DOM
开发语言·javascript·ecmascript
海绵宝宝汉堡包6 小时前
c# 项目 文件夹
开发语言·c#
YuTaoShao6 小时前
【LeetCode 热题 100】139. 单词拆分——(解法一)记忆化搜索
java·算法·leetcode·职场和发展
Best_Liu~6 小时前
策略模式 vs 适配器模式
java·spring boot·适配器模式·策略模式