【问题标题】:@unique constraint with database support in hibernate@unique 约束与休眠中的数据库支持
【发布时间】:2018-02-16 17:04:02
【问题描述】:

我有一个 spring 项目,希望在数据库中的字段上强制执行唯一性并将错误消息返回给 UI。

我已经阅读了this SO answer,这是有道理的,所以@Column(unique = true) 对表进行了约束,但没有强制执行。

所以问题变成了如何创建一个@Unique 注解来检查数据库并在 POST 处理程序上将错误消息返回到 BindingResult。

举个例子就好了。

更新

我尝试了以下方法来制作自定义验证器:

对象(注意我添加了@valid 以获取验证器消息以导航到 BindingResult)

Person.java

@Entity
public class Person {
public Person() {}

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// other stuff
@UniqueNid
private BigInteger nid;

EpisodePerson.java

@Entity
public class EpisodePerson {
public EpisodePerson(){};

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

@ManyToOne
@Valid
private Person person;

EpisodeViewModel (DTO)

public class EpisodeViewModel {

@Valid
private Episode episode = new Episode();
@Valid
private List<EpisodePerson> persons = new ArrayList<>();

UniqueNid.java

@Documented
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueNiaValidator.class)
public @interface UniqueNid {

String message() default "{Duplicate ID}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

UniqueNidValidator.java

public class UniqueNidValidator implements ConstraintValidator<UniqueNid, BigInteger> {
public UniqueNidValidator(){};

private PersonRepository personRepository;

@Autowired
public void setPersonRepository(PersonRepository personRepository) {this.personRepository = personRepository;}

public UniqueNidValidator(PersonRepository personRepository) {
    this.personRepository = personRepository;
}

@Override
public void initialize(UniqueNid constraint) {
}

@Override
public boolean isValid(BigInteger nid, ConstraintValidatorContext context) {
    return nid != null && personRepository.existsByNid(nid);
}
}

PersonRepository.java

...
    Boolean existsByNid(BigInteger nid);
...

Application.java

@SpringBootApplication
@EnableAutoConfiguration(exclude = { org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration.class })

public class Demo3Application extends SpringBootServletInitializer {

@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
    return application.sources(WebApplicationInitializer.class);
}

public static void main(String[] args) {
    SpringApplication.run(Demo3Application.class, args);
}

@Bean
public javax.validation.Validator localValidatorFactoryBean() {
    return new LocalValidatorFactoryBean();
}
}

当我提交一个人时,我得到:

堆栈跟踪(缩写)

java.lang.NullPointerException: null
at com.example.validators.UniqueNidValidator.isValid(UniqueNidValidator.java:31) ~[main/:na]

更新 2

我也试过这个配置

public class UniqueNidValidator implements ConstraintValidator<UniqueNid, BigInteger> {

    public UniqueNidValidator(){};

    private PersonRepository personRepository;

    public UniqueNidValidator(PersonRepository personRepository) {
        this.personRepository = personRepository;
    }

    @Override
    public void initialize(UniqueNid constraint) {
    }

    @Override
    public boolean isValid(BigInteger nid, ConstraintValidatorContext context) {
        System.out.println("About to check " +nid.toString());
        System.out.println("person repo " +personRepository.toString() );
        return personRepository.existsByNid(nid);
    }
}

给出:

java.lang.NullPointerException: null
at com.example.validators.UniqueNiaValidator.isValid(UniqueNiaValidator.java:29) ~[main/:na]

当我尝试将 repo 打印到控制台时。

【问题讨论】:

  • 第 31 行到底是什么代码?看起来personRepository 没有注入,因此现在是null。尝试找出它发生的原因。
  • 另外你为什么要创建LocalValidatorFactoryBean?为什么不让 Spring 为您引导它?
  • ln31: return nid != null && personRepository.existsByNid(nid);如果我在测试评估(正确)为真时单步执行代码。
  • 我想答案可能在这个:stackoverflow.com/questions/24955817/…
  • 在这种情况下,解决此问题的一种方法是拆分实体和 dto。

标签: spring-boot spring-data-jpa bean-validation hibernate-validator


【解决方案1】:

您需要create a custom validation 检查数据库。对于数据库检查,您显然可以使用可能已经存在的 Spring Data Repository,它是 exists() 方法。

自定义验证由标记要检查的字段的注释和实现实际检查的类组成。

一个小的挑战是该类需要一个默认构造函数并且并不真正支持注入依赖项。所以你需要的任何东西,你基本上都必须从一些静态引用中访问,包括例如存储库。因此,您可能有一个单独的 bean,它将存储库放入该静态引用中。

这种“捕获”存储库并使其在静态变量中可用的 bean 可能如下所示。

@Component
public class RepositoryCatcher{
    public static MyRepository;
    public RepositoryCatcher(MyRepository r){
        repository = r;
    }
}

【讨论】:

  • 谢谢 - 我已经添加了代码 - 你能更具体一点吗?谢谢
【解决方案2】:

从您提到的异常看来,唯一可能的 NullPointerException 是personRepository 被错误地注入验证器。

请尝试以下解决方案:

  1. Demo3Application 中删除以下 bean,并让 Spring Boot 创建默认的。

    @Bean
    public javax.validation.Validator localValidatorFactoryBean() {
        return new LocalValidatorFactoryBean();
    }
    
  2. 从验证器中删除存储库的设置器,但将依赖项留在构造函数中,就像现在一样。

    @Autowired
    public void setPersonRepository(PersonRepository personRepository {
        this.personRepository = personRepository;
    }
    

Jens 在他的回答中提到,自定义验证器需要默认构造函数并不完全正确。即使验证器未标记为托管组件,Spring 也会基于构造函数注入依赖项。 @Autowired 注解也是多余的。

此外,您可能在条件上犯了错误。您应该检查一个人是否不存在(注意第二部分中的 ! 标记)。

return nid != null && !personRepository.existsByNid(nid);

我鼓励您查看a blog post which addresses your issue。 GitHub 存储库中提供了示例代码。您可以运行、测试它,然后与您的解决方案进行比较。

【讨论】:

  • 我正在使用 DTO,并且在课堂上的 person.nid 上有 UniqueNid,但在 person 的 DTO 中也有 @valid。这也是个问题吗?
  • 你应该在课堂上只有一个你真正想要验证的。
【解决方案3】:

这是有效的验证器,并在 BindingResult 中出错:

UniqueNidValidator.java

public class UniqueNiaValidator implements ConstraintValidator<UniqueNid, BigInteger> {

public UniqueNiaValidator(){};

@Autowired
private PersonRepository personRepository;

public UniqueNiaValidator(PersonRepository personRepository) {
    this.personRepository = personRepository;
}

@Override
public void initialize(UniqueNid constraint) {
}

@Override
public boolean isValid(BigInteger nid, ConstraintValidatorContext context) {
    return !personRepository.existsByNid(nid);
}
}

注意 !personRepository.existByNid(nid);

另外,回购第二次为空白的原因是因为它被调用了两次,正如here所描述的那样

但无论如何检查 Beans 上的 RDBMS 约束违规可能不是一个好主意。

【讨论】:

    猜你喜欢
    • 2012-06-01
    • 2015-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多