JPA仓库与单表继承(Hibernate)

4

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

@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类,自动装配了这三个通用存储库的实例,并为每个类编写了特定的方法。

@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();
    }
...


我的问题是,当我尝试查找所有普通员工或合同员工时,总是会得到所有类型的员工(员工、普通员工和合同员工)。我不知道为什么会这样,即使该方法的签名表明它返回了适当的类型。

2
你为子类设置了哪些鉴别器值? - J. Doe
对于 RegularEmployee,我设置了 @DiscriminatorValue("regularemployee"),而对于 ContractEmployee,我设置了 @DiscriminatorValue("contractemployee") - whatspoppin
请分享RegularEmployee和ContractEmployee实体的代码。 - dassum
2个回答

4

其中一种选择是在 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已经知道它是一个Repository,因为它扩展了JpaRepository
顺便提一下:如果您不需要Spring创建EmployeeRepository,请在其类顶部添加@NoRepositoryBean

有必要创建 RegularEmployeeRepository 吗?我为什么不能使用通用接口!!!我试图理解它的工作原理,想象一下有多个子实体扩展了顶级实体,我不想为每个子实体都创建一个存储库。 - whatspoppin
1
@whatspoppin 不是必须的,我已经更新了我的答案。但是,如果您需要按子类的特定属性进行查询,则创建其他存储库可能更有意义。 - Marc

1

我已经能够使用你的通用员工库复制你遇到的问题。作为替代方案,我创建了两个独立的库:合同员工库和普通员工库。

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://dev59.com/umIk5IYBdhLWcg3wTcUr#19443031他已经很好地解释了它。


我尝试了您的实现,它有效,但为什么使用“T extends Employee”的通用接口不按照预期工作。想象一下多重继承和实体。 - whatspoppin
关于这个问题,我看到了这个答案(在Spring Data JPA存储库中使用泛型),其中Product有两个子类:Wine和Car(在我们的情况下是Employee:Regular和Contractual)https://dev59.com/umIk5IYBdhLWcg3wTcUr#19443031。我将引用他的部分回答:“这是因为反射查找永远不可能产生Wine或Car,除非您创建一个专用的存储库接口来捕获具体类型信息。” - emyasa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接