1. Обзор
В этой статье мы узнаем об исключении PropertyValueException в Hibernate.
В частности, мы рассмотрим сообщение об ошибке «свойство not-null ссылается на нулевое или переходное значение» .
Hibernate в основном выбрасывает PropertyValueException
в двух случаях:
- при сохранении
нулевого
значения для столбца, отмеченногоnullable = false
- при сохранении объекта с ассоциацией, ссылающейся на несохраненный экземпляр
2. Проверка Hibernate на нуль-значение
Во-первых, давайте обсудим аннотацию Hibernate @Column(nullable = false)
. Мы можем положиться на проверку допустимости значений NULL в Hibernate, если нет другой проверки Bean.
Кроме того, мы можем принудительно применить эту проверку, установив hibernate.check_nullability = true.
Чтобы воспроизвести следующие примеры, нам нужно включить проверку допустимости значений NULL.
3. Сохранение нулевого
значения для ненулевого столбца
Теперь давайте воспользуемся механизмом проверки Hibernate, чтобы воспроизвести ошибку для простого варианта использования. Мы попробуем сохранить @Entity
без установки обязательных полей. В этом примере мы будем использовать простой класс Book :
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false)
private String title;
// getters and setters
}
Столбец заголовка
имеет флаг, допускающий значение NULL , установленный в значение
false.
Теперь мы можем сохранить объект Book
, не устанавливая его заголовок, и утверждать, что было выброшено 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
}
Поле статей
имеет аннотацию @Cascade(CascadeType.ALL
). В результате, когда мы сохраним сущность Author
, операция распространится на все объекты 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
, чтобы он также обновлял все дочерние статьи
.
Чтобы проиллюстрировать все варианты использования, представленные в статье, мы создадим для этого другой метод. Однако рекомендуется устанавливать эти поля из установщика
родительского объекта:
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. Во-первых, мы узнали, как включить проверку допустимости значений NULL
в нашем проекте. После этого мы рассмотрели основные причины, вызывающие исключение PropertyValueException
, и научились их устранять.
Как обычно, исходный код доступен на GitHub .