JPA
(Java持久性API)规范有两种不同的方式来指定实体组合键:@IdClass
和@EmbeddedId
。
我在我的映射实体上使用了两个注释,但对于那些不熟悉JPA
的人来说,这是一个很大的混乱。
我想只采用一种方法来指定复合键。哪一个真的最好?为什么呢?
答案 0 :(得分:79)
我认为@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
推广的更自然的语言不同。但是大多数时候从查询中理解给定字段是复合键的一部分是非常宝贵的帮助。
答案 1 :(得分:17)
我发现了一个必须使用EmbeddedId而不是IdClass的实例。在这种情况下,有一个连接表,其中定义了其他列。我尝试使用IdClass来解决此问题,以表示明确表示连接表中的行的实体的键。我无法以这种方式工作。值得庆幸的是“Java Persistence With Hibernate”有一节专门讨论这个主题。一个提议的解决方案与我的解决方案非常相似,但它使用的是EmbeddedId。我在书中的对象之后对我的对象进行了建模,现在它的行为正确。
答案 2 :(得分:14)
使用复合主键有三种策略:
@Embeddable
,并在其实体类中添加标准属性,标记为@Id
。@EmbeddedId
。@Id
标记它们,并使用@IdClass
标记您的实体类,提供主键类的类。将@Id
与标记为@Embeddable
的类一起使用是最自然的方法。无论如何,@Embeddable
标记可用于非主键可嵌入值。它允许您将复合主键视为单个属性,并允许在其他表中重用@Embeddable
类。
下一个最自然的方法是使用@EmbeddedId
标签。这里,主键类不能在其他表中使用,因为它不是@Embeddable
实体,但它允许我们将键视为
某个类的单个属性。
最后,使用@IdClass
和@Id
注释允许我们使用与主键类中的属性名称对应的实体本身的属性来映射复合主键类。名称必须对应(没有覆盖它的机制),主键类必须履行与其他两种技术相同的义务。这种方法的唯一优点是它能够从封闭实体的接口“隐藏”主键类的使用。 @IdClass
注释采用Class类型的值参数,该参数必须是要用作复合主键的类。与要使用的主键类的属性对应的字段都必须使用@Id
进行注释。
答案 3 :(得分:11)
据我所知,如果您的复合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 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;
}
}
答案 4 :(得分:7)
我认为主要优势是我们可以在使用@GeneratedValue
时使用@IdClass
作为ID吗?我确定我们无法将@GeneratedValue
用于@EmbeddedId
。
答案 5 :(得分:4)
使用@Id
时,复合键不得具有@EmbeddedId
属性。
答案 6 :(得分: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
<的操作/ p>