使用單表繼承子類型查詢 JPA 儲存庫
1. 概述
在開發基於 Java 的應用程式時,JPA 是一個有用的工具。我們知道Java中的繼承,但是當我們在JPA實體中擁有繼承時,JPA提供了多種處理繼承的策略。我們可以將資料儲存在單一表格、連接表或每個子類別實體的表中。
在本教程中,我們將研究在單一表中儲存子類型的資料。
2. 單表繼承子類型
在 JPA 中,可以使用註解@Inheritance(strategy = InheritanceType.SINGLE_TABLE)來配置單表繼承。這意味著單一表格儲存繼承層次結構中的所有實體。此表有一個鑑別器列來區分不同的實體類型(子類型) 。我們可以使用 JPA 儲存庫查詢特定的子類型。
讓我們考慮一個以Employee作為基底類別的繼承層次結構:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="type")
public abstract class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// ...more fields, getter and setters
}
接下來,讓我們建立子類別PermanentEmployee和ContractEmployee來擴充Employee類別:
@Entity
@DiscriminatorValue("PERM")
public class PermanentEmployee extends Employee {
private int employeeId;
// ...more fields, getter and setters
}
@Entity
@DiscriminatorValue("CNTR")
public class ContractEmployee extends Employee {
private int contractPeriod;
// ...more fields, getter and setters
}
註釋@Inheritance定義基底類別Employee中的繼承。註解的策略屬性指派給InheritanceType.SINGLE_TABLE 。此分配指示 Hibernate 將PermanentEmployee和ContractEmployee子類別的記錄儲存在單一表中。
此外,註解@DiscriminatorColumn為子類別定義了鑑別器列。鑑別器列是屬於兩個不同子類別的行的識別碼。註釋@DiscriminatorColumn有一個屬性name來設定鑑別器列名稱。
在此範例中,我們將鑑別器列名稱設定為「 type 」。當我們使用單一表格來儲存來自多個實體子類別的記錄時,此註解可以幫助我們識別該記錄屬於主實體的哪個子類型。
在給定的範例中,類別Employee有一個名為type鑑別器列。
子類別或子類型PermanentEmployee和ContractEmployee中的註解@DiscrimnatorValue定義該特定子類別的鑑別器列的值。
在範例中, PermanentEmployee的所有記錄的鑑別器列type的值為「 PERM' 。對於ContractEmpoyee類,該值為 ' CNTR' 。
3. 使用 JPA Repoistory 進行查詢
3.1.儲存庫類
現在,讓我們為繼承層次結構中的基底類別和子類別建立儲存庫類別。這些類別使我們能夠使用 Spring Data 的 JPA Repository 功能進行查詢。
我們只需要擴充JpaRepository介面:
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}
擴充JpaRepository介面為我們提供了基本查詢,例如save 、 findAll 、 findById, delete,等等。
同樣,讓我們為子類別PermanentEmployee和ContractEmployee建立儲存庫介面:
public interface PermanentEmployeeRepository extends JpaRepository<PermanentEmployee, Long> {
}
public interface ContractEmployeeRepository extends JpaRepository<ContractEmployee, Long> {
}
3.2.持久化配置
現在,我們需要在配置類別PersistenceConfig中聲明EnityManagerFactory bean,並為 Hibernate 庫新增 JPA 供應商適配器:
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan("com.baeldung.jpa.subtypes.entity");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
}
LocalContainerEntityManagerFactoryBean是一個 Spring 類,它為 JPA 的 EntityManagerFactory 提供FactoryBean EntityManagerFactory.它允許我們配置 JPA 提供者、資料來源、實體包等。
3.3.使用 JPA 儲存庫進行查詢
為了查詢基底類別和子類別的數據,我們在服務類別中自動組裝儲存庫類別:
@Autowired
EmployeeRepository empRepository;
@Autowired
PermanentEmployeeRepository permEmpRepository;
@Autowired
ContractEmployeeRepository contrEmpRepository;
讓我們為子類型添加一些範例資料:
PermanentEmployee perm1 = new PermanentEmployee(10, "John", 48);
permEmpRepository.save(perm1);
ContractEmployee contr1 = new ContractEmployee(180, "Mitchell", 23);
contrEmpRepository.save(contr1);
現在,基於單表配置、 PermanentEmployeeRepository,和ContractEmployeeRepository,將記錄插入到單表Employee中。讓我們來看看Employee表的一些範例資料:
| ID | 姓名 | 年齡 | 員工ID | 合約期間 | 類型 |
|---|---|---|---|---|---|
| 1 | 約翰 | 48 | 10 | 無效的 | 永久居留許可 |
| 2 | 米切爾 | 23 | 無效的 | 180 | 碳奈米管 |
正如我們從資料庫的範例資料中看到的, PermanentEmployee類型的記錄沒有特定於ContractEmployee的屬性值,反之亦然。鑑別器列TYPE標識記錄的子類別類型。
可以使用EmployeeRepository檢索employee表中的所有記錄:
List<Employee> empList = empRepository.findAll()
我們使用permEmpRepository從employee表中只取得PermanentEmployee :
List<PermanentEmployee> perEmpList = permEmpRepository.findAll();
類似地, contrEmpRepository從員工表中只取得ContractEmployee :
List<ContractEmployee> contrList = contrEmpRepository.findAll();
3.4.使用自訂查詢
我們可以使用自訂 JPA 查詢來查詢子類別資料。基於鑑別器列建立篩選器查詢為我們提供對應子類別的資料:
@Query("SELECT e FROM Employee e WHERE type(e) = 'PERM' AND e.employeeId < :idlimit "
+ "AND e.name LIKE :prefix% ")
List<PermanentEmployee> filterPermanent(@Param("idlimit") int idlimit, @Param("prefix") String prefix);
@Query("SELECT e FROM Employee e WHERE type(e) = 'CNTR' AND e.contractPeriod < :period "
+ "AND e.name LIKE :prefix% ")
List<ContractEmployee> filterContract(@Param("period") int period, @Param("prefix") String prefix);
在我們的範例中,基底類別Employee配置有區分列type 。同一列用於查詢特定子類別的資料。
在這裡,在方法filterPermanent()中,使用自訂查詢,我們過濾具有列type值「 PERM 」、 employeeId小於參數idlimit以及name以參數前綴開頭的PermanentEmployee記錄prefix.
類似地,在方法filterContract()中,我們根據列type 、 contractPeriod和name過濾ContractEmployee記錄。
基礎儲存庫EmployeeRepository,中使用的自訂查詢使我們能夠使用單一儲存庫,處理單一表中所有子類型的記錄。
4. 結論
在本文中,我們了解如何使用子類型的單表配置來處理 JPA 中的繼承,以及如何使用 Spring Data JPA 儲存庫查詢資料。單表儲存繼承中所有子類別的記錄,鑑別器列區分子類別的記錄。
與往常一樣,本文中使用的完整程式碼可以在 GitHub 上找到。