简体   繁体   中英

How to add a JPA relationship to a subset list of attributes of a persisted Entity?

Having the following entities:

@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;
}

A Discipline is for a specific Employee . An Employee may have 0 to several disciplines. A discipline is for only one Employee , no more no less. In the Discipline world (micro-service), it requires only a few attributes from the full Employee class (id, firstName and lastName). In the Employee World (micro-service), all Employee fields are relevant.

How do I properly set my relation between both entities without fetching the Employee entity for each Discipline but only the required fields as I would do with a projection?

Must I refactor my Employee entity by extracting a superclass with only the attributes' subset?

In a magical world, I would like to have Discipline entity defines as follow:

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

Where EmployeeProfile here should look similar to what a Spring Data JPA interface based projection would use.

Aimed goals by doing this:

  • Avoid incoming issues when our entities will be versionned. In fact, we don't want an addDiscipline request to fail due to an outdated employee instance on an irrelevant attribute. This could happen if we link Discipline to the full `Employee
  • Improve performance by reducing load to our DB (slimmer entities the better)
  • Reduce coupling between our entities as much as possible.
  • Keep entities and DB design as simple as possible.

Thanks to @crizzis who proposed what I was looking for. Here's the solution if anybody else is looking for this in future.

SOLUTION: Simply have two entities, one with all the attributes and another one with only the subset you're interested in and have both entities using same table as follow:

@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;
}

Than you can have link other entities on this EmployeeProfile class as follow:

@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;
}

As by default on ManyToOne relationship no operations are cascaded, this suits perfectly our needs.

@AlanHay proposed to go down the REST way by having a REST endpoint returning this specific DTO. It is another great solution especially in a micro-service architecture.

As in our case, all our entities are still persisted in the same DB, we are going with the above solution as first step of doing micro-services is to build a great/decoupled monolithic application and because it will handle everything in only one DB query. And when the day come to split Discipline and Employee in different micro-services, it will be very simple to do so as Discipline table hold only the employee id, avoiding painful DB migration.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM