混淆:@NotNull 与 @Column(nullable = false) 与 JPA 和 Hibernate

Posted

技术标签:

【中文标题】混淆:@NotNull 与 @Column(nullable = false) 与 JPA 和 Hibernate【英文标题】:Confusion: @NotNull vs. @Column(nullable = false) with JPA and Hibernate 【发布时间】:2011-11-18 08:44:38 【问题描述】:

    当它们出现在@Entity 的字段/getter 上时,它们之间有什么区别? (我通过 Hibernate 持久化实体)。

    它们各自属于什么框架和/或规范?

    @NotNull 位于javax.validation.constraints 内。在javax.validation.constraints.NotNull javadoc 中它说

    注解的元素不能为空

    但它并没有说明元素在数据库中的表示,那我为什么要在列中添加约束nullable=false

【问题讨论】:

【参考方案1】:

@NotNull 是一个JSR 303 Bean Validation 注释。它与数据库约束本身无关。然而,由于 Hibernate 是 JSR 303 的参考实现,它智能地获取这些约束并将它们转换为您的数据库约束,因此您以一个的价格获得两个。 @Column(nullable = false) 是 JPA 将列声明为非空的方式。 IE。前者用于验证,后者用于指示数据库模式详细信息。您只是从 Hibernate 获得了一些关于验证注释的额外(欢迎!)帮助。

【讨论】:

谢谢!因此,如果我希望我的 JPA 持久性不与 Hibernate 实现绑定(即更改为 EJB3),那么我必须同时使用两个注释(以禁止字段及其列中的 null)? 我不知道。没有规范说 JPA 提供者必须识别 JSR 303 注释,但这并不意味着其他提供者不能。我不能说有没有。 JPA 提供者不需要提供 JSR303 实现,但按照规范要求提供与任何第三方 JSR303 实现集成的能力。因此,尽管 Hibernate 确实提供了 JSR303,但您可以出于任何原因决定不使用他们的并与其他人一起使用,或者使用诸如 openJPA 之类的 JPA 实现并使用其他人提供 JSR303。另请注意,Hibernate 的 JPA 实现也是 EJB3。说是不正确的'如果我希望我的 JPA 持久性不与 Hibernate 实现绑定(即更改为 EJB3)' JPA 是 EJB3 规范的一部分。 @Shahzeb:问题不在于谁支持/提供 JSR 303 验证。这是关于哪些 ORM 能够识别 JSR 303 注释,如 @NotNull@Size@Min@Max 等,并将它们转换为数据库约束。 是的,但我的评论在 OP 在您不知道的后续评论中提出的内容中是有效的。【参考方案2】:

最新版本的休眠 JPA 提供程序默认将 bean 验证约束 (JSR 303) 像 @NotNull 应用于 DDL(感谢 hibernate.validator.apply_to_ddl property 默认为 true)。但不能保证其他 JPA 提供者会这样做,甚至有能力这样做。

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

您还应该使用像@Column(nullable = false) 这样的JPA 注释来为jpa 提供程序提供提示,以生成正确的DDL,以使用您想要的数据库约束创建表列。如果您可以或想要依赖像 Hibernate 这样的 JPA 提供程序,它默认将 bean 验证约束应用于 DDL,那么您可以省略它们。

【讨论】:

【参考方案3】:

JPA @Column 注释

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

模式生成工具使用它 Hibernate 在刷新持久性上下文期间使用它

架构生成工具

HBM2DDL 架构生成工具在生成CREATE TABLE 语句时将@Column(nullable = false) 实体属性转换为关联表列的NOT NULL 约束。

正如我在Hibernate User Guide 中解释的那样,最好使用Flyway 之类的工具,而不是依赖 HBM2DDL 机制来生成数据库架构。

持久化上下文刷新

在刷新 Persistence Context 时,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 验证 @NotNull 注解

@NotNull 注解由 Bean Validation 定义,就像 Hibernate ORM 是最流行的 JPA 实现一样,最流行的 Bean Validation 实现是 Hibernate Validator 框架。

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

【讨论】:

为什么说flyway比生成模式更好? 这是一个很好的观察。我用参考链接更新了答案。 感谢您的参考和很好的回答! 感谢您的出色回答。如果我不想使用模式生成工具或者@NotNull 就足够了,我应该两者兼有吗? @Basic(optional=false)@Column(nullable = false) 一样吗? 我从未在我从事的任何项目中使用过@NotNull。我不认为我用过@Basic(optional=false)。我不确定 Hibernate 是否使用那个。我仅在生成模式时使用@Column(nullable = false)。总而言之,使用 FlywayDB 定义您的数据库架构,并在每一层进行验证:Web、控制器、服务层。【参考方案4】:

有趣的是,所有消息来源都强调@Column(nullable=false) 仅用于 DDL 生成。

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

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

有关 hibernate.check_nullability 选项的更多信息可在此处获得:http://docs.jboss.org/hibernate/orm/5.0/userguide/html_single/Hibernate_User_Guide.html#configurations-mapping。

【讨论】:

以上是关于混淆:@NotNull 与 @Column(nullable = false) 与 JPA 和 Hibernate的主要内容,如果未能解决你的问题,请参考以下文章

SQL字段拼接与拆分

Oracle 增加修改删除字段与添加注释

SQL CTE 与视图

MySQL-约束

Oracle数据库定义语言(DDL)

SQL 一般优化