JPA如何处理部分,非不相交的继承(在每表类方案中使用InheritanceType.JOINED)以及EntityManager.find()?

时间:2010-11-10 22:55:39

标签: hibernate inheritance 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);

我缩短的实体类是:

@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如何最好地处理这个模型?

2 个答案:

答案 0 :(得分:5)

  

此外,Person / s上没有鉴别器,因为继承是不相交的。

据我所知,JPA的继承是 strict ,即一个实体属于一个单一的层次结构(一个人是一个玩家或一个裁判,而不是两个,所以只有一个相应的记录在子表)。换句话说,我不认为非脱节继承是支持的(JPA规范中的没有表示顺便说一句)或甚至可能(稍后会详细介绍) )。

你是否使用带有JOINED策略的鉴别器并不意味着继承是非脱节的,它只是被某些持久性提供者用作提示。

实际上,虽然JPA规范建议(在第11.1.10节“DiscriminatorColumn Annotation”中)应该能够使用DiscriminatorJOINED策略,但Hibernate甚至不支持它虽然这可以使用XML映射,如ANN-140中所述。 JPA维基书总结如下:

  

有些JPA提供程序支持带或不带鉴别器列的连接继承,有些需要鉴别器列,有些则不支持鉴别器列。因此,联合继承似乎尚未完全标准化。

如果你看看Hibernate生成的SQL,你会看到它执行outer joincase混合来检测和实例化“正确”类型。例如,如果ProjectSmallProjectLargeProject作为子类,则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的州管理模式。

PS:我很惊讶第一次测试Person pe1 = em.find(Person.class, 1);失败了Hibernate。它绝对适用于早期版本。我想知道你得到的“Player实例”。

参考

答案 1 :(得分:1)

我认为JPA和Java根本不会很好地处理这个类层次结构。

更好的方法是将人与角色分开并拥有一个包含Set的Person类,并让Coach,Referee和Player扩展Role而不是Person。

然后,角色和子类可能以更正常的方式映射,因为它们形成不相交的层次结构,并且具有多个角色的人以可以在Java中的单个Person对象中保存的方式表示,角色映射为JPA中的集合。