我应该使用哪个注释:@IdClass 或 @EmbeddedId
-
03-07-2019 - |
题
这 JPA
(Java Persistence API)规范有两种不同的方式来指定实体组合键: @IdClass
和 @EmbeddedId
.
我在映射的实体上使用了这两种注释,但对于不太熟悉的人来说这是一团糟 JPA
.
我只想采用一种方式来指定组合键。哪一个才是真正最好的?为什么?
解决方案
我认为 @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
推广的更自然的语言不同。但是大多数时候从查询中理解给定字段是复合键的一部分是非常宝贵的帮助。
其他提示
我发现了一个必须使用EmbeddedId而不是IdClass的实例。在这种情况下,有一个连接表,其中定义了其他列。我尝试使用IdClass来解决此问题,以表示明确表示连接表中的行的实体的键。我无法以这种方式工作。值得庆幸的是“Java Persistence With Hibernate”有专门讨论这个主题的部分。一个提议的解决方案与我的解决方案非常相似,但它使用的是EmbeddedId。我在书中的对象之后对我的对象进行了建模,现在它的行为正确。
使用复合主键有三种策略:
- 将其标记为
@Embeddable
并向您的实体类添加一个普通属性,标记为@Id
. - 向您的实体类添加一个普通属性,标记为
@EmbeddedId
. - 将属性添加到实体类的所有字段,并用
@Id
,并标记您的实体类@IdClass
, ,提供主键类的类。
指某东西的用途 @Id
类标记为 @Embeddable
是最自然的方法。这 @Embeddable
无论如何,标签可用于非主键可嵌入值。它允许您将复合主键视为单个属性,并且允许重用 @Embeddable
其他表中的类。
下一个最自然的方法是使用 @EmbeddedId
标签。这里,主键类不能在其他表中使用,因为它不是一个 @Embeddable
实体,但它允许我们将密钥作为一个
某个类的单一属性。
最后,使用 @IdClass
和 @Id
注释允许我们使用与主键类中的属性名称相对应的实体本身的属性来映射复合主键类。名称必须对应(没有覆盖这一点的机制),并且主键类必须履行与其他两种技术相同的义务。这种方法的唯一优点是它能够从封闭实体的接口中“隐藏”主键类的使用。这 @IdClass
注解采用 Class 类型的值参数,该值参数必须是要用作复合主键的类。与要使用的主键类的属性对应的字段必须全部注释为 @Id
.
据我所知,如果您的复合PK包含FK,使用 @IdClass
使用 @EmbeddedId
,你必须为你的FK列定义两次映射,一次在 @Embeddedable
中,一次用于as @ManyToOne
,其中 @ManyToOne
必须是只读的( @PrimaryKeyJoinColumn
),因为你不能在两个变量中设置一个列(可能的冲突)。
因此,您必须使用 @Embeddedable
中的简单类型设置FK。
在使用 @IdClass
的其他网站上,可以更轻松地处理这种情况,如通过OneToOne和ManyToOne关系的主键:
示例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 class
...
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;
}
}
我认为主要的优点是我们可以在使用 @IdClass
时使用 @GeneratedValue
作为id?我确信我们不能将 @GeneratedValue
用于 @EmbeddedId
。
使用 @EmbeddedId
时,复合键不得具有 @Id
属性。
使用EmbeddedId,你可以在HQL中使用IN子句,例如: FROM Entity WHERE id IN:ids
其中id是EmbeddedId,而你想要的IdClass实现相同的结果是很痛苦的做类似 FROM Entity WHERE idPartA =:idPartA0 AND idPartB =:idPartB0 ....或idPartA =:idPartAN AND idPartB =:idPartBN