Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
323 views
in Technique[技术] by (71.8m points)

java - Spring Data: managing a bidirectional many-to-many relationship

I have a bidirectional many-to-many relationship between a Role and Scope. Creating both entities and even their childs with the help of CascadeType.PERSIST is easy and straightforward.

The Role entity is simples as that:

@Entity
@Table(uniqueConstraints = @UniqueConstraint(name = "role_name", columnNames = "name"))
public class Role {

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

    private String name;
    
    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST, mappedBy = "roles")
    private Set<Scope> scopes;

}

And the Scope:

@Entity
@Table(uniqueConstraints = @UniqueConstraint(name = "scope_name", columnNames = "name"))
public class Scope {

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

    private String name;

    @JoinTable(name = "role_scopes", joinColumns = @JoinColumn(name = "scope_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
    @ManyToMany(cascade = CascadeType.REMOVE)
    private Set<Role> roles;

}

Their repositories are simply CrudRepository extensions:

public interface RoleRepository extends CrudRepository<Role, Long> {}
public interface ScopeRepository extends CrudRepository<Scope, Long> {}

The following snippet exemplifies the entities insertion:

Role adminRole = roleRepository.save(new Role("ADMIN"));
Scope allReadScope = scopeRepository.save(new Scope("all.read"));
Scope allWriteScope = scopeRepository.save(new Scope("all.write"));

Role and Scope can be both automatically easily persisted with the help of the CascadeType.PERSIST, as follows:

Role managedRole = roleRepository.save(new Role("ADMIN", new Scope("all.read"), new Scope("all.write")));

However... Updating managedRole leads to org.hibernate.PersistentObjectException: detached entity passed to persist exception:

managedRole.getScopes().remove(allReadScope);
roleRepository.save(managedRole); // PersistentObjectException!

I tried modifying the Role::scopes's CascadeType to also include DETACH, MERGE and/or REFRESH with no success. How do we get around this?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Most likely you face the problem, because you don't maintain both sides of the relationship in the bidirectional mapping. Lets say in Role:

void add(Scope scope) {
   this.scopes.add(scope);
   scope.getRoles().add(this);
}

To be honest with you, I'd resign fully from bidirectional mapping. Maintaining this is a real nightmare.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...