使用單表繼承子類型查詢 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 上找到。