Referential integrity with One to One using hibernate

安稳与你 提交于 2019-12-04 04:21:08

问题


I'm having two tables -

Foo { foo_id, name }
Foo_properties { fp_id, foo_id, phoneNumber}

Now I want to map this in my object model using hibernate.. I need a foo_id in Foo_properties because i want to maintain referential integrity and want to add ON DELETE CASCADE constraint.
so I mapped the relation in the following way -

@Entity
public class Foo{
    @Id
    private long foo_id;

    private String name;

    @OneToOne(mappedBy = "foo")
    private FooProperties fooProperties;
}

@Entity
public class FooProperties{

    @Id
    private long fp_id;

    private String phoneNumber;

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

Now since the owning side is FooProperties class, I'm facing following issues -

If I set the new instance of FooProperties to Foo the existing FooProperties still remains in DB and hibernate doesn't delete that instance, e.g.

Foo foo = entityManager.find(Foo.class, fooId);
foo.setFooProperties(new FooProperties("xxx-xxx-xxx"));
entityManager.merge(foo);

This results into the new row in FooProperties table along with the existing one. Now I don't understand how I can change my mapping to so I can have above code (or variant of it) working for all scenarios, that means I need Foo as a owning side and foo_id in FooProperties. Is there any way to define the mapping like this?

NOTE: I already asked question based on this but I think I wasn't clear in previous question so asked this another one.


回答1:


You were already told to use orphanRemoval = true or CascadeType.DELETE_ORPHAN. However, due to casuistics in interpretation of JPA Specification it wouldn't work as expected for one-to-one relationships (HHH-5559).

You can achieve a proper behaviour of orphanRemoval with the following trick:

@Entity
public class Foo{
    @OneToMany(mappedBy = "foo", orphanRemoval = true)
    private List<FooProperties> fooProperties;

    public FooProperties getFooProperties() {
        if (fooProperties == null || fooProperties.isEmpty()) return null;
        else return fooProperties.get(0);
    }

    public void setFooProperties(FooProperties newFooProperties) {
        if (fooProperties == null) fooProperties = new ArrayList<FooProperties>();
        else fooProperties.clear();
        if (newFooProperties != null)
            fooProperties.add(newFooProperties);            
    }
    ...
}

@Entity
public class FooProperties{
    @ManyToOne
    @JoinColumn(name = "foo_id",  nullable = false)
    private Foo foo;
    ...
}

Or even this, if you don't need FooPropeties.foo:

@Entity
public class Foo{
    @OneToMany(orphanRemoval = true)
    @JoinColumn(name = "foo_id",  nullable = false)        
    private List<FooProperties> fooProperties;

    // getter/setter as above
    ...
}



回答2:


I think instead of calling merge on the entity, if you directly call update on session object then hibernate will first delete the existing row and then it will add the new one. I implemented the same, but, in my case I used xml for mapping the entity. I hope this will help you.




回答3:


Bar is the owner of the association (as indicated by the mappedBy on the inverse side) and thus the cascade has to be set there.

Edit:

To invert that, this might help.




回答4:


There are 2 options for you to choose from, since you don't want to change your mapping :

  1. Do it via your service layer logic. I think you have a similar question already.
  2. Use the Hibernate annotation @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN) on the Foo side of the relationship. However this is explicitly Hibenate and JPA 2 doesn't include support for the same.


来源:https://stackoverflow.com/questions/5389561/referential-integrity-with-one-to-one-using-hibernate

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!