当前位置: 首页 > 知识库问答 >
问题:

@hibernate中数据库支持的唯一约束

邓子濯
2023-03-14

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

我已经阅读了这个SO答案,它是有意义的,所以@Col的(唯一=真)使表上的约束,但不强制它。

因此,问题变成了如何创建一个@Unique注释,该注释与数据库进行检查,并在POST处理程序上向BindingResult返回错误消息

一个例子会很棒。

更新

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

对象(请注意,我添加了@有效以获取验证器消息以导航到BindingResault)

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 {};
}

唯一NidValidator.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);
...

应用.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]

当我尝试将存储库打印到控制台时。

共有3个答案

阎德宇
2023-03-14

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

唯一NidValidator.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);
}
}

请注意!person repository . exist bynid(NID);

此外,回购第二次是空白的原因是因为它被调用了两次,如这里所述

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

滑骞尧
2023-03-14

从您提到的异常来看,似乎唯一可能的NullPointerException是当< code>personRepository被错误地注入到验证器时。

请尝试以下解决方案:

>

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

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

    从验证器中删除存储库的setter,但将依赖项保留在构造函数中。

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

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

    另外,你很可能把条件搞错了。你应该检查一个人是否不存在(注意!第二部分标注)。

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

    我鼓励您查看解决您的问题的博客文章。GitHub存储库中提供了示例代码。您可以运行、测试它,然后与您的解决方案进行比较。

  • 欧阳俊晖
    2023-03-14

    需要创建用于检查数据库的自定义验证。对于数据库检查,您显然可以使用可能已经存在的 Spring 数据存储库及其 exists() 方法。

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

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

    这样一个bean“捕获”了一个存储库,并使它在一个静态变量中可用,可能如下所示。

    @Component
    public class RepositoryCatcher{
        public static MyRepository;
        public RepositoryCatcher(MyRepository r){
            repository = r;
        }
    }
    
     类似资料:
    • 我遇到一个情况,需要根据另一个列值对一个列[属性]强制执行唯一约束。 例如,我有一个像table(ID,EID,Name,ISDeleted)这样的表 ISDeleted只能有一个值null或'y'(active或deleted),并且我想在EID上创建一个唯一的约束,仅当ISDeleted=null时才创建ISDeleted,因为我不关心是否有多个具有相同ID的已删除记录。请注意,EID可以为空

    • 我有一个用户创建屏幕,它记录了各种用户详细信息以及名字和手机号码。我有一个对应的用户表,其中名字和手机号码构成一个复合唯一键。此表中还定义了其他完整性约束。 当在创建用户屏幕上输入违反此约束的用户数据时,需要向用户显示“用户友好”错误消息。 当这种违反发生时,我从MySQL数据库中得到的异常是: 有两个选项可以显示有意义的消息(例如:“错误:给定手机号码的用户名已存在,请更改其中一个”)。 选项1

    • 问题内容: 我试图在表中插入值。但是只插入一个值。尝试插入新值时,日志猫出现错误。 日志猫显示: 插入行时,这两行显示错误。 EventTableHelper 如何解决呢? 问题答案: 您的代码可能违反了字段上主键的唯一性约束。 两种可能的解决方案是: 确保您为每个对象返回唯一值。现在,我看不到您将任何标识符传递给其构造函数,并且可能所有事件都以相同的值插入。 如果您不希望自己生成ID,则可以将设

    • Django试图尽可能多的支持所有数据库后端的特性。然而,并不是所有数据库都一样,所以我们必须在支持哪些特性和做出哪些安全的假定上做出设计决策。 本文描述了一些Django使用数据库的有关特性。当然,它并不想成为各服务器指定的文档或者参考手册的替代品。 综合说明 持续连接特性 持续连接的特性避免了每一次重新建立与数据库的连接的请求中所增加的压力。这些连接通过 CONN_MAX_AGE 参数(控制一

    • 问题内容: 我将Elasticsearch用作文档数据库,并且我创建的每个记录都有一个GUID ID,系统将其用作记录ID。商界人士希望提供一种功能,使用户可以基于日期和迄今为止在这一天/每月创建的记录数来拥有自己的自动文件名约定。 我需要防止重复的用户文件名。有没有一种方法可以将索引字段设置为唯一?像sql唯一约束一样? 问题答案: 您需要使用应该唯一的字段作为文档的ID。默认情况下,具有现有I

    • 问题内容: 我必须获取所有唯一键约束和特定数据库索引的列表。我正在做这样的事情: 只是想确认这是否是正确的方法,或者是否有更好的方法来做同样的事情? 问题答案: 由于独特的约束是在幕后作为索引实现的,因此您可以直接从sys.indexes获取所有这些信息: 对所有数据库重复(大概没有针对特定表的过滤器):