英文:
JPA repository with single table inheritance (hibernate)
问题
我已经创建了两个实体(RegularEmployee 和 ContactEntity),它们都继承自 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();
    }
...
我的问题是,当我尝试查找所有常规员工或合同员工时,我总是会得到所有类型的员工(包括员工、常规员工和合同员工)。
我不知道为什么会出现这种情况,尽管方法的签名表明它应该返回适当的类型。
英文:
I have created two entites (RegularEmployee and ContactEntity) that extends the Employee entity.
@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;
...
Im using SINGLE_TABLE inheritance for this implementations, and created a generic JpaRepository for manipulating data:
@Repository
public interface EmployeeRepository<T extends Employee> extends JpaRepository<T, Long> {
}
I've created also the Service class that autowire three instance of these generic repositories, and specific methods for each class.
@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();
    }
...
My problem is, that when I try to find all regular employees or contract employees, I always get all type of employees (employees, regular employees and contract employees all together).
I do not know why it behaves like this, even though the method's signature says it returns the appropriate type.
答案1
得分: 4
在`EmployeeRepository`中,一种选择是使用`@Query`:
```java
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); // 打印 Mark 和 Luke
        regularRepo.findAll().forEach(System.out::println); // 仅打印 Luke
        regularRepoT.findAllRegularEmployees().forEach(System.out::println); // 仅打印 Luke
    }
    //...
}
另外,你可以在EmployeeRepository的类顶部省略@Repository。Spring 已经知道它是一个存储库,因为它扩展了JpaRepository。
附注:如果你不希望Spring创建EmployeeRepository,可以在其类顶部添加@NoRepositoryBean。
注意:代码中的一些符号可能在显示上会有差异,但我已经尽力保留了原始代码的结构和内容。如果需要进一步的帮助,请随时提问。
<details>
<summary>英文:</summary>
One option is to use `@Query` in `EmployeeRepository`:
    public interface EmployeeRepository<T extends Employee> extends JpaRepository<T, Long> {
        @Query("from RegularEmployee")
        List<RegularEmployee> findAllRegularEmployees();
    }
A second option is to create an additional repository for each subclass of `Employee`. For `RegularEmployee` would be:
    public interface RegularEmployeeRepository extends EmployeeRepository<RegularEmployee>{}
This is how to use both options in `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
        }
    //...
    }
Also you can omit `@Repository` on top of `EmployeeRepository`. Spring already knows that is a Repository because it extends `JpaRepository`.
**Side note**: if you don't need `EmployeeRepository` to be created by Spring add `@NoRepositoryBean` on top of its class.
  [1]: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query.spel-expressions
</details>
# 答案2
**得分**: 1
我已经能够使用您的通用EmployeeRepository复制您遇到的问题。作为一种替代方法,我创建了两个单独的存储库:ContractualEmployeeRepository和RegularEmployeeRepository。
```java
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 他在解释方面做得非常出色。
英文:
I've been able to replicate what you've encountered using your generic EmployeeRepository. As an alternative I created two separate repositories: ContractualEmployeeRepository and RegularEmployeeRepository.
public interface ContractualEmployeeRepository extends JpaRepository<ContractualEmployee, String> {
}
public interface RegularEmployeeRepository extends JpaRepository<RegularEmployee, String> {
}
Then, I created an integration test.
@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());
    }
}
and it works.
As for the usage and limitations of Generics in Spring Data JPA repositories: https://stackoverflow.com/a/19443031/14180014 He had done a great job explaining it.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论