0

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

following is a sketch of the er model

m-2127
  • 121
  • 1
  • 8
  • It can't. Java objects have a type, and the type of a Java object can't be changed. You need to either not use JPA, but JDBC to modify the DTYPE column in the table, or (better) you need to change your design and use composition rather than inheritance: a use *has a* role, and this role can be employee or manager. – JB Nizet May 13 '19 at 09:49
  • Oh ok. So if I use composition instead of inheritance, will I be able to change a user from user type to employee type or manager type and back again to user when the particular project is finished. And can you suggest a tutorial to learn about composition.Thanks btw. – m-2127 May 13 '19 at 11:08
  • Yes: `user.setRole(managerRole); user.setRole(basicUserRole);` – JB Nizet May 13 '19 at 11:35
  • I have uploaded the answer and it works fine @JBNizet – m-2127 May 16 '19 at 08:19

1 Answers1

0

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
m-2127
  • 121
  • 1
  • 8