2

I've a entity setup that boils down to something like that:

Person (N:1) -> Job (1:N) -> Company

So a person has a java.util.Set of Job instances. The set is defined like this:

@OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Job> jobs = new LinkedHashSet<>();

And the company looks like this:

@Entity
public class Company {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(nullable = false, unique = true)
    private String name;
    @OneToMany
    private List<Stelle> jobs = new ArrayList<Stelle>();

    public String getDisplayText() { return name; }
    @Override
    public int hashCode() {
        int result = 1;
        result = 31 * result
            + ((getDisplayText() == null) ? 0 : getDisplayText().hashCode());
        return 31 * result + ((getId() == null) ? 0 : getId().hashCode());
    }
    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Company))
           return false;
        return !(getId() == null && ((Company) obj).getId() != null)
             || getId() == null
             || (getId().equals(((Company) obj).getId());
    }
    // getters + setters
}

I create and attach jobs to a person instance in my frontend. The company is selected and attached to the new Job entity with a javax.faces.Converter, where it is fetched from the persistence context via its id.

When I call entityManager.merge(person) on a person that has two Jobs with the same Company, I get this exception:

Caused by: java.lang.IllegalStateException: Error occurred while storing 
  entity [example]. An entity copy [com.company.entity.Company#1] was already
  assigned to a different entity [example].
at org.hibernate.event.internal.EventCache.put(EventCache.java:192)     
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:287)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:153)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:851)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:833)   
at org.hibernate.engine.spi.CascadingActions$6.cascade(CascadingActions.java:277)
[...]

My problem seems to be that DefaultMergeEventListener.java:113 (Hibernate-4.3.0.CR1) says:

if ( copyCache.containsKey( entity ) &&
        ( copyCache.isOperatedOn( entity ) ) ) {
    LOG.trace( "Already in merge process" );
    event.setResult( entity );
}
else { /* fetch entity and put it in copyCache */ }

copyCache is a java.util.IdentityHashMap, so my two detached entities are not considered equal for this cache, even if they are for according to their equals() method.

My question is simple: can I somehow keep using the flow described above or what do I have to alter to get Hibernate to merge this entity graph?

Edited to add: I'm aware of Hibernate: An entity copy was already assigned to a different entity and IllegalStateException: Error occurred while storing entity <entity> An entity copy <entity> was already assigned to a different entity <entity_copy> describing similar problems.

Community
  • 1
  • 1
mabi
  • 5,279
  • 2
  • 43
  • 78
  • 1
    check link::: http://stackoverflow.com/questions/10550511/illegalstateexception-with-hibernate-4-and-manytoone-cascading – dev Nov 27 '13 at 10:52
  • @Purnendu good find, it's the same problem. Mentions "downgrading" as the working solution, though. – mabi Nov 27 '13 at 11:05
  • From linked the question it's more like "fix your application code". This means "cache every seen instance of `Company` in the converter" for me. Would be a rather sad result. I'm hoping for an explanation why the restriction in Listener is so hard in the first place. – mabi Nov 27 '13 at 11:10

1 Answers1

2

I got such problem earlier and i used below code in hibernate file:::

Session session = getSession();
if(pojo.id!=null){
   session.merge(pojo)
}
else{
    session.saveOrUpdate(pojo)
}

where getSession() is

public Session getSession() {
    Session session = getSessionFactory().getCurrentSession();
    if (session == null) {
        session = getSessionFactory().openSession();
    }
    return session;
}
dev
  • 715
  • 5
  • 21
  • Thanks, but I don't follow. What's `pojo`? My person? In the case I'm currently testing, the person always has a `id` set, so the `saveOrUpdate` branch never would get called. – mabi Nov 27 '13 at 10:30
  • pojo is your entity Company – dev Nov 27 '13 at 10:34
  • So you're suggesting I'd walk the object graph myself and merge the entities manually before calling `merge` on the person object? This would be really unfortunate, I expected the `cascade=ALL` to do this for me. – mabi Nov 27 '13 at 10:39
  • You have to use merge on person. – dev Nov 27 '13 at 10:43