`
tomrose
  • 浏览: 144530 次
  • 来自: ...
社区版块
存档分类
最新评论

jpa继承关系详解

    博客分类:
  • ejb
阅读更多
7 Inheritance
    对象使用引用以便关联到其它对象;关系型数据库表之间采用外键来描述表的关系。在关系型数据库中通常没有自然且有效的方法来描述类的继承关系。JPA通过Inheritance annotation提供了几种继承策略,它有以下属性:

InheritanceType strategy:用来声明继承策略。可选值是InheritanceType.SINGLE_TABLE、InheritanceType.JOINED和InheritanceType .TABLE_PER_CLASS。缺省值是InheritanceType.SINGLE_TABLE。
   关于Inheritance的更多内容,可以参考Hibernate实战by Christian Bauer, Gavin King。



7.1 Single Table
    InheritanceType.SINGLE_TABLE 策略为类的继承体系采用同一个表。表名是基类的名称。例如:

Java代码
@Entity 
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)  
public class Base {  
    @Id 
    private int id;  
      
    @Basic 
    private String baseName;  
}  
 
@Entity 
public class Derived1 extends Base {  
    @Basic 
    private String derived1Name;  
}  
 
@Entity 
public class Derived2 extends Base {  
    @Basic 
    private String derived2Name;  


@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class Base {
@Id
private int id;

@Basic
private String baseName;
}

@Entity
public class Derived1 extends Base {
@Basic
private String derived1Name;
}

@Entity
public class Derived2 extends Base {
@Basic
private String derived2Name;
}

   使用MappingTool建立的表结构如下:

Sql代码
mysql> describe base;  
+--------------+--------------+------+-----+---------+----------------+  
| Field        | Type         | Null | Key | Default | Extra          |  
+--------------+--------------+------+-----+---------+----------------+  
| id           | int(11)      | NO   | PRI | NULL    | auto_increment |  
| baseName     | varchar(255) | YES  |     | NULL    |                |  
| DTYPE        | varchar(255) | YES  | MUL | NULL    |                |  
| derived1Name | varchar(255) | YES  |     | NULL    |                |  
| derived2Name | varchar(255) | YES  |     | NULL    |                |  
+--------------+--------------+------+-----+---------+----------------+  

mysql> describe base;
+--------------+--------------+------+-----+---------+----------------+
| Field        | Type         | Null | Key | Default | Extra          |
+--------------+--------------+------+-----+---------+----------------+
| id           | int(11)      | NO   | PRI | NULL    | auto_increment |
| baseName     | varchar(255) | YES  |     | NULL    |                |
| DTYPE        | varchar(255) | YES  | MUL | NULL    |                |
| derived1Name | varchar(255) | YES  |     | NULL    |                |
| derived2Name | varchar(255) | YES  |     | NULL    |                |
+--------------+--------------+------+-----+---------+----------------+
Java代码
EntityManager em = entityManagerFactory.createEntityManager();  
em.getTransaction().begin();  
Base base = new Base();  
base.setBaseName("base");  
em.persist(base);  
Derived1 d1 = new Derived1();  
d1.setBaseName("derived1's base");  
d1.setDerived1Name("derived1");  
em.persist(d1);  
Derived2 d2 = new Derived2();  
d2.setBaseName("derived2's base");  
d2.setDerived2Name("derived2");  
em.persist(d2);  
em.getTransaction().commit();  
em.close(); 

EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
Base base = new Base();
base.setBaseName("base");
em.persist(base);
Derived1 d1 = new Derived1();
d1.setBaseName("derived1's base");
d1.setDerived1Name("derived1");
em.persist(d1);
Derived2 d2 = new Derived2();
d2.setBaseName("derived2's base");
d2.setDerived2Name("derived2");
em.persist(d2);
em.getTransaction().commit();
em.close();    以上代码执行后,数据库中base表的数据如下(其中DTYPE列由OpenJPA自动插入,用于区分不同的class,关于Discriminator的详细用法请参考OpenJPA User's Guide):Sql代码
mysql> select * from base;  
+----+-----------------+----------+--------------+--------------+  
| id | baseName        | DTYPE    | derived1Name | derived2Name |  
+----+-----------------+----------+--------------+--------------+  
|  1 | base            | Base     | NULL         | NULL         |  
|  2 | derived1's base | Derived1 | derived1     | NULL         |  
|  3 | derived2's base | Derived2 | NULL         | derived2     |  
+----+-----------------+----------+--------------+--------------+  

mysql> select * from base;
+----+-----------------+----------+--------------+--------------+
| id | baseName        | DTYPE    | derived1Name | derived2Name |
+----+-----------------+----------+--------------+--------------+
|  1 | base            | Base     | NULL         | NULL         |
|  2 | derived1's base | Derived1 | derived1     | NULL         |
|  3 | derived2's base | Derived2 | NULL         | derived2     |
+----+-----------------+----------+--------------+--------------+  
7.1.1 Advantages
    InheritanceType.SINGLE_TABLE 策略的优势在于简单且性能高(因为不需要使用连接查询等)。如果类的继承体系中,子类和父类间的差异主要在于行为,同时子类之间以及子类和父类之间的属性差异不大(例如子类不增加属性或者增加的属性数目比较少),那么适用于这个策略。

7.1.2 Disadvantages
    这个策略导致规范化级别降低。由于类继承体系中的每个类的属性都要映射到表的一列,因此当类的继承体系变的复杂的时候,表也随之变大。子类中属性对应的列必须声明为nullable。



7.2 Joined
    InheritanceType.JOINED策略为类继承体系中的每个类创建不同的表。每个表只包含类中定义的列,因此在load一个子类的时候,JPA实现需要同时查询子类映射的表,以及通过关联查询所有的父类映射的表。PrimaryKeyJoinColumn annotation用来指定子类映射的表如何关联到父类映射的表。它有以下属性:

String name: 子类映射表中的列名。如果只有一个identity filed,那么缺省使用这个field对应的列名。
String referencedColumnName: 父类映射表中用来关联的列名。如果只有一个identity filed,那么缺省使用这个field对应的列名。
String columnDefinition: 数据库中列的数据类型。只有当JPA vendor支持通过metadata创建表的时候,这个属性才被使用。
   以下是个简单的例子:

Java代码
@Entity 
@Inheritance(strategy=InheritanceType.JOINED)  
public class Base {  
    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY)  
    private int id;  
      
    @Basic 
    private String baseName;  
}  
 
@Entity 
@PrimaryKeyJoinColumn(name="id", referencedColumnName="id")  
public class Derived1 extends Base {  
    @Basic 
    private String derived1Name;  
}  
 
@Entity 
@PrimaryKeyJoinColumn(name="id", referencedColumnName="id")  
public class Derived2 extends Base {  
    @Basic 
    private String derived2Name;  


@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class Base {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;

@Basic
private String baseName;
}

@Entity
@PrimaryKeyJoinColumn(name="id", referencedColumnName="id")
public class Derived1 extends Base {
@Basic
private String derived1Name;
}

@Entity
@PrimaryKeyJoinColumn(name="id", referencedColumnName="id")
public class Derived2 extends Base {
@Basic
private String derived2Name;

   使用MappingTool建立的表结构如下:

Sql代码
mysql> describe base;  
+----------+--------------+------+-----+---------+----------------+  
| Field    | Type         | Null | Key | Default | Extra          |  
+----------+--------------+------+-----+---------+----------------+  
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |  
| baseName | varchar(255) | YES  |     | NULL    |                |  
+----------+--------------+------+-----+---------+----------------+  
 
mysql> describe derived1;  
+--------------+--------------+------+-----+---------+-------+  
| Field        | Type         | Null | Key | Default | Extra |  
+--------------+--------------+------+-----+---------+-------+  
| id           | int(11)      | NO   | PRI |         |       |  
| derived1Name | varchar(255) | YES  |     | NULL    |       |  
+--------------+--------------+------+-----+---------+-------+  
 
mysql> describe derived2;  
+--------------+--------------+------+-----+---------+-------+  
| Field        | Type         | Null | Key | Default | Extra |  
+--------------+--------------+------+-----+---------+-------+  
| id           | int(11)      | NO   | PRI |         |       |  
| derived2Name | varchar(255) | YES  |     | NULL    |       |  
+--------------+--------------+------+-----+---------+-------+ 

mysql> describe base;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| baseName | varchar(255) | YES  |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+

mysql> describe derived1;
+--------------+--------------+------+-----+---------+-------+
| Field        | Type         | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+-------+
| id           | int(11)      | NO   | PRI |         |       |
| derived1Name | varchar(255) | YES  |     | NULL    |       |
+--------------+--------------+------+-----+---------+-------+

mysql> describe derived2;
+--------------+--------------+------+-----+---------+-------+
| Field        | Type         | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+-------+
| id           | int(11)      | NO   | PRI |         |       |
| derived2Name | varchar(255) | YES  |     | NULL    |       |
+--------------+--------------+------+-----+---------+-------+

Java代码
EntityManager em = entityManagerFactory.createEntityManager();  
em.getTransaction().begin();  
Base base = new Base();  
base.setBaseName("base");  
em.persist(base);  
Derived1 d1 = new Derived1();  
d1.setBaseName("derived1's base");  
d1.setDerived1Name("derived1");  
em.persist(d1);  
Derived2 d2 = new Derived2();  
d2.setBaseName("derived2's base");  
d2.setDerived2Name("derived2");  
em.persist(d2);  
em.getTransaction().commit();  
em.close(); 

EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
Base base = new Base();
base.setBaseName("base");
em.persist(base);
Derived1 d1 = new Derived1();
d1.setBaseName("derived1's base");
d1.setDerived1Name("derived1");
em.persist(d1);
Derived2 d2 = new Derived2();
d2.setBaseName("derived2's base");
d2.setDerived2Name("derived2");
em.persist(d2);
em.getTransaction().commit();
em.close();    以上代码执行后,数据库中base表的数据如下:Sql代码
mysql> select * from base;  
+----+-----------------+  
| id | baseName        |  
+----+-----------------+  
|  1 | derived2's base |  
|  2 | derived1's base |  
|  3 | base            |  
+----+-----------------+  
 
mysql> select * from derived1;  
+----+--------------+  
| id | derived1Name |  
+----+--------------+  
|  2 | derived1     |  
+----+--------------+  
 
mysql> select * from derived2;  
+----+--------------+  
| id | derived2Name |  
+----+--------------+  
|  1 | derived2     |  
+----+--------------+ 

mysql> select * from base;
+----+-----------------+
| id | baseName        |
+----+-----------------+
|  1 | derived2's base |
|  2 | derived1's base |
|  3 | base            |
+----+-----------------+

mysql> select * from derived1;
+----+--------------+
| id | derived1Name |
+----+--------------+
|  2 | derived1     |
+----+--------------+

mysql> select * from derived2;
+----+--------------+
| id | derived2Name |
+----+--------------+
|  1 | derived2     |
+----+--------------+

7.2.1 Advantages
    InheritanceType. JOINED策略的优势在于数据库表中没有冗余字段,因此规范化级别比较高;当有新的子类加入到类的继承体系中时,已有表的schema无须修改。如果类的继承体系中,子类和父类间的差异不在于行为,同时子类间的属性差异比较大,那么适用于这个策略。

7.2.2 Disadvantages
    由于在查询的时候需要进行关联,那么查询的速度会比其它方式慢。此外可能需要多个插入和更新语句来处理多个表。



7.3 Table Per Class
    InheritanceType.TABLE_PER_CLASS策略为类继承体系中的每个类创建不同的表。和InheritanceType.JOINED策略不同的是,每个表中包含所有的子类和父类中定义的所有列。因此在load一个子类的时候,JPA实现只需要同时查询子类映射的表。
    以下是个简单的例子:

Java代码
@Entity 
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)  
public class Base {  
    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY)  
    private int id;  
      
    @Basic 
    private String baseName;  
}  
 
@Entity 
public class Derived1 extends Base {  
    @Basic 
    private String derived1Name;  
}  
 
@Entity 
public class Derived2 extends Base {  
    @Basic 
    private String derived2Name;  


@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public class Base {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;

@Basic
private String baseName;
}

@Entity
public class Derived1 extends Base {
@Basic
private String derived1Name;
}

@Entity
public class Derived2 extends Base {
@Basic
private String derived2Name;
}

   使用MappingTool建立的表结构如下:

Sql代码
mysql> describe base;  
+----------+--------------+------+-----+---------+----------------+  
| Field    | Type         | Null | Key | Default | Extra          |  
+----------+--------------+------+-----+---------+----------------+  
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |  
| baseName | varchar(255) | YES  |     | NULL    |                |  
+----------+--------------+------+-----+---------+----------------+  
 
mysql> describe derived1;  
+--------------+--------------+------+-----+---------+----------------+  
| Field        | Type         | Null | Key | Default | Extra          |  
+--------------+--------------+------+-----+---------+----------------+  
| id           | int(11)      | NO   | PRI | NULL    | auto_increment |  
| baseName     | varchar(255) | YES  |     | NULL    |                |  
| derived1Name | varchar(255) | YES  |     | NULL    |                |  
+--------------+--------------+------+-----+---------+----------------+  
 
mysql> describe derived2;  
+--------------+--------------+------+-----+---------+----------------+  
| Field        | Type         | Null | Key | Default | Extra          |  
+--------------+--------------+------+-----+---------+----------------+  
| id           | int(11)      | NO   | PRI | NULL    | auto_increment |  
| baseName     | varchar(255) | YES  |     | NULL    |                |  
| derived2Name | varchar(255) | YES  |     | NULL    |                |  
+--------------+--------------+------+-----+---------+----------------+ 

mysql> describe base;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| baseName | varchar(255) | YES  |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+

mysql> describe derived1;
+--------------+--------------+------+-----+---------+----------------+
| Field        | Type         | Null | Key | Default | Extra          |
+--------------+--------------+------+-----+---------+----------------+
| id           | int(11)      | NO   | PRI | NULL    | auto_increment |
| baseName     | varchar(255) | YES  |     | NULL    |                |
| derived1Name | varchar(255) | YES  |     | NULL    |                |
+--------------+--------------+------+-----+---------+----------------+

mysql> describe derived2;
+--------------+--------------+------+-----+---------+----------------+
| Field        | Type         | Null | Key | Default | Extra          |
+--------------+--------------+------+-----+---------+----------------+
| id           | int(11)      | NO   | PRI | NULL    | auto_increment |
| baseName     | varchar(255) | YES  |     | NULL    |                |
| derived2Name | varchar(255) | YES  |     | NULL    |                |
+--------------+--------------+------+-----+---------+----------------+ Java代码
EntityManager em = entityManagerFactory.createEntityManager();  
em.getTransaction().begin();  
Base base = new Base();  
base.setBaseName("base");  
em.persist(base);  
Derived1 d1 = new Derived1();  
d1.setBaseName("derived1's base");  
d1.setDerived1Name("derived1");  
em.persist(d1);  
Derived2 d2 = new Derived2();  
d2.setBaseName("derived2's base");  
d2.setDerived2Name("derived2");  
em.persist(d2);  
em.getTransaction().commit();  
em.close(); 

EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
Base base = new Base();
base.setBaseName("base");
em.persist(base);
Derived1 d1 = new Derived1();
d1.setBaseName("derived1's base");
d1.setDerived1Name("derived1");
em.persist(d1);
Derived2 d2 = new Derived2();
d2.setBaseName("derived2's base");
d2.setDerived2Name("derived2");
em.persist(d2);
em.getTransaction().commit();
em.close();    以上代码执行后,数据库中base表的数据如下:

Sql代码
mysql> select * from base;  
+----+----------+  
| id | baseName |  
+----+----------+  
|  1 | base     |  
+----+----------+  
 
mysql> select * from derived1;  
+----+-----------------+--------------+  
| id | baseName        | derived1Name |  
+----+-----------------+--------------+  
|  1 | derived1's base | derived1     |  
+----+-----------------+--------------+  
 
mysql> select * from derived2;  
+----+-----------------+--------------+  
| id | baseName        | derived2Name |  
+----+-----------------+--------------+  
|  1 | derived2's base | derived2     |  
+----+-----------------+--------------+ 

mysql> select * from base;
+----+----------+
| id | baseName |
+----+----------+
|  1 | base     |
+----+----------+

mysql> select * from derived1;
+----+-----------------+--------------+
| id | baseName        | derived1Name |
+----+-----------------+--------------+
|  1 | derived1's base | derived1     |
+----+-----------------+--------------+

mysql> select * from derived2;
+----+-----------------+--------------+
| id | baseName        | derived2Name |
+----+-----------------+--------------+
|  1 | derived2's base | derived2     |
+----+-----------------+--------------+

7.3.1 Advantages
    对于已知class类型的实例来说,这个策略十分有效。跟InheritanceType.JOINED策略类似,当有新的子类加入到类的继承体系中时,已有表的schema无须修改。

7.3.2 Disadvantages
    这个策略在处理多态关系的时候会存在很多限制,此时某个引用(或者集合中的引用)可能指向任何子类的实例。由于无法使用关联查询,因此在查询的时候可能需要使用多个SQL语句或者使用UNION。

分享到:
评论
1 楼 nforce_com 2011-04-02  

相关推荐

Global site tag (gtag.js) - Google Analytics