Spring Boot JPA many to many delete cascade - java

So i have a MyUser entity which refers to a Role entity with a many to many relationship.
But when i try to delete the user, i always get the errror that the user_id is always referenced on the user_role jointable...
I have already tried every cascade type... but didn't get the solution
Please help
Thanks
#Table(name = "users")
public class MyUser {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
... Other properties ...
#ManyToMany
#JoinTable(
name = "users_roles",
joinColumns = #JoinColumn(
name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(
name = "role_id", referencedColumnName = "id"))
private Collection<Role> roles;
// getters setters
}
#Entity
#Table(name = "roles")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String role;
// getters setters
}

As it stated in the documentation:
For #ManyToMany associations, the REMOVE entity state transition doesn’t make sense to be cascaded because it will propagate beyond the link table. Since the other side might be referenced by other entities on the parent-side, the automatic removal might end up in a ConstraintViolationException.
So, before removing Role entity you should be assured that there is no MyUser entity linked with this role.

Related

Spring boot #JoinCulumn ManyToOne relationship column does not exist

I currently have a problem with this Relationship, I have tried everything I saw on the internet. Still, I get this error: ERROR: column roles0_.user_id does not exist.
I have a boot app that has spring security, and I need to login using users from PostgreSQL database.
But I just can't get the relation between the user and the Role to work.
Here are Entity classes:
#Data
#Entity
#Table(name="user",schema = "public")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Integer id;
#Column(unique = true)
private String username;
private String password;
private boolean enabled;
#OneToMany(mappedBy = "user", fetch = FetchType.EAGER)
private List<Role> roles;
}
#Data
#Entity
#Table(name="role",schema = "public")
public class Role {
#Id
#Column(name="role_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
#ManyToOne()
#JoinColumn(name ="user_id")
private User user;
}
The database looks fine, I looked at the column names, etc. I don't know what to do to get rid of this error. I have the user table, and another table named roles, which include id and name, 2 inputs, USER and ADMIN...
It seems that the #JoinColumn annotation requires one additional column in the roles table the one with #ManytoOne relation, because when I add the column the error disappears, but when I'm trying to get the role from each user, I get an empty List. The foreign key is set as well, from the roles column to the role_id column from role table.
worked for me this way:
#Entity
#Data
#Table(name = "users")
public class User{
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "user_roles", joinColumns = {#JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "role_id", referencedColumnName = "id")})
private List<Role> roles;
}
and then in roles just:
#Entity
#Table(name = "roles")
public class Role{
#ManyToMany(mappedBy = "roles", fetch = LAZY)
private List<User> users;
}
that's if you are ok with third table user_roles (user_id, role_id) which manages the many to many relation
User table :
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user", fetch = FetchType.EAGER)
private List<Role> roles;
Role table :
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id")
private User user;

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?
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.

Hibernate Cascade.All property doesn't add ON DELETE CASCADE on Postgres Foreign Key Constraint

I am using SpringBoot + PostgreSQL + JPA Hibernate, everything seems to working fine however the Cascade.ALL property is not applied on the table user_roles. Am i missing anything important. I have tried ManytoMany relationship and ManytoOne but no luck yet.
The entity files I am using are as follows.
User.java
#Entity
#Table(name = "USERS")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class User {
#Id
#SequenceGenerator( name = "lineupSeq", sequenceName = "lineup_seq", allocationSize = 20, initialValue = 50 )
#GeneratedValue( strategy = GenerationType.SEQUENCE, generator = "lineupSeq" )
#Column(name="id")
private Long id;
#Column(unique=true)
private String username;
#Column
#JsonIgnore
private String password;
#Column(name="prodcrud")
private String prodcrud;
#Column(name="devcrud")
private String devcrud;
#Column(name="testcrud")
private String testcrud;
#ManyToOne(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
#JoinTable(name = "user_roles",
joinColumns = {#JoinColumn(name = "user_id", referencedColumnName="id") },
inverseJoinColumns = {#JoinColumn(name = "role_id", referencedColumnName="id") })
private Role role;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
Screenshot of postgres constraint added
Postgres constraint added
Yes, you simply expect this annotation to do something it doesn't do.
Cascade.ALL tells JPA that every operation (persist, merge, delete, etc.) done, at runtime, using JPA, on a User must also be applied on its role.
Note that it doesn't make sense: since it's a ManyToOne, trying to delete the role of a user when deleting the user will lead to broken foreign key constraints, since the same role is also referenced by other users.
Read the documentation. Don't guess what annotations do.

How to resolve many-to-many relationship without creating a junction class in Java?

In my DB I have three table USER, PERMISSION and a junction table USER_PERMISSIONS. As you can see I have clear many-to-many relationship.
And here is the question. I'm using a Hibernate. Is it possible to use only two classes, User and Permissions without creating a junction class UserPermissions? I mean can I use some kind of annotations to include my junction table USER_PERMISSIONS?
Yes you can. You need to use the #JoinTable and #JoinColumn annotation. Example below:
#Entity
#Table(name = "USER")
public class User {
#Id
#Column(name = "ID")
private Long id;
#ManyToMany
#JoinTable(name = "USER_PERMISSIONS",
joinColumns = #JoinColumn(name = "USER_ID", referencedColumnName = "ID"),
inverseJoinColumns = #JoinColumn(name = "PERM_ID", referencedColumnName = "ID"))
private Collection<Permissions> permissions;
A full working example with bidirectionaly many to many can be found in here http://en.wikibooks.org/wiki/Java_Persistence/ManyToMany

While deleting a user entity in hibernate the role get deleted as well

I have 3 tables:
user: (id,username,password)
role: (id,role)
user_roles: (user_id, role_id)
and the following two Hibernate entities:
User:
#Entity
#Table(name = "user")
public class User implements Serializable{
#Id
#GeneratedValue
private Long id;
private String userName;
private String password;
private boolean active = false;
#OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER )
#JoinTable(name = "user_roles", joinColumns = { #JoinColumn(name = "user_id", referencedColumnName = "id") }, inverseJoinColumns = { #JoinColumn(name = "role_id", referencedColumnName = "id") })
private Set<Role> roles = new HashSet<Role>();
....
Role:
#Entity
#Table (name = "roles")
public class Role implements Serializable {
#Id #GeneratedValue
private Long id;
private String role;
....
When I delete now a User the corresponding role get deleted as well from the "role" table instead to delete just the user row and the user_roles relation. Even if other users are still related to that role. I use the following to delete the user.
#Transactional
public void deleteByName(String userName) {
User user = this.getByName(userName);
Session session = sessionFactory.getCurrentSession();
session.delete(user);
}
Anyone know why and how to solve this?
Thanks
This is the expected behaviour for the current mapping. It is configured by the cascade setting:
#OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER )
private Set<Role> roles = new HashSet<Role>();
If you do not want the roles to be removed when a user is, you will have to narrow down your cascade settings. Right now you are using ALL. This is equivalent to {PERSIST, REMOVE, REFRESH, MERGE, DETACH}. Decide on which ones you need and remove the others.
There is one thing I did not understand. You say that the roles are removed "Even if other users are still related to that role". This should not be possible.
The relationship of Role and User is a many-to-one, so there can be only one user attached to a role.

Categories

Resources