Всегда использовать примитивные объектные обертки для JPA @Id вместо примитивного типа?
Я обнаружил проблему с использованием примитивного типа в качестве объекта @Id для JPA в сочетании с Spring Data JPA. У меня есть родительские / дочерние отношения с Cascade.ALL на родительской стороне, а у дочернего элемента есть PK, который в то же время также является FK родителя.
class Parent {
@Id
private long id;
@OneToOne(mappedBy = "parent", cascade = ALL)
private Child child;
}
class Child {
@Id
@OneToOne
private Parent parent;
}
Итак, когда я бегу:
...
Parent parent = new Parent();
Child child = new Child(parent);
parent.setChild(child);
em.persist(parent)
...
все отлично работает Но я использовал Spring Data JPA для сохранения сущности, поэтому вместо этого я запускаю:
parentRepository.save(parent); // instead of em.persist(parent);
и этот был неудачным со следующим исключением:
Caused by: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Parent
Проблема была в том, что Spring Data JPAsave() Метод проверяет, является ли объект новым, и если он является новым, тоem.persist() используется иначеem.merge() используется.
Интересная часть здесь, как Spring проверяет, является ли объект новым или нет:
getId(entity) == null;
И, конечно, это было ложно, потому что я использовал long как тип для @Id, и значение по умолчанию для long равно 0. Когда я изменил long на Long, все работает и с Spring Data JPA.
Поэтому рекомендуется всегда использовать объектные обертки для примитивных типов (таких как Long вместо long) вместо примитивных типов. Любой сторонний ресурс, описывающий это как рекомендуемую практику, был бы очень хорош.