1.概要

この記事では、Hibernateの PropertyValueException。 特に、 「not-nullプロパティはnullまたは一時的な値を参照します」 エラーメッセージ。

Hibernateは主に次の2つの状況でPropertyValueExceptionをスローします。

  • nullable =falseでマークされた列のnull値を保存する場合
  • 保存されていないインスタンスを参照する関連付けを持つエンティティを保存する場合

2. HibernateのNullabilityチェック

まず、Hibernateの @Column(nullable = false)アノテーションについて説明します。 頼りにできる他のBean検証が存在しない場合のHibernateのnull可能性チェック。 

さらに、設定することでこの検証を実施できます hibernate.check_nullability=true。 次の例を再現するには、null可能性チェックを有効にする必要があります。

3. null以外の列のnull値の保存

それでは、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 題名列には null許容フラグをに設定間違い。 これで保存できますタイトルを設定せずにオブジェクトを作成し、 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");
}

したがって、問題を修正するには、エンティティを保存する前に必須フィールドを設定するだけで済みます: book.setTitle( “Clean Code”)

4. 保存されていないインスタンスを参照するアソシエーションの保存

このセクションでは、より複雑な設定で一般的に発生するシナリオについて説明します。 この例では、双方向の関係を共有するAuthorエンティティとArticleエンティティを使用します。

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

articlesフィールドには@Cascade(CascadeType。 ALL)アノテーションがあります。 その結果、作成者エンティティを保存すると、操作はすべてのArticleオブジェクトに伝播されます。

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

それでは、作成者といくつかの記事を保存して、何が起こるか見てみましょう。

@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");
}

双方向の関係で作業している場合、両側から割り当てを更新するのを忘れるというよくある間違いを犯す可能性があります。 Authorクラスからsetterを変更して、すべての子記事も更新すると、これを回避できます。

この記事で紹介されているすべてのユースケースを説明するために、このための別の方法を作成します。 ただし、親エンティティのsetterからこれらのフィールドを設定することをお勧めします。

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

これで、割り当てを設定するための新しい方法を使用でき、エラーは発生しません。

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

この記事では、Hibernateの検証メカニズムがどのように機能するかを見ました。 最初に、プロジェクトでnullabilityチェックを有効にする方法を発見しました。 その後、 PropertyValueException の主な原因を例示し、それらを修正する方法を学びました。

いつものように、ソースコードはGitHubから入手できます。