Persistence top

Get started with Spring Data JPA through the reference Learn Spring Data JPA course:

>> CHECK OUT THE COURSE

1. Overview

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 will mainly throw PropertyValueException under two circumstances:

  • 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

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. 

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.

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

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:

@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:

@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”).

4. Saving an Association Referencing an Unsaved Instance

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:

@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:

@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.

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:

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

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.

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

Persistence bottom
Get started with Spring Data JPA through the reference Learn Spring Data JPA course: >> CHECK OUT THE COURSE
Persistence footer banner
Comments are closed on this article!