我应该使用哪个注释:@IdClass或@EmbeddedId


128

JPA(Java持久性API)规范有2名不同的方式来指定实体组合键:@IdClass@EmbeddedId

我在映射的实体上同时使用了这两个注释,但是对于不太熟悉的人来说,这真是一个大麻烦JPA

我只想采用一种指定复合键的方法。哪一个真的是最好的?为什么?

Answers:


86

我认为这@EmbeddedId可能更冗长,因为@IdClass无法使用任何字段访问运算符来访问整个主键对象。使用,@EmbeddedId您可以这样做:

@Embeddable class EmployeeId { name, dataOfBirth }
@Entity class Employee {
  @EmbeddedId EmployeeId employeeId;
  ...
}

这给构成组合键的字段提供了清晰的概念,因为它们全部聚集在通过字段访问运算符访问的类中。

@IdClass和的另一个不同@EmbeddedId之处是编写HQL时:

@IdClass你一起写:

从员工e中选择e.name

@EmbeddedId你一起写:

从员工e中选择e.employeeId.name

您必须为同一查询写更多文本。有人可能会认为这与一种更自然的语言有所不同,例如由提倡的语言IdClass。但是大多数时候,从查询中正确了解给定字段是组合键的一部分是非常有用的。


10
尽管我同意上面给出的解释,但我也想添加一个独特的用例,@IdClass即使我@EmbeddedId在大多数情况下都比较喜欢(可以从Antonio Goncalves的一次会议中了解到这一点。他建议@IdClass在复合的情况下可以使用键类是无法访问或从其它模块或遗留代码,我们不能添加注释。在这些情况进来@IdClass会给我们一个方式我们。
拉夫拉瓦特

1
我认为这是可能的情况下,使用@IdClass由@Gaurav给出的注解是非常原因,JPA规范列表创建复合键的两种方法.. @IdClass@EmbeddidId
kapad

20

有三种使用复合主键的策略:

  • 将其标记为,@Embeddable并为其添加一个普通属性(标记为)@Id
  • 将标记为的普通属性添加到您的实体类@EmbeddedId
  • 将属性的所有字段添加到您的实体类中,用标记它们@Id,并用标记您的实体类@IdClass,以提供主键类的类。

使用@Id标记为的类@Embeddable是最自然的方法。@Embeddable无论如何,该标记可用于非主键可嵌入值。它允许您将复合主键视为单个属性,并允许@Embeddable在其他表中重用该类。

第二种最自然的方法是使用@EmbeddedId标签。在这里,主键类不是@Embeddable实体,因此不能在其他表中使用,但是它确实允许我们将键视为某个类的单个属性。

最后,@IdClass@Id注释的使用允许我们使用与主键类中的属性名称相对应的实体本身的属性来映射复合主键类。名称必须对应(没有机制可以覆盖它),并且主键类必须遵守与其他两种技术相同的义务。这种方法的唯一优点是它能够从封闭实体的接口“隐藏”主键类的使用。的@IdClass批注采用类类型的值参数,该参数必须是类被用作化合物主键。与要使用的主键类的属性相对应的字段必须全部用注释@Id

参考:http//www.apress.com/us/book/9781430228509


17

我发现了一个必须使用EmbeddedId而不是IdClass的实例。在这种情况下,有一个联接表,其中定义了其他列。我试图使用IdClass来解决此问题,以代表明确表示联接表中行的实体的键。我无法以这种方式工作。幸运的是,“使用Hibernate进行Java持久性”中有专门针对该主题的部分。一个建议的解决方案与我的非常相似,但是它使用EmbeddedId。我按照本书中的对象对对象进行了建模,现在它可以正确运行。


13

据我所知,您的复合PK是否包含FK,使用起来更简单,更直接 @IdClass

随着@EmbeddedId你要定义你的FK列映射了两次,onece在@Embeddedable一次用于为即@ManyToOne哪里@ManyToOne有只读(@PrimaryKeyJoinColumn),因为你不能有两个变量(可能发生的冲突)一个集。
因此,您必须使用中的简单类型来设置FK @Embeddedable

在另一个站点上,通过OneToOne和ManyToOne关系@IdClass显示在主键中,可以更轻松地处理这种情况:

JPA 2.0 ManyToOne id注释示例

...
@Entity
@IdClass(PhonePK.class)
public class Phone {

    @Id
    private String type;

    @ManyToOne
    @Id
    @JoinColumn(name="OWNER_ID", referencedColumnName="EMP_ID")
    private Employee owner;
    ...
}

JPA 2.0 id类示例

...
public class PhonePK {
    private String type;
    private long owner;

    public PhonePK() {}

    public PhonePK(String type, long owner) {
        this.type = type;
        this.owner = owner;
    }

    public boolean equals(Object object) {
        if (object instanceof PhonePK) {
            PhonePK pk = (PhonePK)object;
            return type.equals(pk.type) && owner == pk.owner;
        } else {
            return false;
        }
    }

    public int hashCode() {
        return type.hashCode() + owner;
    }
}

1
只是不要忘记在您的PK课上添加吸气剂
奏鸣曲

@Sonata为什么我们需要吸气剂?我尝试时没有任何吸气剂/ setter,但效果很好
xagaffar,

感谢您提供id类示例!尽管我最终也需要实现Serializable。还可以添加getter和setter,特别是如果您的IDE可以自动生成它们时。
Starwarswii19年

8

我认为主要的优势在于,@GeneratedValue使用@IdClass?时,我们可以将其用作ID 。我敢肯定,我们不能用@GeneratedValue@EmbeddedId


1
在Embeddedid中不能使用@GeneratedValue吗?
凯塞尔2012年

1
我已经成功将它与EmbeddedId一起使用,但是显然它的支持因数据库而异。将其与IdClass一起使用时也是如此。规范说:“ GeneratedValue注释只能用于简单(即非复合)主键。”
BPS 2015年

4

使用复合键时,不得具有@Id属性@EmbeddedId


1

使用EmbeddedId,您可以在HQL中使用IN子句,例如:FROM Entity WHERE id IN :ids其中id是EmbeddedId,而要使用IdClass达到相同的结果则很麻烦,您将需要执行以下操作FROM Entity WHERE idPartA = :idPartA0 AND idPartB = :idPartB0 .... OR idPartA = :idPartAN AND idPartB = :idPartBN

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.