Spring data jpa separate entity

I started working on a Spring Boot application using Spring Data JPA to set up ManyToMany relationships between users and roles.

This relationship is defined in the User class as follows:

@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(name="user_role", joinColumns = {@JoinColumn(name="user_id")}, inverseJoinColumns = {@JoinColumn(name="role_id")})
private Set<UserRole> roles;

      

I am creating roles using:

@Transactional
private void generateSeedRoles() {
    UserRole adminRole = new UserRole(RoleEnum.ADMIN.toString());
    userRoleRepository.save(adminRole);

    UserRole userRole = new UserRole(RoleEnum.USER.toString());
    userRoleRepository.save(userRole);
}

      

Assigning roles to users after failures:

@Transactional
private void generateSeedUsers() {
    UserRole adminRole = userRoleRepository.findUserRoleByRoleName("ADMIN");

    User user = User.createUser("user1", "user1@user.com", "pass");
    user.setRoles(new HashSet<UserRole>(Arrays.asList(adminRole)));

    userRepository.save(user);
}

      

The following exception is thrown (formatted readable):

org.springframework.dao.InvalidDataAccessApiUsageException: 
detached entity passed to persist: co.feeb.models.UserRole; 
nested exception is org.hibernate.PersistentObjectException: 
detached entity passed to persist: co.feeb.models.UserRole

      

However, if the user persists before creating the relationship, it works fine:

@Transactional
private void generateSeedUsers() {
    UserRole adminRole = userRoleRepository.findUserRoleByRoleName("ADMIN");

    User user = User.createUser("user1", "user1@user.com", "pass");
    //Save user
    userRepository.save(user);

    //Build relationship and update user
    user.setRoles(new HashSet<UserRole>(Arrays.asList(adminRole)));
    userRepository.save(user);
}

      

Having to save / update a user twice seems somewhat unreasonable to me. Is there a way to assign the resulting role to a new user without saving the user in the first place?

+3


source to share


1 answer


When you persist your object, Spring internally checks if the object is new (I forgot the exact mechanism), and issues either persist (if new) or merge (if exists).

Since you have defined your User to UserRole relationship with Cascade.ALL, any save / merge with User will be included in the UserRole as well.

Given how Spring implements persistence and the cascading behavior above, consider the following scenarios:



  • if both User and UserRole are new you will get persistent action on user save and cascading save to UserRole which works great since UserRole is new.
  • If the user is new, but the UserRole already exists - again you will get a persistent action when the user is saved and the cascading save to the UserRole, which results in an error because the UserRole already exists!

If you can ensure that any custom resource you add to the user relationship always exists, you can simply remove the Cascade.Persist from your cascade parameters. Otherwise, I suppose you will have to use merge - via the custom repository and entityManager - while saving your Account in both scenarios above.

As for using Cascade.ALL, which probably doesn't make sense in this situation, since you have a @ManyToMany relationship, cascading EVERYTHING from User to UserRole can have some unwanted effects (e.g. Cascade.Remove removes UserRole every time User deleted).

+6


source







All Articles