Rüdiger
Rüdiger

Reputation: 943

Problems wiring up three Entities in Spring Boot Jpa with each other

Let's assume I have a table project in my database. Each project has many users and each user might have many projects. This would be a classic ManyToMany-correlation and so, if I would like to depict that in my Spring Boot JPA, I could do as follows:

Set up my Entities and wire them up:

@Entity
public class User {
  @id
  @Column(name="ID")  
  public int id;

  @ManyToMany
  public Set<Project> projectSet;
}

@Entity
@Table(name="PROJECT")
public class Project {
  @id
  @Column("ID")
  private int id;

  @ManyToMany
  private Set<User> users;
}

Implement my Repository:

@Repository
public interface ProjectRepo implements JpaRepository<Project, Integer> {
  public Set<User> getAllUsers();
}

Now let's assume I would like to specify what role each user had in this project. So I would have a table role that specifies different roles with the following Entity-representation:

@Entity
@Table(name="ROLES")
public class Role {
  @id
  @Column(name="ID")
  private int id;

  @Column(name="DESCRIPTION")
  private String description;
}

But how is that connected to my Users and Projects? So what I want to show is that one user can have one role in one project, but many roles in many projects. Also, One Role can be assigned to many users in one or many projects and one project can have many users where each of them has only one role but many roles are there. If I simply add a @ManyToMany Set<Roles> roleSet to my Users, I could not say which User would have which Role in which project. So how to depict that? Would I need to create a third class for connecting them?

Edit 1:

For me to clarify, I am going to try to provide the code for the suggestion in marknote's answer -

Let's assume I have the classes from above (Project, User, Role) and I want to depict that every user has a certain role in one project, but might have another role in another project. Also, multiple roles in one project are not possible (to keep it simple).

So I'd create a new class Assignment (pretty much like I would solve that use case in my database structure) and connect it with my User, my Project and my Role.

@Entity
@Table(name="USER")
public class User {
  @id
  @Column(name="id")
  private int id;

  @Column(name="firstname")
  private String firstname;

  @Column(name="lastname")
  private String lastname;

  @OneToMany
  private List<Assignment> assignments;
  //getters and setters and stuff
}

@Entity
@Table(name="PROJECT")
public class Project {
  @id
  @Column(name="PNUM")
  private String pnum;

  @Column(name="PNAME")
  private String pname;

  @OneToMany
  private List<Assignment> assignments;
  //getters and setters and stuff
}

@Entity
@Table(name="ROLE")
public class Role {
  @id
  @Column(name="id")
  private int id;

  @Column(name="DESCRIPTION")
  private String description;

  @OneToMany
  private List<Assignment> assignments;
  //getters and setters and stuff
}

That's alright so far, so I'm gonna create the following Assignment-class. I need to be able to trace from User to Project and to Role, so I need the three fields. Let's inmagine the (usual) use-case: The table Assignment is connecting User, Project and Role by having a combined PK out of the FKs to the names tables. Now a combined PK in Spring Boot JPA is usually (as far as I know) provided in a new class that uses the @Embeddable-annotation. From that perspective, I'd do the following:

@Embeddable
public class AssignmentId implements Serializable {
  private User user;
  private Project project;
  private Role role;
}

And change my Assignment-class to:

@Entity
@Table(name="ASSIGNMENT")
public class Assignment {
  @EmbeddedId
  private AssignmentId assignmentId;
}

Now when doing it that way, I run into following error when trying to run spring-boot:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: Could not determine type for: com.demo.example.entity.Project, at table: assignment, for columns: [org.hibernate.mapping.Column(project)]

Can someone relate to this error? It might be a problem with the @EmbeddedId I think.

Edit 2

The problem is with the @Embedded class. There are problems wiring up the Entity to an element:

private Role role;
private Project project;
private User user;

Upvotes: 2

Views: 796

Answers (1)

marknote
marknote

Reputation: 1077

Depends on your business requirement. Let's say user's role will be different in projects, you can introduce another class Assignment, then it becomes 3 @OneToMany :) enter image description here

You will refer to User, Project, Role in Assignment with @ManyToOne, but you may or may not need to use @OneToMany. There is a very good article on the best practice of implement @OneToMany: https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate thanks to @vlad-mihalcea

Upvotes: 3

Related Questions