JPA:@JoinColumn和@PrimaryKeyJoinColumn之间的区别是什么?

95

@JoinColumn@PrimaryKeyJoinColumn的确切区别是什么?

你可以使用@JoinColumn来定义外键列。典型的列可能如下所示(例如,在具有附加属性的联接表中):

@ManyToOne
@JoinColumn(name = "...")
private OtherClass oc;

如果我将该列晋升为主键,会发生什么(也称为识别关系)?由于该列现在是主键,我必须使用@Id进行标记:

@Id
@ManyToOne
@JoinColumn(name = "...")
private OtherClass oc;

现在的问题是:

@Id + @JoinColumn 和只使用 @PrimaryKeyJoinColumn 是否相同?

@ManyToOne
@PrimaryKeyJoinColumn(name = "...")
private OtherClass oc;

如果不是这样的话,那么@PrimaryKeyJoinColumn有什么用呢?

4个回答

61
如果我将该列提升为主键(即标识关系),会发生什么?由于该列现在是主键,我必须使用@id对其进行标记。这种增强的派生标识符支持实际上是JPA 2.0中的新内容的一部分(请参见JPA 2.0规范中的2.4.1节“对应于派生标识符的主键”)。JPA 1.0不允许在OneToOne或ManyToOne上使用Id。使用JPA 1.0,您必须使用PrimaryKeyJoinColumn,并为外键列定义Basic Id映射。
现在的问题是:@Id + @JoinColumn和@PrimaryKeyJoinColumn是否相同?
您可以通过在OneToOne或ManyToOne上使用Id来获得类似的结果,这是映射JPA 2.0中的派生标识符的首选方式,而且更加简单。PrimaryKeyJoinColumn仍可在JOINED继承策略中使用。以下是JPA 2.0规范的相关部分:

11.1.40 PrimaryKeyJoinColumn Annotation

The PrimaryKeyJoinColumn annotation specifies a primary key column that is used as a foreign key to join to another table.

The PrimaryKeyJoinColumn annotation is used to join the primary table of an entity subclass in the JOINED mapping strategy to the primary table of its superclass; it is used within a SecondaryTable annotation to join a secondary table to a primary table; and it may be used in a OneToOne mapping in which the primary key of the referencing entity is used as a foreign key to the referenced entity[108].

...

If no PrimaryKeyJoinColumn annotation is specified for a subclass in the JOINED mapping strategy, the foreign key columns are assumed to have the same names as the primary key columns of the primary table of the superclass.

...

Example: Customer and ValuedCustomer subclass

@Entity
@Table(name="CUST")
@Inheritance(strategy=JOINED)
@DiscriminatorValue("CUST")
public class Customer { ... }

@Entity
@Table(name="VCUST")
@DiscriminatorValue("VCUST")
@PrimaryKeyJoinColumn(name="CUST_ID")
public class ValuedCustomer extends Customer { ... }

[108] The derived id mechanisms described in section 2.4.1.1 are now to be preferred over PrimaryKeyJoinColumn for the OneToOne mapping case.

参见


这个来源 http://weblogs.java.net/blog/felipegaucho/archive/2009/10/24/jpa-join-table-additional-state 表示使用 @ManyToOne 和 @Id 在 JPA 1.x 中是可行的。那么现在谁是正确的呢? 作者使用一个预发布的 JPA 2.0 兼容版本的 EclipseLink(文章发表时的版本为 2.0.0-M7)来撰写一篇关于 JPA 1.0 的文章。这篇文章是具有误导性的,因为作者使用了 JPA 1.0 中不存在的内容。

记录一下,在EclipseLink 1.1中增加了对OneToOneManyToOne上的Id的支持(请参阅来自EclipseLink comitter和Java Persistence wiki书籍的主要贡献者James Sutherland此消息)。但是我要强调,这不是JPA 1.0的一部分。


这个来源http://weblogs.java.net/blog/felipegaucho/archive/2009/10/24/jpa-join-table-additional-state指出在JPA 1.x中使用@ManyToOne和@Id是可行的。现在谁是正确的? - Kawu
好的。谢谢你澄清了这一点。我理解得没错,那个糟糕的例子中使用了错误的@IdClass吗?@Id注释不应该放在实体类中的单独(冗余/重复)列中才是正确的吗?(尽管我知道现在不推荐使用@IdClass) - Kawu
我的意思是,类中应该有两个属性:@Id @Column private String institution; 和 @Id @Column private String competition; 这样就可以指定主键了,对吧? - Kawu
@Kawu,非常抱歉,老实说,在一个小的评论框里讨论这个问题太难了。我甚至不确定我是否理解了你在谈论什么。如果你有其他具体问题,请建议选择一个JPA实现并进行尝试,或者发布一个的问题,包括完整的示例(以及JPA版本)。这将会使事情变得更容易。 - Pascal Thivent

61

我通常会通过这个图来区分两者:

使用 PrimaryKeyJoinColumn

enter image description here

使用 JoinColumn

enter image description here


5
@yusher,“PrimaryKeyJoinColumn”使用相同的主键值来连接两个表,“JoinColumn”中,主表的主键将成为另一张表中的外键。 - Sam YC
你能展示一下你是如何实施的吗? - undefined

3

我知道这是一篇旧文章,但使用PrimaryKeyColumn的好处在于您想要单向关系或多个表共享相同ID时。

总的来说,这是一个不好的做法,最好使用带有JoinColumn的外键关系。

话虽如此,如果您正在处理旧数据库并且该数据库使用了这样的系统,则可以使用它。


1

当你想管理(更改列名、设置可空等)目标实体表中的外键列时,你可以使用@ JoinColumn。在这里,Address表将包含User表的id作为外键,但该列将被命名为 user_id (@ Sam YC的第二个方案)

@Entity
public class Address implements Serializable {

@Id
@GeneratedValue
private String id;

private String city;

@OneToOne(optional = false)
@JoinColumn(name = "user_id", updatable = false)
private User user;
}

当您想要使用引用实体的主键作为目标实体的主键时,可以使用@ PrimaryKeyJoinColumn。在这里,Address知道引用User,但Address表没有外键列,因为它与用户ID具有相同的id(@ Sam YC的第一个方案)。

@Entity
public class Address implements Serializable {

@Id
@GeneratedValue(generator = "foreignKeyGenerator")
@GenericGenerator(
        name = "foreignKeyGenerator",
        strategy = "foreign",
        parameters = @org.hibernate.annotations.Parameter(
                name = "property", value = "userT"
        )
)
private String id;
private String city;

@OneToOne(optional = false)
@PrimaryKeyJoinColumn
private User userT;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接