2011-08-25 97 views
2

我有以下情形:JPA /休眠,@OneToMany和约束(Oracle)的

@Entity 
class A { 

    @Id 
    @GeneratedValue 
    private long dbId; 

    @OneToMany (cascade = CascadeType.ALL) 
    @JoinColumn(name = "A_ID", referencedColumnName = "DBID") 
    Set<C> setOfCs; 

} 

@Entity 
class B { 

    @Id 
    @GeneratedValue 
    private long dbId; 

    @OneToMany (cascade = CascadeType.ALL) 
    @JoinColumn(name = "B_ID", referencedColumnName = "DBID") 
    Set<C> setOfCs; 

} 

@Entity 
class C { 

    @Id 
@GeneratedValue 
private long dbId; 

} 

表C与两列A_ID和B_ID作为外键分别A.DBID和B.DBID创建。这对我来说没有什么意义,因为C中的每个元素都是从A或(xor)B链接的,但是不能同时连接(两个关系都是一对多非多对多关系)。

是否有一种方法可以在A_ID和B_ID没有外键约束的情况下拥有相同的表(这很好)?当我设置A-> C时,Oracle抱怨没有设置C.B_ID。

感谢

利玛窦

回答

1

你为什么不使用@JoinTable?它将删除表C上的任何外键。

@OneToMany(cascade = CascadeType.ALL,fetch=FetchType.LAZY,orphanRemoval=true) 
@JoinTable(name = "A_B_TABLE", joinColumns = { @JoinColumn(name = "A_ID") },  inverseJoinColumns = { @JoinColumn(name = "DB_ID") }) 
public Set<C> getSetOfCs() { 
    return setOfCs; 
} 

顺便说一句,不要注释项目,而是在它的getter上做它。

+0

这样做可以避免这个问题,但它会削弱参照完整性:如果A-C和B-C关系都用连接表处理,那么就不可能确保存在某种关系。 –

+0

此外,你误以为getter访问比field访问更好;这主要是品味的问题,并且有[无论是哪种方式](http://blog.xebia.com/2009/06/jpa-implementation-patterns-field-access-vs-property-access/) - 我发现支持实地访问的论据完全有说服力,正如它所发生的那样。 –

+0

谢谢你解决我的问题。是的,它会在数据库级别上失去一些完整性检查,但这些检查是(或者应该)由我们的应用程序确保的。如果拥有与原始示例中可空的外键相同的数据库结构,那也不错。由于在真正的应用程序中,我们与表格'C'有很多关系,所以我们将有许多额外的表格,但在第一次测试中似乎不是一个大问题。 – Matteo

0

我能想到的唯一方法就是通过一个通用的类强制两个关系,C可以有一个外键。

最简单的方法是引入一些X类,它们是A和B的超类,并且具有dbId和setOfC属性。 C的表将会有一个列X_ID。

或者,使C抽象并引入子类Ca和Cb,使得A的实例始终引用Ca的实例,并将B的实例引用到Cb。 Ca和Cb中的每一个只需要一个外键。虽然这可能非常尴尬;这意味着你永远不可能将C的实例从A转移到B.

如果你不能引入任何基类,你可以将Wheeler's law和plaster应用于间接,结合上述两种方法。创建一个抽象类X,并赋予其A和B的dbId和setOfC属性; C然后与X有一个幻影ManyToOne关系,并且它有相应的外键。创建X,Xa和Xb的两个具体子类,每个分别与A或B的实例具有OneToOne关系,A和B的实例具有指向另一个方向的相应OneToOne关系。

+0

是的,我也想过改变模型,但这(原因有几个)只有我最后的选择。 – Matteo