使用hibernate一对一引用完整性

时间:2011-03-22 10:05:03

标签: java hibernate one-to-one

我有两张桌子 -

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

现在我想使用hibernate在我的对象模型中映射它。 我需要在Foo_properties中使用foo_id,因为我想保持参照完整性并希望添加ON DELETE CASCADE约束。
所以我按照以下方式绘制了关系 -

@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;
}

既然拥有方是FooProperties类,我面临以下问题 -

如果我将FooProperties的新实例设置为Foo,则现有的FooProperties仍然保留在DB中,而hibernate不会删除该实例,例如

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

这导致FooProperties表中的新行以及现有行。现在我不明白我如何改变我的映射,所以我可以让上面的代码(或它的变体)适用于所有场景,这意味着我需要Foo作为拥有方和FooProperties中的foo_id。有没有办法像这样定义映射?

注意:我已基于此问过question,但我认为我在上一个问题中并不清楚,所以又问了另一个问题。

4 个答案:

答案 0 :(得分:4)

您已被告知使用orphanRemoval = trueCascadeType.DELETE_ORPHAN。但是,由于对JPA规范的解释具有欺骗性,因此对于一对一的关系(HHH-5559),它将无法正常工作。

您可以使用以下技巧实现orphanRemoval的正确行为:

@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;
    ...
}

如果您不需要FooPropeties.foo

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

    // getter/setter as above
    ...
}

答案 1 :(得分:2)

我认为不是在实体上调用merge,如果直接调用更新会话对象,那么hibernate将首先删除现有行,然后添加新行。我实现了相同的,但在我的情况下,我使用xml映射实体。我希望这会对你有所帮助。

答案 2 :(得分:0)

Bar是关联的所有者(如反面的mappedBy所示),因此必须在那里设置级联。

编辑:

要反转它,this可能有帮助。

答案 3 :(得分:0)

有两个选项供您选择,因为您不想更改映射:

  1. 通过服务层逻辑完成。我认为你已经有了类似的问题。
  2. 在关系的Foo端使用Hibernate注释@Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)。然而,这是明确的Hibenate和JPA 2不包括对它的支持。