天天看點

OpenJPA (5)

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 政策為類的繼承體系采用同一個表。表名是基類的名稱。例如:

@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建立的表結構如下:

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    |                |
+--------------+--------------+------+-----+---------+----------------+ 
           
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):

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建立表的時候,這個屬性才被使用。

   以下是個簡單的例子:

@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建立的表結構如下:

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    |       |
+--------------+--------------+------+-----+---------+-------+
           
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表的資料如下:

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實作隻需要同時查詢子類映射的表。

    以下是個簡單的例子:

@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建立的表結構如下:

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    |                |
+--------------+--------------+------+-----+---------+----------------+
           
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表的資料如下: 

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。