Consider following entities (getters and setters are omitted for brevity):
{code:java}@Entity public class Parent { @Id @GeneratedValue private int id;
@OneToMany(mappedBy = "parent", orphanRemoval = true, cascade = CascadeType.ALL) @BatchSize(size = 20) private List<Child> children = new ArrayList<Child>();
public void addChild(Child child) { child.setParent(this); this.children.add(child); } }
@Entity public class Child { @Id @GeneratedValue private int id;
@ManyToOne(optional = false, fetch = FetchType.LAZY) @JoinColumn(name = "parent_id", referencedColumnName = "id", nullable = false, updatable = false) private Parent parent; }{code}
We have already saved the parent in the database and want to add a child to it. We use Spring Data JPA and test is written with it.
{code:java}Parent existingParent = parentRepository.findById(PARENT_ID).get(); Child newChild= new Child (); existingParent .addChild(newChild); Parent savedParent = parentRepository.save(existingParent);{code}
After that hibernate perform Merge operation on Parent entity and Cascade merge on Child collection. We expect that _savedParent_ will have one element in children collection, but it contains two the same elements. In database created only one.
This example works fine.
{code:java}Parent existingParent = parentRepository.findById(PARENT_ID).get(); Child newChild= new Child (); existingParent.getChildren().size(); existingParent .addChild(newChild); Parent savedParent = parentRepository.save(existingParent);{code}
After debugging I found out, that in Class DefaultMergeEventListener
{code:java}protected void entityIsPersistent(MergeEvent event, Map copyCache) { LOG.trace( "Ignoring persistent instance" );
//TODO: check that entry.getIdentifier().equals(requestedId)
final Object entity = event.getEntity(); final EventSource source = event.getSession(); final EntityPersister persister = source.getEntityPersister( event.getEntityName(), entity );
( (MergeContext) copyCache ).put( entity, entity, true ); //before cascade!
cascadeOnMerge( source, persister, entity, copyCache ); // here we have two elements in collection, one Child that has been persisted in database, and the same Child object, before it persisted in database. copyValues( persister, entity, entity, source, copyCache ); // here we have two the same Child object that have been persisted in database event.setResult( entity ); }{code}
Verified this bug with the latest Hibernate-core. |
|