Hibernate’s “Not-Null Property References a Null or Transient Value” Error – Hibernate的 “非空属性引用空值或暂存值 “错误

最后修改: 2022年 4月 17日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we’ll learn about Hibernate’s PropertyValueException. In particular, we’ll consider the “not-null property references a null or transient value” error message.

在这篇文章中,我们将了解Hibernate的PropertyValueException。特别是,我们将考虑“not-null属性引用空值或瞬时值”的错误信息。

Hibernate will mainly throw PropertyValueException under two circumstances:

Hibernate主要会在两种情况下抛出PropertyValueException

  • when saving a null  value for a column marked with nullable = false
  • when saving an entity with an association referencing an unsaved instance

2. Hibernate’s Nullability Checking

2.Hibernate的Nullability检查

Firstly, let’s discuss Hibernate’s @Column(nullable = false) annotation. We can rely on Hibernate’s nullability checking if no other Bean Validation is present. 

首先,我们来讨论一下Hibernate的@Column(nullable = false)注释。我们可以依赖 如果没有其他Bean Validation,Hibernate的nullability检查。

Besides, we can enforce this validation by setting hibernate.check_nullability = true. In order to reproduce the following examples, we need to enable nullability checking.

此外,我们可以通过设置hibernate.check_nullability = true来强制执行这种验证。为了重现下面的例子,我们需要启用无效性检查。

3. Saving a null Value for a not-null Column

3.为一个非空列保存一个null

Now, let’s leverage Hibernate’s validation mechanism to reproduce the error for a simple use case. We’ll try to save an @Entity without setting its mandatory fields. For this example, we’ll use the simple Book class:

现在,让我们利用Hibernate的验证机制来重现一个简单用例的错误。我们将尝试保存一个@Entity,而不设置其强制字段。在这个例子中,我们将使用简单的Book类。

@Entity
public class Book {

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

    @Column(nullable = false)
    private String title;

    // getters and setters
}

The title column has the nullable flag set to false. We can now save a Book object without setting its title and assert that PropertyValueException is thrown:

title列的nullable标志被设置为 false。我们现在可以保存一个图书对象,而不设置其标题,并断言PropertyValueException被抛出。

@Test
public void whenSavingEntityWithNullMandatoryField_thenThrowPropertyValueException() {    
    Book book = new Book();

    assertThatThrownBy(() -> session.save(book))
      .isInstanceOf(PropertyValueException.class)
      .hasMessageContaining("not-null property references a null or transient value");
}

Therefore, we only need to set the mandatory fields before saving the entity in order to fix the issue: book.setTitle(“Clean Code”).

因此,我们只需要在保存实体之前设置强制字段,以解决这个问题。book.setTitle(“Clean Code”)

4. Saving an Association Referencing an Unsaved Instance

4.保存一个引用未保存实例的协会

In this section, we’ll explore a commonly encountered scenario with a more complex setup. For this example, we’ll use the Author and Article entities that share a bidirectional relationship:

在本节中,我们将探讨一个经常遇到的、设置更复杂的场景。在这个例子中,我们将使用共享双向关系的AuthorArticle实体。

@Entity
public class Author {

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

    private String name;

    @OneToMany
    @Cascade(CascadeType.ALL)
    private List<Article> articles;

    // constructor, getters and setters
}

The articles field has the @Cascade(CascadeType.ALL) annotation. As a result, when we’ll save an Author entity, the operation will propagate through all the Article objects:

文章字段有@Cascade(CascadeType.ALL)注释。因此,当我们保存一个作者实体时,该操作将通过所有的文章对象传播。

@Entity
public class Article {

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

    private String title;

    @ManyToOne(optional = false)
    private Author author;

    // constructor, getters and setters
}

Now, let’s try to save an Author and some Articles and see what happens:

现在,让我们试着保存一个作者和一些文章,看看会发生什么。

@Test
public void whenSavingBidirectionalEntityiesithCorrectParent_thenDoNotThrowException() {
    Author author = new Author("John Doe");
    author.setArticles(asList(new Article("Java tutorial"), new Article("What's new in JUnit5")));

    assertThatThrownBy(() -> session.save(author))
      .isInstanceOf(PropertyValueException.class)
      .hasMessageContaining("not-null property references a null or transient value");
}

When we’re working with bidirectional relationships, we can make the common mistake of forgetting to update the assignment from both sides. We can avoid this if we change the setter from the Author class to update all the child articles as well.

当我们在处理双向关系时,我们可能会犯一个常见的错误,即忘记从两边更新分配。如果我们改变Author类的setter来更新所有的子article,就可以避免这种情况。

In order to illustrate all use-cases presented in the article, we’ll create a different method for this. However, it’s a good practice to set these fields from the setter of the parent entity:

为了说明文章中介绍的所有用例,我们将为此创建一个不同的方法。然而,从父实体的setter中设置这些字段是一个好的做法。

public void addArticles(List<Article> articles) {
    this.articles = articles;
    articles.forEach(article -> article.setAuthor(this));
}

We can now use the new method for setting the assignment and expect no errors:

我们现在可以使用新的方法来设置赋值,并且预期不会出现错误。

@Test
public void whenSavingBidirectionalEntitesWithCorrectParent_thenDoNotThrowException() {
    Author author = new Author("John Doe");
    author.addArticles(asList(new Article("Java tutorial"), new Article("What's new in JUnit5")));

    session.save(author);
}

5. Conclusion

5.总结

In this article, we saw how Hibernate’s validation mechanism works. First, we discovered how to enable the nullability checking in our project. After that, we exemplified the main reasons causing PropertyValueException and learned how to fix them.

在这篇文章中,我们看到了Hibernate的验证机制是如何工作的。首先,我们发现如何在我们的项目中启用nullability检查。之后,我们举例说明了导致PropertyValueException的主要原因,并学习了如何修复它们。

As usual, the source code is available over on GitHub.

像往常一样,源代码可在GitHub上获得。