混淆:JPA和Hibernate中的@NotNull vs. @Column(nullable = false)有何区别?

265
1. 当它们出现在一个@Entity的字段/获取器中时,它们有什么区别?(我通过Hibernate持久化实体)。 2. 它们各属于哪个框架和/或规范? 3. @NotNull位于javax.validation.constraints中。在javax.validation.constraints.NotNull javadoc中,它说“注释元素不能为null”,但它不讨论元素在数据库中的表示方式,那么为什么我要将约束nullable=false添加到列中呢?
5个回答

350

@NotNullJSR 303 Bean Validation的注释。它本身与数据库约束无关。然而,由于Hibernate是JSR 303的参考实现,它会智能地捕捉这些约束并将其转换为数据库约束,因此您只需付出一份代价即可获得两个效果。@Column(nullable = false)是JPA声明列不为空的方式。也就是说,前者用于验证,后者用于指示数据库模式细节。您只是从Hibernate中获得了一些额外(且受欢迎的)有关验证注释的帮助。


2
谢谢!所以,如果我希望我的JPA持久性不与Hibernate实现绑定(即更改为EJB3),那么我必须同时使用注释(在字段及其列中禁止null)吗? - rapt
3
我不知道。没有规范说JPA提供者必须识别JSR 303注解,但这并不意味着其他提供者不会。我不能确定是否有任何提供者这样做或者不这样做。 - Ryan Stewart
7
JPA提供者不必提供JSR303实现,但根据规范需要提供与任何第三方JSR303实现集成的能力。因此,虽然Hibernate提供了JSR303,但由于某种原因,您可以决定不使用他们的实现,转而选择其他提供商或使用像openJPA这样的JPA实现,并使用其他提供商提供JSR303。另外请注意,Hibernate的JPA实现也是EJB3,因此说“如果我希望我的JPA持久性不与Hibernate实现绑定(即切换到EJB3)”是不正确的。JPA是EJB3规范的一部分。 - Shahzeb
5
@Shahzeb:这个问题不是关于谁支持/提供JSR 303验证,而是关于哪些ORM识别JSR 303注释,比如@NotNull,@Size,@Min,@Max等,并将其转化为数据库约束。 - Ryan Stewart
1
是的,但我的评论是针对OP在后续评论中提出的问题的背景而言的,而您不知道这一点。 - Shahzeb
显示剩余9条评论

20

最新版本的Hibernate JPA提供程序默认将bean验证约束(JSR 303),如@NotNull应用于DDL(感谢hibernate.validator.apply_to_ddl属性默认为true)。但是,没有保证其他JPA提供程序这样做,甚至有能力这样做。

您应该使用bean验证注释,如@NotNull确保在验证JVM中的Java bean时,bean属性设置为非空值(这与数据库约束无关,但在大多数情况下应对应于它们)。

此外,您还应该使用JPA注释,如@Column(nullable = false),以向jpa提供程序提供提示,生成具有所需数据库约束的表列的正确DDL。如果您可以或者愿意依赖像Hibernate这样将bean验证约束默认应用于DDL的JPA提供程序,则可以省略它们。


19

JPA的@Column注解

@Column注解的nullable属性有两个作用:

  • 它被模式生成工具使用
  • 在刷新持久化上下文时Hibernate也会使用它

模式生成工具

HBM2DDL模式生成工具将@Column(nullable = false)实体属性转换为相应表列的NOT NULL约束,当生成CREATE TABLE语句时。

正如我在Hibernate用户指南中所解释的那样,最好使用像Flyway这样的工具来生成数据库架构,而不是依赖HBM2DDL机制。

持久化上下文刷新

在刷新持久化上下文时,Hibernate ORM也会使用@Column(nullable = false)实体属性:

new Nullability( session ).checkNullability( values, persister, true );
如果验证失败,Hibernate会抛出一个PropertyValueException,从而防止INSERT或UPDATE语句被不必要地执行:
if ( !nullability[i] && value == null ) {
    //check basic level one nullablilty
    throw new PropertyValueException(
            "not-null property references a null or transient value",
            persister.getEntityName(),
            persister.getPropertyNames()[i]
        );    
}

Bean Validation的@NotNull注释

@NotNull注释由Bean Validation定义,就像Hibernate ORM是最受欢迎的JPA实现一样,最受欢迎的Bean Validation实现是Hibernate Validator框架。

当与Hibernate ORM一起使用Hibernate Validator时,在验证实体时,Hibernate Validator将抛出一个ConstraintViolation 异常。


你为什么认为Flyway比生成模式更好? - Andronicus
1
这是一个很好的观察。我已经更新了答案并添加了参考链接。 - Vlad Mihalcea
感谢您提供的参考和出色的答案! - Andronicus
感谢您的出色回答。如果我不想使用模式生成工具,我应该同时拥有两者吗?@NotNull就足够了吗?@Basic(optional=false)@Column(nullable = false)是一样的吗? - Arash
我从未在我所工作的任何项目中使用过@NotNull。我不认为我使用过@Basic(optional=false)。我不确定Hibernate是否使用它。我只在生成模式时使用了@Column(nullable = false)。总之,使用FlywayDB定义您的数据库模式,并在每个层面进行验证:Web、控制器、服务层。 - Vlad Mihalcea

13

值得注意的是,所有来源都强调 @Column(nullable=false) 仅用于DDL生成。

然而,即使没有 @NotNull 注释,并且 hibernate.check_nullability 选项设置为 true,Hibernate也会对要持久化的实体进行验证。

如果 nullable=false 属性没有值,即使数据库层面没有实现这样的限制,它将抛出 PropertyValueException,指出“非空属性引用了null或瞬态值”。

有关 hibernate.check_nullability 选项的更多信息,请参见:http://docs.jboss.org/hibernate/orm/5.0/userguide/html_single/Hibernate_User_Guide.html#configurations-mapping


0
@NotNull @Column(nullable=false)
通常用于检查Java对象字段的非空性 通常用于DDL生成(⇒您可以在Java对象字段中创建一个空字段,但无法将其持久化到数据库列中)
是Bean验证规范的一部分,如hibernate-validator 是JPA规范的一部分
Hibernate足够聪明,它理解如果在Java对象字段上使用@NotNull来阻止空字段,这意味着数据库中对应的列也不应该有空值。因此,在使用Hibernate时,它提供了字段级和数据库级的非空性。 在使用Hibernate时,除了数据库列级的非空性验证外,还提供了Java对象字段级的验证,前提是spring.jpa.properties.hibernate.check_nullability=true
建议使用@NotNull,因为数据库脚本通常不是由Hibernate创建的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接