Я новичок в Spring/JPA/Hibernate, и, хотя это звучит просто, на самом деле это не так. Мне не помешала бы помощь.
У меня есть родительский объект, который содержит список дочерних объектов. Я буду использовать их, чтобы упростить обсуждение:
@Entity
public class Parent {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@OneToMany(fetch=FetchType.EAGER, cascade = CascadeType.ALL, mappedBy="parent")
private List<Child> children= new ArrayList<Child>();
etc...
}
@Entity
public class Child {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@ManyToOne
private Parent parent;
etc...
}
@Repository
public interface ParentRepository extends JpaRepository<Parent, Long> {};
Раунд 1, я создаю нового родителя и нового дочернего элемента, добавляю дочерний элемент в список родителя и устанавливаю родителя для дочернего элемента. Когда я сохраняю родителя, ребенок также сохраняется.
@Transactional(propagation=Propagation.REQUIRES_NEW)
void create() {
Parent parent = new Parent();
Child child = new Child();
parent.add(child);
child.setParent(parent);
parent = repository.save(parent);
}
Теперь, раунд 2, я добавляю нового ребенка:
@Transactional(propagation=Propagation.REQUIRES_NEW)
void update() {
Parent parent = repository.findOne(parentID);
Child newChild = new Child();
newChild.setParent(parent);
parent.add(newChild);
parent = repository.save(parent);
}
Однако на этот раз новый ребенок никогда не сохраняется!
Я пробовал почти все варианты CascadeType, @GeneratedValue GenerationType, @Transactional Propagation type...
Проследив это через спящий режим (мучительно!), вот что я нашел:
- При сохранении во второй раз проблема со вторым (новым) потомком.
- Проблема, по-видимому, заключается в том, что когда приходит время сохранять родительский список дочерних элементов, новый дочерний элемент отсутствует в EntityManager (пока) и, следовательно, считается переходным.
- В результате он фактически передается по цепочке как null, что приводит к следующему:
org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing thetransaction at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:521) ... Caused by: javax.persistence.RollbackException: Error while committing the transaction at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:92) at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:512) ... Caused by: org.hibernate.AssertionFailure: collection [null] was not processed by flush() at org.hibernate.engine.spi.CollectionEntry.postFlush(CollectionEntry.java:225) ...
- Может быть важно, что в моем фактическом коде «Ребенок» также имеет карту дочерних сущностей. Это «значение» — это то, что передается как null из-за незаконного присвоения «Transient».
- Я использовал репозиторий.saveAndFlush(), чтобы синхронизировать вещи для отладки. Когда я использую только .save(), вызывается мой @PreUpdate EntityListener, но слушатель @PostUpdate никогда не вызывается.
- Кажется, что не было бы проблемы, если бы Child был просто сохранен или ему был присвоен идентификатор, по крайней мере, до сохранения Parent. Но также кажется контрпродуктивным делать это вручную. Тем не менее, это единственная альтернатива, о которой я могу думать.
Спасибо за прочтение. Любая помощь приветствуется!