代码之家  ›  专栏  ›  技术社区  ›  Kawu

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

  •  3
  • Kawu  · 技术社区  · 14 年前

    部分 非不相交的 带有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 ORMs中可能吗?注释看起来怎么样?

    // 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和jurier实例。

    2 回复  |  直到 6 年前
        1
  •  5
  •   Pascal Thivent    14 年前

    此外,由于继承是非分离的,因此对Person/s没有歧视。

    据我所知,JPA的遗产是 严格的 i、 e.一个实体属于一个单一的层级(一个人要么是球员,要么是裁判,而不是两者都是,因此子表中只有一条对应的记录)。换句话说,我不认为 非不相交的 支持继承( 在JPA规范中说,这是顺便说一句)或者甚至是可能的(稍后将详细介绍)。

    以及你是否使用一个鉴别器 JOINED 策略与否并不意味着继承 非不相交的 或者不,这只是一些持久性提供者的一个提示。

    实际上,虽然JPA规范建议(在第11.1.10节“DiscriminatorColumn注释”)应该能够使用 Discriminator 加入 策略,Hibernate甚至不支持它,尽管这可以使用XML映射,如 ANN-140

    有些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如何实现 策略,我想 非不相交的 继承通常是公正的 不可能 Player 实例和 Coach 相同的实例 id 多个对象表示数据库的同一行 ,这将打破JPA的状态管理模型。

    我很惊讶第一次考试 Person pe1 = em.find(Person.class, 1); 玩家 你得到的实例。

    工具书类

        2
  •  1
  •   Don Roby    14 年前

    最好的办法是把人和角色分开,让一个包含集合的单人类,让教练、裁判和球员代替人来扩展角色。

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