Kaw*_*awu 3 inheritance hibernate jpa eclipselink
我在使用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);
Run Code Online (Sandbox Code Playgroud)
我缩短的实体类是:
@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;
...
}
Run Code Online (Sandbox Code Playgroud)
部分继承:人员实体可以在没有任何相应的裁判,教练或球员实体的情况下存在
非不相交的继承:可以有任意数量的相应子实体,即一个人可以同时是裁判,教练和玩家,可能是三者的任意组合.每个子表只能有一个或一个实体,但不能是多个.
请注意,因为继承是部分的,所以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);
Run Code Online (Sandbox Code Playgroud)
EclipseLink 2.1.1和2.2.0每晚构建在EntityManager.find(...)上完全失败,而Hibernate至少返回一些预期的实例.请注意这里的" 预期 " 一词.这是我对JPA ORM应该做什么的理解,但我可能会弄错.
如您所见,Hibernate总是返回Player的子类实例,但如果Player实例也可用,则无法找到Coach和Referee实例.
那么,JPA如何最好地处理这个模型?
此外,Person/s上没有鉴别器,因为继承是非不相交的.
据我所知,JPA的继承是严格的,即实体属于单个层次结构(Person是Player或Referee,而不是两者,因此子表中只有一个相应的记录).换句话说,我不认为支持非不相交的继承(JPA规范中没有任何内容表示它是顺便说一句)或者甚至可能(稍后会详细介绍).
无论你是否使用带有JOINED策略的鉴别器并不意味着继承是非不相交的,这只是一些持久性提供者的提示.
实际上,虽然JPA规范建议(在第11.1.10节"DiscriminatorColumn Annotation"中)应该能够使用Discriminator带JOINED策略的,但Hibernate甚至不支持它,尽管这可以使用XML映射,如ANN中所报告的那样.-140.JPA维基书总结如下:
一些JPA提供程序支持带或不带鉴别器列的连接继承,有些需要鉴别器列,有些不支持鉴别器列.因此,联合继承似乎尚未完全标准化.
如果你看一下Hibernate生成的SQL,你会发现它outer join与a混合执行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
Run Code Online (Sandbox Code Playgroud)
显然,上述内容对于非不相交的继承是有问题的,即两个子表中的行对于相同的ID.
但是无论Hibernate如何实现JOINED策略,我认为非脱节继承更普遍是不可能的,因为持久化上下文如何工作,你不能在持久化上下文中拥有一个Player实例和一个Coach实例id,你不能有多个对象代表数据库的同一行,这将破坏JPA的状态管理模型.
PS:我对Person pe1 = em.find(Person.class, 1);Hibernate 的第一次测试失败感到惊讶.它绝对适用于早期版本.我想知道Player你得到的"什么" 实例.