Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple representations of the same entity in SpringBoot 2.0.4

Tags:

I have a basic SpringBoot 2.0.4.RELEASE app. using Spring Initializer, JPA, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR file.

I have a User object with roles:

@Entity
@Table(name="t_user")
public class User implements Serializable, UserDetails {


 @ManyToMany(cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
    @JoinTable(
        name="t_user_role",
        joinColumns=@JoinColumn(name="user_id", referencedColumnName="id"),
        inverseJoinColumns=@JoinColumn(name="role_id", referencedColumnName="id"))
    private Set<Role> roles = new HashSet<>();
..
}

When I init the app. I create all the roles:

roleService.save(new Role(RolesEnum.USER.getRoleName()));
roleService.save(new Role(RolesEnum.ADMIN.getRoleName()));

Then I create a User with the USER role:

User user1 = new User();


         Role role = roleService.findByName(RolesEnum.USER.getRoleName());

         user.getRoles().add(role);
          userService.save(user);

but When I create another user with the same role:

User user2 = new User();


         Role role = roleService.findByName(RolesEnum.USER.getRoleName());

         user2.getRoles().add(role);
          user2Service.save(user);

I got this error:

Multiple representations of the same entity [com.tdk.backend.persistence.domain.backend.Role#1] are being merged. Detached: [com.tdk.backend.persistence.domain.backend.Role@5295d3de]; Detached: [com.tdk.backend.persistence.domain.backend.Role@2b3d9d32]

In the Role entity I don't have the field users declared since I will not get all the users based on a role

like image 730
Nunyet de Can Calçada Avatar asked Aug 25 '18 20:08

Nunyet de Can Calçada


1 Answers

you are facing this issue because you are adding the object to the list/set which is already available.while performing save operation with oneToMany mapping

Now removing CascadeType.MERGE from cascade is one solution but not a best solution because after removing MERGE from Cascade you won't be able to update the mapped object ever

If you want to perform update operation also along with save for the mapped objects then before adding mapped object to list/collection just check/search within the list for the object and if the mapped object is available within the list then perform operation on that particular object.

keep cascade = CascadeType.ALL

Role role = roleService.findByName(RolesEnum.USER.getRoleName());
Note- make sure you have overridden hashcode/equals properly
boolean b = user2.getRoles().contains(role);
if (b!=true){
user2.getRoles().add(role);
}
 user2Service.save(user);

or using stream

Role r= user2.getRoles().stream().filter(oldRole->oldRole.equals(role)).findAny().orElse(null);
        
        if(r==null) {
 user2.getRoles().add(role);
    }
     user2Service.save(user);
like image 91
abhinav kumar Avatar answered Sep 28 '22 19:09

abhinav kumar