Search code examples
hibernatejpamany-to-many

JpaRepository won't insert many-to-many data


I have User entity and Role entity which are generated using JPA Tools. User and Role have many-to-many relationship. I use following code to insert admin, however, no data is inserted into user_role table. Also I notice role class has @JoinTable but user class does not have it. Are there differences?

private void addAdmin() {
    User admin = new User();
    admin.setDisplayName("admin");

    Role role1 = new Role();
    role1.setRole("ROLE_SUPER_ADMIN");
    Role role2 = new Role();
    role2.setRole("ROLE_END_USER");
    List<Role> roles = new ArrayList<Role>();
    roles.add(role1);
    roles.add(role2);
    admin.setRoles(roles);
    userRepository.save(admin);
}

Following is my User class:

@Entity
@NamedQuery(name="User.findAll", query="SELECT u FROM User u")
public class User implements Serializable {
  private static final long serialVersionUID = 1L;

  @Id
  @GeneratedValue(strategy=GenerationType.AUTO)
  private int id;

  @Column(name="display_name")
  private String displayName;

  //bi-directional many-to-many association to Role
  @ManyToMany(mappedBy="users")
  private List<Role> roles;
}

Following is my role class:

@Entity
@NamedQuery(name="Role.findAll", query="SELECT r FROM Role r")
public class Role implements Serializable {
  private static final long serialVersionUID = 1L;

  @Id
  @GeneratedValue(strategy=GenerationType.AUTO)
  private int id;

  private String role;

  //bi-directional many-to-many association to User
  @ManyToMany
  @JoinTable(
    name="user_role"
    , joinColumns={
        @JoinColumn(name="role_id")
        }
    , inverseJoinColumns={
        @JoinColumn(name="user_id")
        }
    )
  private List<User> users;
}

Solution

  • roles association in the User entity is the inverse side of the association (specified by mappedBy). That means that it is the duty of Role class to maintain the association, so to make the association changes persistent, you have to modify the owner side as well:

    role1.getUsers().add(admin);
    role2.getUsers().add(admin);
    

    Anyway, it is a very good convention to always update both sides of bidirectional associations, so that the state of the inverse side also reflects the changes in the owner side. That's why it is a common practice to create helper methods that change both sides of the association:

    @Entity
    public class User {
    
      //bi-directional many-to-many association to Role
      @ManyToMany(mappedBy="users")
      private List<Role> roles;
    
      public void addRole(Role role) {
        if (!roles.contains(role)) {
          roles.add(role);
          role.addUser(this);
        }
      }
    }
    
    @Entity
    public class Role {
    
      //bi-directional many-to-many association to User
      @ManyToMany
      @JoinTable(
        ...
        )
      private List<User> users;
    
      public void addUser(User user) {
        if (!users.contains(user)) {
          users.add(user);
          user.addRole(this);
        }
      }  
    }