简体   繁体   中英

Hibernate merge issue with one to one mapping

I have a class hierarchy mapped with InheritanceType.JOINED in Hibernate (3.5.6 final) with JPA 2.0 -

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class A{
    private Long id;

    @OneToOne
    @JoinColumn(name = "foo_id",  nullable = false)
    private Foo foo;
    ...
}

@Entity
@PrimaryKeyJoinColumn(name = "B_ID")
public class B{
        ...
}

@Entity
@PrimaryKeyJoinColumn(name = "C_ID")
public class C{
        ...
}

And the Foo entity is -

@Entity
public class Foo{
    @Id
    private Long id;

    @OneToOne(cascade = CascadeType.ALL, mappedBy = "foo")
    private A a

    // getters / setters omitted    
}

Now I saved the entity Foo with reference of B to field 'a'. Later on I would like to update the instance of Foo with reference of class C to property 'a', so what I did is -

loadedFooInstance.setA(new C());
entityManager.merge(loadedFooInstance);

But what I notice is - it doesn't delete the original B object which was assigned to that Foo object before assigning C. Because of this I need to delete all the reference of A assigned to that Foo instance manually before assigning new one.
But I believe hibernate must have the way to deal with such scenarios and I'm missing something in my mapping. Is anything wrong in my mapping.. or is this kind of mapping can be achieved in better way , so I don't need to deal with such manual work.

Your OneToOne Relation ship is managed by the A-side not by the Foo-side. So you need to change relationship on the A-side (because hibernate payes ony attention to the side where the relationship is managed)

A old = loadedFooInstance.getA();
oldA.setFoo(null);
C c = new C();
c.setFoo(loadedFooInstance);
/*and it is good practic to update foo too*/
loadedFooInstance.setA(c);

The other way is to change the side which manage the relationship:

public abstract class A{
   ...
   @OneToOne(mappedBy = "a") 
   private Foo foo;
   ...
}

public class Foo{
    ...
    @OneToOne
    @JoinColumn(name = "a_id",  nullable = false)
    private A a
    ...
 }

But this would change your database layout too!

Something is not right here in this schema. When you delete B you want to delete Foo, hence A is the owning side of the relationship. So without the existence of B, Foo should not exist. But what you are doing now is removing B from Foo, hence violating the relationship as now there is no B. You should follow Ralph's advice#2. Advice#1 will work fine, but clearly Foo should be the owning side. And the cascading will work from the mapped by side also. Your defined relationship is not correct.

EDIT :

In order to set the existing B in Foo to C: This is almost similar to Ralph's ealier Advice#1

Foo foo = entityManager.find(Foo.class, id);
B b = foo.getA();
entityManager.remove(b); //Explicitly remove or B still remains in database as B is the owning side
C c = new C();
c.setFoo(foo);
entityManager.persist(c);  //c is the owning side, hence save c and not foo
entityManager.refresh(foo);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM