I am working on a project management system which has three users namely employee, Manager and HRM. The employee and Manager are in the same entity having many-to-many recursive relationship(let's call this Employee entity). The Employee entity and the HRM entity inherits a User entity. The hibernate inheritance strategy used here is single-table. Initially when a user is registered he is saved as an instance of User( Repository of type User). I want to update the user instance to an instance of an employee or instance of Manager when he is assigned to a particular project. How can this be implemented using spring data jpa. I am doing the project using spring boot.
I have created the entities using java classes and mapped each entity. I have not provided the Project and Tasks class in the following code. If necessary I can provide.
Following is the User class.
@Entity
@Table(name = "users")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
name="User_Type",
discriminatorType=DiscriminatorType.STRING
)
public class User{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="user_id")
private Long id;
private String name;
private String email;
private String password;
private String mobilenumber;
private String gender;
private String resetToken;
@ManyToMany(fetch = FetchType.LAZY, cascade={CascadeType.ALL})
@JoinTable(name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>();//roles refer to the employee,manager and HRM roles
//public getters and setters
Following is the inherited Employee class
@Entity
@DiscriminatorValue("Employee")
public class Employee extends User {
@ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL })
@JoinTable(name = "employee_project",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "project_id")
)
private Set<Project> project = new HashSet<>();
@ManyToMany
@JoinTable(name = "employee_tasks",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "task_id")
)
private Set<Task> tasks = new HashSet<>();
@ManyToMany
@JoinTable(name = "rm_employee",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "rm_id")
)
private Set<Employee> managers = new HashSet<>();
@ManyToMany
@JoinTable(name = "rm_employee",
joinColumns = @JoinColumn(name = "rm_id"),
inverseJoinColumns = @JoinColumn(name = "user_id")
)
private Set<Employee> employees = new HashSet<>();
//public getters and setters
I tried the following which is to downcast the instance of User to the instance of Employee which results in CastException.
Optional<User> optional = userRepo.findById(id);
Employee employee = (Employee)optional.get();
I was able to solve the question without using inheritance. Actually I had to avoid inheritance since as java is concerned the type cannot be changed from one to another. As in this example the type User cannot be converted to Employee or Manager as you wish. So the solution is to create two separate classes for the Manager and Employee(This should not extend the User class as I have done in the question) . These two should be annotated as Entity and should have an Id field. You can have a constructor with the above said Id field in both classes and when ever you assign a user to a project you can use this constructor depending on whether he is manager or employee to create a respective instance of manager or employee.
the employee class is as follows
public class Employee{
@Id
private Long employeeId;
@ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL })
@JoinTable(name = "employee_project",
joinColumns = @JoinColumn(name = "employee_id"),
inverseJoinColumns = @JoinColumn(name = "project_id")
)
private Set<Project> project = new HashSet<>();
@ManyToMany
@JoinTable(name = "employee_rm",
joinColumns = @JoinColumn(name = "employee_id"),
inverseJoinColumns = @JoinColumn(name = "rm_id")
)
private Set<Manager> managers = new HashSet<>();
@ManyToMany
@JoinTable(name = "employee_tasks",
joinColumns = @JoinColumn(name = "employee_id"),
inverseJoinColumns = @JoinColumn(name = "task_id")
)
private Set<Task> tasks = new HashSet<>();
public Employee() {}
public Employee(Set<Project> project, Set<Manager> managers, Set<Task> tasks, Long employeeId ) {
super();
this.project = project;
this.managers = managers;
this.tasks = tasks;
this.employeeId=employeeId;
}
//with public getters and setters
and the Managers class is as follows
@Entity
public class Manager {
@Id
private Long managerId ;
@ManyToMany
@JoinTable(name = "manager_employee",
joinColumns = @JoinColumn(name = "manager_id"),
inverseJoinColumns = @JoinColumn(name = "user_id")
)
private Set<Employee> employees = new HashSet<>();
@OneToOne
@JoinColumn(name="project_id")
private Project project;
public Manager() {}
public Manager(Long managerId) {
super();
this.managerId = managerId;
}
//public getters and setters