如何将JPA关系添加到持久实体的属性的子集列表?

时间:2019-07-10 21:25:13

标签: java hibernate jpa spring-data-jpa

具有以下实体:

@Entity
@Table
public class Employee {

  @Id
  @GeneratedValue(generator = "UUID")
  @GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
  private UUID id;

  @NotBlank
  private String firstName;

  @NotBlank
  private String lastName;

  private Gender gender;
  private Instant birthDate;
  private String email;
  private String corporateEmail;

  @Embedded
  private Address address;

  // and many more 
  // + all getters and setters
}

@Entity
@Table
public class Discipline {

  @Id
  @GeneratedValue(generator = "UUID")
  @GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
  private UUID id;

  @NotNull
  private Instant date;
  private String infraction;
}

Discipline用于特定的EmployeeEmployee可以具有0到几个学科。一门学科只适用于一个Employee,且不少于。在Discipline世界(微服务)中,它只需要完整Employee类中的一些属性(id,firstName和lastName)。在Employee世界(微服务)中,所有Employee字段都是相关的。

如何在不为每个Employee提取Discipline实体的情况下正确设置两个实体之间的关系,而只像投影一样提取必填字段?

我是否必须通过提取仅包含属性子集的超类来重构Employee实体?

在一个神奇的世界中,我希望Discipline实体定义如下:

@Entity
@Table
public class Discipline {

  @Id
  @GeneratedValue(generator = "UUID")
  @GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
  private UUID id;

  @NotNull
  private Instant date;
  private String infraction;

  @ManyToOne
  private EmployeeProfile employeeProfile;
}

public interface EmployeeProfile {

  UUID getId();
  String getFirstName();
  String getLastName();
}

EmployeeProfile的位置应类似于基于Spring Data JPA接口的投影所使用的位置。

这样做可以达到目标:

  • 在对我们的实体进行版本控制时,避免出现传入问题。实际上,我们不希望由于无关属性上的雇员实例过时而导致addDiscipline请求失败。如果我们将Discipline链接到完整的`Employee
  • ,则可能会发生这种情况
  • 通过减少对数据库的负载来提高性能(更好的实体)
  • 尽可能减少实体之间的耦合。
  • 保持实体和数据库设计尽可能简单。

1 个答案:

答案 0 :(得分:0)

感谢@crizzis提出了我想要的东西。如果将来还有其他人在寻找,这就是解决方案。

解决方案: 只需有两个实体,一个具有所有属性,另一个具有仅您感兴趣的子集,并且两个实体都使用相同的表,如下所示:

@Entity
@Table(name = "EMPLOYEE")
public class Employee {

  @Id
  @GeneratedValue(generator = "UUID")
  @GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
  private UUID id;

  @NotBlank
  private String firstName;

  @NotBlank
  private String lastName;

  private Gender gender;
  private Instant birthDate;
  private String email;
  private String corporateEmail;

  @Embedded
  private Address address;

  ...
}

@Entity
@Table(name = "EMPLOYEE")
@Immutable
public class EmployeeProfile {

  @Id
  private UUID id;
  private String firstName;
  private String lastName;
}

相比之下,您可以在此EmployeeProfile类上链接其他实体,

@Entity
@Table
public class Discipline {

  @Id
  @GeneratedValue(generator = "UUID")
  @GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
  private UUID id;

  @NotNull
  private Instant date;
  private String infraction;

  @ManyToOne
  @JoinColumn(name = "employee_id")
  private EmployeeProfile employeeProfile;
}

默认情况下,ManyToOne关系中没有级联操作,因此完全符合我们的需求。

@AlanHay建议通过让REST端点返回此特定的DTO来实现REST方式。这是另一个很棒的解决方案,尤其是在微服务架构中。

在本例中,我们所有的实体仍然保留在同一个数据库中,我们采用上述解决方案,因为进行微服务的第一步是构建一个出色的/解耦的整体应用程序,因为它将处理所有只有一个数据库查询。而且当将DisciplineEmployee拆分为不同的微服务的日子时,这样做非常简单,因为Discipline表仅保留员工ID,从而避免了痛苦的DB迁移。