配置 Hibernate 注释以管理历史记录集合的最佳方法是什么?

What is the best way to configure Hibernate annotations to manage a collection of historical records?

我有一个案例,除了 Employee 实体和 Shift 实体之间的 @OneToMany 关系之外,我还有几个 @OneToOne 关系。一名员工有一个当前班次、一个上一个班次,以及代表历史的班次的集合员工执行的轮班记录。下面的代码显示了我如何设置休眠注释来表示这一点。

@Entity
public class Employee {
   @Id
   @Column(name = "EMP_ID")
   private Long id;

   @OneToOne(cascade = {CascadeType.ALL})
   @JoinColumn(name = "CURRENT_SHIFT_ID")
   private Shift currentShift;

   @OneToOne(cascade = {CascadeType.ALL})
   @JoinColumn(name = "LAST_SHIFT_ID")
   private Shift lastShift;

   @OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
   @JoinColumn(name = "EMP_ID")
   private List<Shift> shifts;
}
@Entity
public class Shift {
   @Id
   @Column(name = "SHIFT_ID")
   private Long id;

   @ManyToOne(fetch = FetchType.LAZY)
   @JoinColumn(name = "EMP_ID", nullable = false)
   private Employee employee;

   @Column(name = "START_DATE")
   private Calendar startDate;

   @Column(name = "END_DATE")
   private Calendar endDate;
}

这里有一些示例代码来说明我正在尝试做什么。

   public void test1(long employeeId) {
      Employee dbEmployee = entityManager.find(Employee.class, employeeId);

      Calendar startDate = Calendar.getInstance();
      Calendar endDate = Calendar.getInstance();

      // create the first shift
      Shift one = new Shift();
      one.setEmployee(dbEmployee);
      one.setStartDate(startDate);
      one.setEndDate(endDate);

      // create the second shift
      Shift two = new Shift();
      two.setEmployee(dbEmployee);
      two.setStartDate(startDate);
      two.setEndDate(endDate);

      // create the third shift
      Shift three = new Shift();
      three.setEmployee(dbEmployee);
      three.setStartDate(startDate);
      three.setEndDate(endDate);

      // add shifts 1-3 to the collection of shifts that the employee just performed
      List<Shift> shifts = new ArrayList<Shift>();
      shifts.add(one);
      shifts.add(two);
      shifts.add(three);
      dbEmployee.setShifts(shifts);

      // set the last shift to shift #2
      dbEmployee.setLastShift(two);

      // set the current shift to shift #3
      dbEmployee.setCurrentShift(three);

      entityManager.merge(dbEmployee);
   }

在上面的 Test1 方法中,我正在为员工创建班次 1-3 并另外填充班次集合指定哪个班次是员工的最后一个班次和当前班次。当我对员工执行合并时,生成的数据库表如下所示:

员工Table

id: 1, currentShiftId: 3, lastShiftId: 2

移位Table

id: 1, employeeId: 1, 开始日期:..., 结束日期:...

id: 2, employeeId: 1, 开始日期:..., 结束日期:...

id: 3, employeeId: 1, 开始日期:..., 结束日期:...

到目前为止一切看起来都很好...

   public void test2(long employeeId) {
      Employee dbEmployee = entityManager.find(Employee.class, employeeId);

      Calendar startDate = Calendar.getInstance();
      Calendar endDate = Calendar.getInstance();

      // create the fourth shift
      Shift four = new Shift();
      four.setEmployee(dbEmployee);
      four.setStartDate(startDate);
      four.setEndDate(endDate);

      // create the fifth shift
      Shift five = new Shift();
      five.setEmployee(dbEmployee);
      five.setStartDate(startDate);
      five.setEndDate(endDate);

      // create the sixth shift
      Shift six = new Shift();
      six.setEmployee(dbEmployee);
      six.setStartDate(startDate);
      six.setEndDate(endDate);

      // add shifts 4-6 to the collection of shifts that the employee just performed
      List<Shift> shifts = new ArrayList<Shift>();
      shifts.add(four);
      shifts.add(five);
      shifts.add(six);
      dbEmployee.setShifts(shifts);

      // set the last shift to shift #5
      dbEmployee.setLastShift(five);

      // set the current shift to shift #6
      dbEmployee.setCurrentShift(six);

      entityManager.merge(dbEmployee);
   }

但是,当我执行上面的 Test2 方法时,创建了 4-6 班,并且我对员工执行了合并,这结果是:

员工Table

id: 1, currentShiftId: 6, lastShiftId: 5

移位Table

id: 1, employeeId: NULL, 开始日期:..., 结束日期:...

id: 2, employeeId: NULL, startDate:..., endDate:...

id: 3, employeeId: NULL, startDate:..., endDate:...

id: 4, employeeId: 1, 开始日期:..., 结束日期:...

id: 5, employeeId: 1, 开始日期:..., 结束日期:...

id: 6, employeeId: 1, 开始日期:..., 结束日期:...

所以班次 1-3 正在与我的员工分离。我知道因为我正在执行合并,其中只有班次 4-6 在班次列表中,所以休眠正在更新数据库以仅映射这 3 个班次,而其他班次则变得孤立,但我真正想要的是保持数据库中的那些历史记录。我想这样的事情是可以做到的;我只是没有正确休眠。任何帮助将不胜感激。

谢谢!

我能够通过从员工 class 中删除 @JoinColumn(name = "EMP_ID") 并使用 mappedBy 关系 属性.

来获得我想要的行为
@Entity
public class Employee {
   @Id
   @Column(name = "EMP_ID")
   private Long id;

   @OneToOne(cascade = {CascadeType.ALL})
   @JoinColumn(name = "CURRENT_SHIFT_ID")
   private Shift currentShift;

   @OneToOne(cascade = {CascadeType.ALL})
   @JoinColumn(name = "LAST_SHIFT_ID")
   private Shift lastShift;

   @OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL}, mappedBy = "employee")
   //@JoinColumn(name = "EMP_ID") // this will try to manage the relationship
   private List<Shift> shifts;
}
@Entity
public class Shift {
   @Id
   @Column(name = "SHIFT_ID")
   private Long id;

   @ManyToOne(fetch = FetchType.LAZY)
   @JoinColumn(name = "EMP_ID", nullable = false)
   private Employee employee;

   @Column(name = "START_DATE")
   private Calendar startDate;

   @Column(name = "END_DATE")
   private Calendar endDate;
}

我的新理解是,当您希望该实体管理关系时,使用@JoinColumn 注释。相反,通过使用 mappedBy 属性,我能够指定该关系将由反向实体管理。这非常适合我的情况。