I'm trying to create a temporal model where an Employee entity contains a collection of EmployeeDetails. Each EmployeeDetails is marked with dates validFrom and a validTo so I can keep track of an employee's name changes over time. To prevent an employee from having multiple names at the same point in time I have created a NoOverlap constraint and placed it on the EmployeeDetails collection. Later on I am planning to add Envers on top of this so I can keep track of when the an employee officially changed her name and when the new name was entered into the application.
Example 1:
Code:
@Entity
public class Employee {
// Things left out for brevity
@Version
private long version;
@ElementCollection
@NoOverlap // <- Custom constraint
private List<EmployeeDetails> details;
}
@Embeddable
public class EmployeeDetails {
// Things left out for brevity
private Date validFrom;
private Date validTo;
private String name;
}
In example 1 everything works great. Adding a new EmployeeDetails to the collection triggers a validation of the Employee which prevents overlapping entries. Modifying an existing EmployeeDetails also triggers a validation of the Employee which again prevents overlapping entries.
Example 2:
Code:
@Embeddable
public class EmployeeDetails {
// Things left out for brevity
private Date validFrom;
private Date validTo;
private String name;
@ManyToOne
private Department department;
}
In example 2 I have added support for tracking which deparment an employee is working in. This also works great.
Example 3:
Code:
@Embeddable
public class EmployeeDetails {
// Things left out for brevity
private Date validFrom;
private Date validTo;
private String name;
@ManyToOne
private Department department;
@OneToMany
private List<Project> projects;
}
In example 3 I have added support for tracking which projects an employee is a member of. Unfortunately Hibernate won't start at all with this configuration due to a bug that was reported eight years ago. See issue
https://hibernate.atlassian.net/browse/HHH-4313. I wish this would be fixed but it's way beoynd my skills to submit a patch for it. The suggested workaround is to model the embeddable as an entity instread.
Example 4:
Code:
@Entity
public class Employee {
// Things left out for brevity
@Version
private long version;
@OneToMany
@NoOverlap // <- Custom constraint
private List<EmployeeDetails> details;
}
@Entity
public class EmployeeDetails {
// Things left out for brevity
private Date validFrom;
private Date validTo;
private String name;
}
In example 4 adding a new EmployeeDetails to the collection triggers a validation of the Employee which prevents overlapping entries. But modifying an existing EmployeeDetails does not trigger a validation of the Employee. This unfortunately makes it possible to create overlapping entries.
I've tried to solve this with various approaches such as implementing a custom TraversableResolver in Hibernate Validator, creating a Hibernate Interceptor and overriding the findDirty method, by implementing a CustomEntityDirtinessStrategy and finally by making all my entities immutable in order to prevent modifications of existing entities.
None of these approaches have worked really well and all come with even more complicated drawbacks. I believe some of the approaches listed above could work but I get lost in the Hibernate internals when trying to find a working solution. Is there any hope of someone fixing HHH-4313? Does anyone have an example of modified TraversableResolver or a custom dirty checking algorithm that could work for this situation?
I have very small maven project with an entity/embeddable-model and an entity/entity-model side-by-side and a complete set of unit tests that I will happily make avaialble to anyone willing to give this a try. Just tell me where to post it. :)