I'm new to Spring/JPA/Hibernate, and while it sounds easy reality just hasn't been. I could use some help.
I have a parent Entity that holds a list of child Entities. I'll use these to keep the discussion simple:
@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> {};
Round 1, I create a new parent and a new child, add the child to the parent's list and set the parent on the child. When I save the parent the child is saved as well.
@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);
}
Now, Round 2, I add a new child:
@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);
}
However, this time the new child is never persisted!
I've tried most every variation of CascadeType, @GeneratedValue GenerationType, @Transactional Propagation type...
Tracing this through hibernate (painful!), here's what I've found:
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) ...
Thanks for reading. Any help would be much appreciated!
I found the problem, though I don't really have a complete solution yet.
First, some additional background. In addition to Parent and Child, I had a related class I'll call "House" here. House has an EntityListener defined so that when it is saved/updated, the associated Parent & Child objects get created/updated. So it is during House's PostPersist/PostUpdate that Parent and Child objects are created, linked, pointed back to House, and then persisted.
So the problem seems to be this is done before the House transaction completes. By merely pulling out the Parent/Child activity until after the House activity completes, all the problems went away.
The only thing I can figure (I'll dig a little deeper) is that since House hasn't been completely persisted at that moment, it results in the Transient condition described above.
Update:
Chalk one up to ignorance. Apparently EntityCallback methods "should not call EntityManager or Query methods and should not access any other entity objects." Did not know that. This raises the question now of how should I trigger an Entity's creation on another's activity. But I'll start another thread for that if necessary. Thanks all!
Everything in what you've shown seems pretty normal, so the problem might lie in that map you mentioned. I have a working example of a bidirectional one-to-many using Spring Data JPA on Github. You can look through the code or clone and run it with:
git clone git://github.com/zzantozz/testbed tmp
cd tmp/spring-data
mvn -q compile exec:java -D exec.mainClass=rds.springdata.JpaBiDiOneToManyExample
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With