【问题标题】:How to handle Runtime Polymorphism in Spring Boot?如何处理 Spring Boot 中的运行时多态性?
【发布时间】:2022-01-16 12:21:23
【问题描述】:

我有抽象类 Employee

public abstract class Employee {

private int employeeId;

private String name;
}

我还有两个扩展Employee 的具体类,即OfficeEmployeeHomeEmployee,它们目前是空的。 这是我的控制器:

@RestController
@RequestMapping("/api/employee")
public class EmployeeController {

@Autowired
private EmployeeService employeeService;

@PostMapping("/office")
public EmployeeResponse saveOfficeEmployee(@RequestBody OfficeEmployee request) {
    return employeeService.save(request);
}

@PostMapping("/home")
public EmployeeResponse saveHomeEmployee(@RequestBody HomeEmployee request) {
    return employeeService.save(request);
 }
}

最后是 EmployeeService 类:

@Service
public class EmployeeService {

@Autowired
private EmployeeRepository employeeRepository;

  public Employee save(Employee request) {
  // here i think i should do something like this: Employee employee = new OfficeEmployee or
  // Employee employee = new HomeEmployee();
    Employee employee = employeeRepository.save(employee);
    return employee;
 }
}

如何确定我从POST 请求中得到了哪些员工?我处理这个问题有错吗?

【问题讨论】:

    标签: java spring rest polymorphism


    【解决方案1】:

    是的,抽象类实体是我用来在多个表中添加公共列的东西。 例如,如果我想在许多表上添加 createdDate 和 updatedDate,我将在抽象实体类中定义这 2 列(例如称为 BaseDateEntity),并在我想使用它的所有实体类中继承它。还要用@MappedSuperclass 注释基实体类。但是存储库应该是每个特定的实体类。您不能为继承抽象 Employee 实体的所有实体使用 1 个存储库,否则查询将在您的超类 baseentity(=Employee) 的所有子类实体(OfficeEmployee、HomeEmployee、XyzEmployee、..)上执行,这可能就足够了时不时的。

    您的实体代码的粗略示例。

    import java.persistence.MappedSuperclass;
    @MappedSuperclass
    public abstract class Employee { //body skipped for brevity}
    

    还有一个选择。在您的基础实体上使用 @Entity 和 @DiscriminatorColumn。 并在您的子实体上使用 @Entity 和 @DiscriminatorValue。

    @Entity
    @DiscriminatorColumn
    @Inheritance(strategy=InheritanceType.JOINED) //more explanation on this below
    public abstract class Employee {//body skipped for brevity}
    
    @Entity
    @DiscriminatorValue("Officeemployee")
    public class OfficeEmplyee extends Employee {}
    

    您不能同时使用 MappedSuperclass 和 Entity。所以选择一个。

    您的存储库的粗略示例

    public interface EmployeeRepository<T extends Employee> extends JpaRepository<T, Long>{}
    
    public interface OfficeEmployeeRepository extends JpaRepository<OfficeEmployee,Long>{}
    

    显然我跳过了 HomeEmployee 的代码示例,因为它与 OfficeEmployee 相同。

    此外,如果您不想专门针对 OfficeEmployees 进行查询,则不需要 OfficeEmployeeRepository。如果您总是查询Employees 的所有子类,那么您只需要EmployeeRepository。但是在这种情况下,我认为您需要 EmployeeRepository 来进行一般员工查询,还需要 OfficeRepository 和 HomeRepository 来查询特定类型的员工

    要进一步解释 MappedSuperclass 方法和 DiscriminatorValue 方法之间的区别,您必须考虑 DB 中的表。

    在您不想为父(抽象)实体对象创建另一个表的简单情况下,使用 MappedSuperclass 会简单得多。它只是将抽象父实体中描述的附加列映射(添加)到子实体上。在我通常的用例(createdDate,updadedDate 列)中,这是更好的方法,因为没有理由为所有已创建的数据集建立表 createdDate&updatedDate 列。 (所有帖子、公告、cmets、线程、重新回复、A2A 等的表格?没有意义)

    但是,在您的情况下,您可能希望保留一张包含各种员工的表格。在这种情况下,请使用 discriminatorcolumn & discriminatorvalue 方法。这就是 @Inheritance(strategy=) 注释发挥作用的地方。

    如果@Inheritance 不存在,默认继承策略是SINGLE_TABLE。这是不言自明的imo。所有子类实体列也被添加到这个超类(抽象实体)表中。它将创建一个巨大的 Employee 表。由于它不需要连接查询,因此查询起来更快更简单。但缺点是表格很大,而且会有很多空值。 (如果 OfficeWorker 有名为 'OfficeLocation' 的列而 HomeWorker 没有,那么在巨大的 Employee 表中,每个 HomeWorker 行都会有 OfficeLocation=null。)

    我上面使用的是JOINED策略。也是不言自明的。制作一张所有 Employee 的表格,一张所有 OfficeWorker 的表格,一张所有 H​​omeWorker 的表格。但是在这种情况下,Employee 表只有公共的列值(id、name、..what not)和类型(OfficeWorker vs HomeWorker),以及用于连接查询 OfficeWorker 表和 HomeWorker 表的外键。

    最后一个选项是 TABLE_PER_CLASS。它不会生成所有员工的表。所以它与 MappedSuperclass 注释相同,但更详细。从不推荐。

    【讨论】:

    • 嗨,杰夫,感谢您提供有用的提示。但我认为这不能回答我的问题。您将如何处理 EmployeeService 类中的 save 方法?
    • 您还需要为每个员工类型设置单独的服务类。您可以为员工制作抽象服务类,并为每个特定的员工类型制作特定的服务类
    【解决方案2】:

    为什么您认为您需要确定任何事情? EmployeeRepository 已经具备处理所有类型的Employees 的能力。

    为此,Employee 必须是 @Entity。不过,它仍然可以是抽象的。

    附带说明一下,具有单独端点(/home/office)的替代方法是将@JsonTypeInfo 与一种可用策略一起使用,以从输入数据中确定Employee 子类型。

    【讨论】:

    • 嗨 crizzis,我可能没有很好地提出这个问题。在save 方法中,我有映射器,我需要从请求中复制属性,转换为实体(HomeEmployeeOfficeEmployee),将实体保存到数据库并返回响应对象。为了便于阅读,我缩短了问题。
    猜你喜欢
    • 2021-11-23
    • 2014-06-20
    • 2019-05-30
    • 1970-01-01
    • 2018-02-23
    • 1970-01-01
    • 2017-10-11
    • 1970-01-01
    相关资源
    最近更新 更多