【问题标题】:JPA repository with single table inheritance (hibernate)具有单表继承的 JPA 存储库(休眠)
【发布时间】:2020-12-18 17:29:03
【问题描述】:

我创建了两个扩展 Employee 实体的实体(RegularEmployeeContactEntity)。

@Entity
@Table(name="employees")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING)
@DiscriminatorValue(value="employee")
public class Employee {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

...

我为此实现使用SINGLE_TABLE 继承,并创建了一个通用的 JpaRepository 来操作数据:

@Repository
public interface EmployeeRepository<T extends Employee> extends JpaRepository<T, Long> {
}

我还创建了自动装配这些通用存储库的三个实例的服务类,以及每个类的特定方法。

@Service
public class EmployeeService {

    @Autowired
    private EmployeeRepository<Employee> employeeRepo;

    @Autowired
    private EmployeeRepository<RegularEmployee> regularRepo;

    @Autowired
    private EmployeeRepository<ContractEmployee> contractRepo;

    public List<Employee> getAllEmployee() {
        return employeeRepo.findAll();
    }

    public List<RegularEmployee> getAllRegularEmployee(){
        return regularRepo.findAll();
    }

    public List<ContractEmployee> getAllContractEmployee() {
        return contractRepo.findAll();
    }
...

我的问题是,当我尝试查找所有正式员工或合同工时,我总是会找到所有类型的员工(员工、正式员工和合同工)。

我不知道它为什么会这样,尽管方法的签名说它返回适当的类型。

【问题讨论】:

  • 您为子类设置了哪些鉴别器值?
  • 对于RegularEmployee,我设置@DiscriminatorValue("regularemployee"),对于ContractEmployee,我设置@DiscriminatorValue("contractemployee")
  • 分享RegularEmployee和ContractEmployee实体的代码。

标签: java spring hibernate jpa


【解决方案1】:

我已经能够使用您的通用 EmployeeRepository 复制您遇到的内容。作为替代方案,我创建了两个单独的存储库:ContractualEmployeeRepository 和 RegularEmployeeRepository。

public interface ContractualEmployeeRepository extends JpaRepository<ContractualEmployee, String> {
}

public interface RegularEmployeeRepository extends JpaRepository<RegularEmployee, String> {
}

然后,我创建了一个集成测试。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {Main.class})
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class,
        TransactionalTestExecutionListener.class,
        DbUnitTestExecutionListener.class})
@TestPropertySource(locations="classpath:application-test.properties")
@DatabaseSetup("classpath:SingleTableDataSet.xml")
public class IntegrationTest {

    @Autowired
    private RegularEmployeeRepository regularEmployeeRepository;

    @Autowired
    private ContractualEmployeeRepository contractualEmployeeRepository;

    @Test
    public void test() {
        Assert.assertEquals(6, regularEmployeeRepository.findAll().size());
        Assert.assertEquals(4, contractualEmployeeRepository.findAll().size());
    }

}

它有效。

关于 Spring Data JPA 存储库中泛型的使用和限制:https://stackoverflow.com/a/19443031/14180014他解释得很好。

【讨论】:

  • 我已经尝试过您的实现,它可以工作,但是为什么使用T extends Employee 的通用接口无法正常工作。想象一下有多个继承和实体
  • 关于这一点,我遇到了这个答案(在 Spring Data JPA 存储库中使用泛型),其中产品有 2 个子类:Wine 和 Car(在我们的例子中是 Employee:Regular 和 Contractual)stackoverflow.com/a/19443031/14180014我将在这里引用他的部分回答“这是因为反射查找永远无法生成 Wine 或 Car,除非您为其创建专用的存储库接口来捕获具体的类型信息。”跨度>
【解决方案2】:

一种选择是在EmployeeRepository 中使用@Query

public interface EmployeeRepository<T extends Employee> extends JpaRepository<T, Long> {
    @Query("from RegularEmployee")
    List<RegularEmployee> findAllRegularEmployees();
}

第二种选择是为Employee 的每个子类创建一个附加存储库。对于RegularEmployee 将是:

public interface RegularEmployeeRepository extends EmployeeRepository<RegularEmployee>{}

这是在EmployeeService 中使用这两个选项的方法:

@Service
public class EmployeeService {
    @Autowired EmployeeRepository<Employee> employeeRepo;

    @Autowired EmployeeRepository<RegularEmployee> regularRepoT;

    @Autowired RegularEmployeeRepository regularRepo;

    @PostConstruct
    public void init(){
        employeeRepo.save(new ContractEmployee("Mark"));
        employeeRepo.save(new RegularEmployee("Luke"));
        employeeRepo.findAll().forEach(System.out::println); // prints Mark and Luke
        regularRepo.findAll().forEach(System.out::println); // prints only Luke
        regularRepoT.findAllRegularEmployees().forEach(System.out::println); // prints only Luke
    }
//...
}

您也可以在EmployeeRepository 之上省略@Repository。 Spring 已经知道这是一个存储库,因为它扩展了 JpaRepository

旁注:如果您不需要由 Spring 创建 EmployeeRepository,请在其类的顶部添加 @NoRepositoryBean

【讨论】:

  • 是否需要创建RegularEmployeeRepository,为什么我不能使用通用接口!!!我试图了解它是如何工作的,图像具有多个扩展顶级实体的子实体,我不会为每个子实体创建存储库
  • @whatspoppin 没必要,我更新了我的答案。但是,如果您需要通过子类的特定属性进行查询,则创建额外的存储库可能更有意义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-10-04
  • 2021-03-16
  • 2023-03-14
  • 1970-01-01
  • 2019-07-05
  • 2013-09-15
  • 1970-01-01
相关资源
最近更新 更多