jpa繼承關係詳解
阿新 • • 發佈:2019-01-08
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。
物件使用引用以便關聯到其它物件;關係型資料庫表之間採用外來鍵來描述表的關係。在關係型資料庫中通常沒有自然且有效的方法來描述類的繼承關係。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
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
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。