我在使用JPA注释建模/理解部分,非不相交继承时遇到问题。
以下是四个表格:
CREATE TABLE Persons (
id INTEGER NOT NULL,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
PRIMARY KEY (id));
CREATE TABLE Referees (
id INTEGER NOT NULL,
license_nbr VARCHAR(10) DEFAULT NULL NULL,
PRIMARY KEY (id),
CONSTRAINT referees_persons_fk
FOREIGN KEY (id)
REFERENCES Persons (id)
ON DELETE CASCADE
ON UPDATE CASCADE);
CREATE TABLE Coaches (
id INTEGER NOT NULL,
license_nbr VARCHAR(10) DEFAULT NULL NULL,
PRIMARY KEY (id),
CONSTRAINT coaches_persons_fk
FOREIGN KEY (id)
REFERENCES Persons (id)
ON DELETE CASCADE
ON UPDATE CASCADE);
CREATE TABLE Players (
id INTEGER NOT NULL,
registration_nbr VARCHAR(20) DEFAULT NULL NULL,
PRIMARY KEY (id),
CONSTRAINT players_persons_fk
FOREIGN KEY (id)
REFERENCES Persons (id)
ON DELETE CASCADE
ON UPDATE CASCADE);
我缩短的实体类是:
@Entity
@Table(name = "Persons")
@Inheritance(strategy = InheritanceType.JOINED)
public class Person implements Serializable
{
@Id
@Column(name = "id")
private Integer id;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
...
}
@Entity
@Table(name = "Referees")
public class Referee extends Person implements Serializable
{
@Column(name = "license_nbr")
private String licenseNbr = null;
...
}
@Entity
@Table(name = "Coaches")
public class Coach extends Person implements Serializable
{
@Column(name = "license_nbr")
private String licenseNbr = null;
...
}
@Entity
@Table(name = "Players")
public class Player extends Person implements Serializable
{
@Column(name = "registration_nbr")
private String registrationNbr = null;
...
}
部分继承:人物实体可以在没有任何相应的裁判,教练或球员实体的情况下存在
非不相交的继承:可以有任意数量的相应子实体,即一个人可以同时是裁判,教练和球员,可能是三者的任意组合。每个子表只能有一个或一个实体,但不能是多个。
请注意,因为继承是部分的,所以Person不是抽象的。此外,Person / s上没有鉴别器,因为继承是不相交的。
这样的模型在Java ORM中是否可行?注释会是什么样的?
我在使用以下方式注释类的方式查找实例时遇到了问题:
// Michael Jordan: the person who's only a player, too
// EclipseLink 2.1.1 ( 1): ?
// EclipseLink 2.2.0 ( 1): ?
// Hibernate 3.6 (-1): wrong Player instance
Person pe1 = em.find(Person.class, 1);
System.out.println("Loaded person = " + pe1);
// EclipseLink 2.1.1 ( 1): ?
// EclipseLink 2.2.0 ( 1): ?
// Hibernate 3.6 (+1): correct Player instance
Player pl1 = em.find(Player.class, 1);
System.out.println("Loaded player = " + pl1);
// EclipseLink 2.1.1 ( 1): ?
// EclipseLink 2.2.0 ( 1): ?
// Hibernate 3.6 (+1): correct null
Referee re1 = em.find(Referee.class, 1);
System.out.println("Loaded referee = " + re1);
// EclipseLink 2.1.1 ( 1): ?
// EclipseLink 2.2.0 ( 1): ?
// Hibernate 3.6 (+1): correct null
Coach ch1 = em.find(Coach.class, 1);
System.out.println("Loaded coach = " + ch1);
System.out.println();
System.out.println();
// Dirk Nowitzki: the person who's also a player *and* a referee
// EclipseLink 2.1.1 ( 1): ?
// EclipseLink 2.2.0 ( 1): ?
// Hibernate 3.6 (-1): wrong Player instance
Person pe2 = em.find(Person.class, 2);
System.out.println("Loaded person = " + pe2);
// EclipseLink 2.1.1 ( 1): ?
// EclipseLink 2.2.0 ( 1): ?
// Hibernate 3.6 (+1): correct Player instance
Player pl2 = em.find(Player.class, 2);
System.out.println("Loaded player = " + pl2);
// EclipseLink 2.1.1 ( 1): ?
// EclipseLink 2.2.0 ( 1): ?
// Hibernate 3.6 (-1): wrong null
Referee re2 = em.find(Referee.class, 2);
System.out.println("Loaded referee = " + re2);
// EclipseLink 2.1.1 ( 1): ?
// EclipseLink 2.2.0 ( 1): ?
// Hibernate 3.6 (+1): correct null
Coach ch2 = em.find(Coach.class, 2);
System.out.println("Loaded coach = " + ch2);
System.out.println();
System.out.println();
// Blake Griffin: the person who's also a player, referee, *and* coach
// EclipseLink 2.1.1 ( 1): ?
// EclipseLink 2.2.0 ( 1): ?
// Hibernate 3.6 (-1): wrong Player instance
Person pe3 = em.find(Person.class, 3);
System.out.println("Loaded person = " + pe3);
// EclipseLink 2.1.1 ( 1): ?
// EclipseLink 2.2.0 ( 1): ?
// Hibernate 3.6 (+1): correct Player instance
Player pl3 = em.find(Player.class, 3);
System.out.println("Loaded player = " + pl3);
// EclipseLink 2.1.1 ( 1): ?
// EclipseLink 2.2.0 ( 1): ?
// Hibernate 3.6 (-1): wrong null
Referee re3 = em.find(Referee.class, 3);
System.out.println("Loaded referee = " + re3);
// EclipseLink 2.1.1 ( 1): ?
// EclipseLink 2.2.0 ( 1): ?
// Hibernate 3.6 (-1): wrong null
Coach ch3 = em.find(Coach.class, 3);
System.out.println("Loaded coach = " + ch3);
EclipseLink 2.1.1和2.2.0每晚构建在EntityManager.find(...)上完全失败,而Hibernate至少会返回一些预期的实例。请注意“预期”一词。这是我对JPA ORM应该做什么的理解,但我可能会弄错。
如您所见,Hibernate总是返回Player的子类实例,但如果Player实例也可用,则无法找到Coach和Referee实例。
那么,JPA如何最好地处理这个模型?
答案 0 :(得分:5)
此外,Person / s上没有鉴别器,因为继承是不相交的。
据我所知,JPA的继承是 strict ,即一个实体属于一个单一的层次结构(一个人是一个玩家或一个裁判,而不是两个,所以只有一个相应的记录在子表)。换句话说,我不认为非脱节继承是支持的(JPA规范中的没有表示顺便说一句)或甚至可能(稍后会详细介绍) )。
你是否使用带有JOINED
策略的鉴别器并不意味着继承是非脱节的,它只是被某些持久性提供者用作提示。
实际上,虽然JPA规范建议(在第11.1.10节“DiscriminatorColumn Annotation”中)应该能够使用Discriminator
和JOINED
策略,但Hibernate甚至不支持它虽然这可以使用XML映射,如ANN-140中所述。 JPA维基书总结如下:
有些JPA提供程序支持带或不带鉴别器列的连接继承,有些需要鉴别器列,有些则不支持鉴别器列。因此,联合继承似乎尚未完全标准化。
如果你看看Hibernate生成的SQL,你会看到它执行outer join
与case
混合来检测和实例化“正确”类型。例如,如果Project
有SmallProject
和LargeProject
作为子类,则from Project
会给出:
select
project0_.id as id66_,
project0_.name as name66_,
project0_1_.budget as budget67_,
case
when project0_1_.id is not null then 1
when project0_2_.id is not null then 2
when project0_.id is not null then 0
else -1
end as clazz_
from
Project project0_
left outer join
LARGEPROJECT project0_1_
on project0_.id=project0_1_.id
left outer join
SMALLPROJECT project0_2_
on project0_.id=project0_2_.id
显然,上面的问题与非脱节继承有关,即两个子表中的行都是相同的ID。
但无论Hibernate如何实现JOINED
策略,我认为非脱节继承更通常只是不可能给定持久化上下文如何工作,在持久化上下文中,您不能拥有Player
实例和Coach
实例具有相同的id
,您不能拥有多个对象来表示数据库的同一行,这会破坏JPA的州管理模式。
Person pe1 = em.find(Person.class, 1);
失败了Hibernate。它绝对适用于早期版本。我想知道你得到的“Player
实例”。
答案 1 :(得分:1)
我认为JPA和Java根本不会很好地处理这个类层次结构。
更好的方法是将人与角色分开并拥有一个包含Set的Person类,并让Coach,Referee和Player扩展Role而不是Person。
然后,角色和子类可能以更正常的方式映射,因为它们形成不相交的层次结构,并且具有多个角色的人以可以在Java中的单个Person对象中保存的方式表示,角色映射为JPA中的集合。