ChatGPT解决这个技术问题 Extra ChatGPT

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

JPA(Java Persistence API)规范有 2 种不同的方式来指定实体复合键:@IdClass@EmbeddedId

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

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


K
Kurt Du Bois

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

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

这给出了构成复合键的字段的清晰概念,因为它们都聚合在一个通过字段访问运算符访问的类中。

@IdClass@EmbeddedId 的另一个区别是在编写 HQL 时:

使用 @IdClass,您可以编写:

select e.name from Employee e

并且使用 @EmbeddedId 您必须编写:

select e.employeeId.name from Employee e

您必须为同一个查询编写更多文本。有些人可能会争辩说,这与 IdClass 所提倡的更自然的语言不同。但大多数情况下,从查询中了解给定字段是组合键的一部分是非常有用的。


虽然我同意上面给出的解释,但我还想为 @IdClass 添加一个独特的用例,尽管在大多数情况下我更喜欢 @EmbeddedId(从 Antonio Goncalves 的会议中知道这一点。他建议我们可以如果复合键类不可访问或来自另一个模块或无法添加注释的遗留代码,请使用 @IdClass。在这些情况下,@IdClass 将为我们提供一种 .
我认为使用 @Gaurav 给出的 @IdClass 注释的情况可能正是 JPA 规范列出了创建复合键的两种方法的原因。@IdClass@EmbeddidId
j
jumping_monkey

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

将其标记为@Embeddable 并向您的实体类添加一个普通属性,并用@Id 标记。

为您的实体类添加一个普通属性,用@EmbeddedId 标记。

将属性添加到您的实体类的所有字段,用@Id 标记它们,并用@IdClass 标记您的实体类,提供您的主键类的类。

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

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

最后,使用 @IdClass@Id 注释允许我们使用实体本身的属性映射复合主键类,这些属性对应于主键类中的属性名称。 名称必须一致(没有覆盖它的机制),并且主键类必须履行与其他两种技术相同的义务。这种方法的唯一优点是它能够从封闭实体的接口“隐藏”主键类的使用。 @IdClass 注解采用 Class 类型的 value 参数,该参数必须是用作复合主键的类。 使用的主键类属性对应的字段都必须用@Id注解。

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


l
laz

我发现了一个实例,我必须使用 EmbeddedId 而不是 IdClass。在这种情况下,有一个定义了附加列的连接表。我尝试使用 IdClass 来表示明确表示连接表中的行的实体的键来解决这个问题。我无法让它以这种方式工作。值得庆幸的是,“Java Persistence With Hibernate”有一个专门讨论这个主题的部分。一个提议的解决方案与我的非常相似,但它使用 EmbeddedId 代替。我按照书中的对象建模了我的对象,它现在可以正常运行。


O
Ondrej Bozek

据我所知,如果您的复合 PK 包含 FK,则使用起来会更容易、更直接@IdClass

使用 @EmbeddedId,您必须为 FK 列定义映射两次,一次在 @Embeddedable 中,一次在 @ManyToOne 中,其中 @ManyToOne 必须是只读的(@PrimaryKeyJoinColumn),因为您不能一列设置在两个变量中(可能发生冲突)。
因此,您必须使用 @Embeddedable 中的简单类型设置 FK。

在使用 @IdClass 的其他站点上,可以更容易地处理这种情况,如 Primary Keys through OneToOne and ManyToOne Relationships 所示:

示例 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;
    }
}

只是不要忘记在你的 PK 类中添加 getter
@Sonata 为什么我们需要吸气剂?我在没有任何 getter/setter 的情况下尝试了它,它工作正常
感谢 id 类示例!虽然我最终也需要实现 Serializable 。还不如添加 getter 和 setter,特别是如果您的 IDE 可以自动生成它们。
b
bluish

我认为主要优点是我们可以在使用 @IdClass 时使用 @GeneratedValue 作为 id?我确定我们不能将 @GeneratedValue 用于 @EmbeddedId


不能在 Embeddedid 中使用@GeneratedValue 吗?
我已经成功地将它与 EmbeddedId 一起使用,但显然它的支持因数据库而异。将它与 IdClass 一起使用也是如此。规范说:“GeneratedValue 注释只能可移植地用于简单(即非复合)主键。”
b
bluish

使用 @EmbeddedId 时,复合键不能有 @Id 属性。


A
Aleks Ben Maza

使用 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 的操作