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 小时前
C 语言问题
c语言·开发语言
宸津-代码粉碎机1 小时前
LLM 模型部署难题的技术突破:从轻量化到分布式推理的全栈解决方案
java·大数据·人工智能·分布式·python
都叫我大帅哥1 小时前
TOGAF实战解码:六大行业案例解析与成功启示
java
都叫我大帅哥1 小时前
RabbitMQ消息确认机制:从外卖小哥到数据安全的奇幻漂流
java·rabbitmq
周航宇JoeZhou4 小时前
JP3-3-MyClub后台后端(二)
java·mysql·vue·ssm·springboot·项目·myclub
羊锦磊4 小时前
[ java 网络 ] TPC与UDP协议
java·网络·网络协议
找不到、了4 小时前
Java设计模式之<建造者模式>
java·设计模式·建造者模式
新手小新5 小时前
C++游戏开发(2)
开发语言·前端·c++
Code blocks5 小时前
关于“LoggerFactory is not a Logback LoggerContext but Logback is on ......“的解决方案
java·spring boot·后端
你的电影很有趣5 小时前
lesson30:Python迭代三剑客:可迭代对象、迭代器与生成器深度解析
开发语言·python