I have two entity-classes: Account and Role. These are mapped with the many-to-many relationship. I want to delete one account from database. The code below is working for me, however, I believe there is a better approach.
My Account.class:
public class Account {
//some code
#ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE, CascadeType.PERSIST})
#JoinTable(name = "account_role",
joinColumns = {#JoinColumn(name = "account_id")},
inverseJoinColumns = {#JoinColumn(name = "role_id")})
private Set<Role> roles = new HashSet<>();
}
My Role.class:
public class Role {
//some code
#EqualsAndHashCode.Exclude
#ManyToMany(mappedBy = "roles", fetch = FetchType.EAGER,
cascade = {CascadeType.MERGE , CascadeType.PERSIST/*, CascadeType.DETACH, CascadeType.REFRESH*/})
private Set<Account> accounts = new HashSet<>();
}
This is my mapping in PostgreSQL
TL;DR
Code below works fine, what is the better approach?
#Override
public void deleteUserAndHisTokensById(Long accountId) {
Account accountToBeDelete = accountRepository.findDistinctById(accountId);
accountToBeDelete.getRoles()
.forEach(role -> {
Set<Account> updatedAccounts = role.getAccounts()
.stream()
.filter(account -> !account.equals(accountToBeDelete))
.collect(Collectors.toSet());
role.setAccounts(updatedAccounts);
roleRepository.save(role);
});
accountToBeDelete.setRoles(null);
accountRepository.deleteById(accountId);
}
If you add CascadeType.REMOVE, just like #JonathanJohx mentioned. You will be able to remove it just like this:
accountRepository.deleteById();
Moreover, if you want to remove roles from accounts add CascadeType.REMOVE to the other side of the relationship, so you can do this:
Role role = roleRepository.findById(10);
Account account = accountRepository.findById(11);
account.getRoles().remove(role);
Related
I have Client and Notification which linked by #ManyToMany relationship like that:
Client:
#ManyToMany (fetch = FetchType.LAZY, cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "notification_list",
joinColumns = #JoinColumn(name = "client_ID"),
inverseJoinColumns = #JoinColumn(name = "notification_ID")
)
#Getter #Setter
private Set<Notification> notifications = new LinkedHashSet<>();
Notification:
#ManyToMany(mappedBy = "notifications", fetch = FetchType.LAZY)
#Getter #Setter
private Set<Client> clients = new HashSet<>();
I want only the link to be deleted, that is, the entry from the auth_data spanning table. How do I do this? Do I need to create a separate JpaRepository, or is it enough to simply remove the Notification from the client?
SOLVED!
I added method to Client entity class:
public void removeNotification(Notification notification){
this.notifications.remove(notification);
notification.getClients().remove(this);
}
Made ClientServiceIml and did this:
#Override
public void removeNotifications(Client client) {
for(Notification notification : client.getNotifications()) {
client.removeNotification(notification);
}
clientRepository.save(client);
}
All work fine.
I have ManyToMany relationships in Hibernate
Project.class
#ManyToMany
#JoinTable(
name = "user_projects",
joinColumns = { #JoinColumn(name = "project_id")},
inverseJoinColumns = { #JoinColumn(name = "user_id")}
)
private Set<User> projectUsers = new HashSet<>();
User.class
#ManyToMany(fetch = FetchType.EAGER,
mappedBy="projectUsers",
cascade = CascadeType.ALL)
private Set<Project> userProjects = new HashSet<>();
And when I get current user thru
#AuthenticationPrincipal User user
I have Method threw 'org.hibernate.LazyInitializationException' exception when user.getUserProjects();
It's because when using #ManyToMany, first all Projects of user are fetched, then all Users of fetched Projects and so on... This cyclic dependency causes the error, because there is eager fetching missing on Project side.
The solution would be to use:
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(
name = "user_projects",
joinColumns = { #JoinColumn(name = "project_id")},
inverseJoinColumns = { #JoinColumn(name = "user_id")}
)
private Set<User> projectUsers = new HashSet<>();
Although I do not recommend this solution, because of performance issues (and possible side effects). Better solution would be to have an entity for join table.
Try to use #Transactional maybe can solved it because
the #Transactional annotation on the getFavorites() method indicates that the session will be closed at the end of this method
How to make Bridge table using annotation using Hibernate/JPA configuration.
1: BookModel
2: UserModel
now I have to create a bridge table by these two by fields
book_id and user_id
You are trying to implement Many to Many relationship between your entities. So for this, If you have list of Books in User model, then you can annotate the list as following:
public class UserModel {
#ManyToMany(cascade = CascadeType.REMOVE)
#JoinTable(name = "book_user_table", joinColumns = { #JoinColumn(name = "book_id") }, inverseJoinColumns = { #JoinColumn(name = "user_id") })
private List<BookModel> books;
//Getters and setters
}
and in BookModel, if you have list of users, then you need to use #mappedBy() annotation like following:
#ManyToMany(mappedBy = "books")
private List<UserModel> users;
This will generate the 3rd table, which will have the name book_user_table, with your required columns. See this for detailed explanation: https://dzone.com/tutorials/java/hibernate/hibernate-example/hibernate-mapping-many-to-many-using-annotations-1.html
From what I understand from your quesrtion is, you want to know how to map book and user so that there is many to many association between these entities.
If so, you need to specify #ManyToMany on both the associations and make one of them as inverse and the other end with #JoinTable. An example mapping here. Snippet below.
#ManyToMany(cascade = { CascadeType.ALL })
#JoinTable(
name = "Employee_Project",
joinColumns = { #JoinColumn(name = "employee_id") },
inverseJoinColumns = { #JoinColumn(name = "project_id") }
)
Set<Project> projects = new HashSet<>();
On the inverse end,
#ManyToMany(mappedBy = "projects")
private Set<Employee> employees;
I have a many-to-many relationship between user and role entities. when I try to merge a role by adding new user objects to that, after merging i expect a new row in the joined table (in sql server database) to be inserted but nothing happens.
I guess the problem is the direction of the relationship which the owner is User entity now and should be switched to Role. But if I change it then my spring security wont work. Is there any other way to solve this? except changing many-to-many sides?
Thanks in advance.
User class
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(
name = "SEC_USER_ROLE",
joinColumns = {
#JoinColumn(name = "SEC_USER_ID", referencedColumnName = "SEC_USERS")},
inverseJoinColumns = {
#JoinColumn(name = "SEC_ROLE_ID", referencedColumnName = "SEC_ROLES")})
private List<Role> roles;
--
Role class
#ManyToMany(mappedBy = "roles", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<User> users;
And here is my merge function
#Override
#Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public T merge(T o) {
o = em.merge(o);
em.flush();
return o;
}
I have a table that has two different many-to-many relations to two different tables. Let's say I have User <---> UserRole <--> Role and User <--> UserGroups <--> Groups. Since I am new to hibernate and database mapping I was wondering if having my User entity have attributes roles and groups in it, both with #ManytoMany annotations is good practice and acceptable?
i.e.:
#Entity(name = "User")
#Table(name = "USER")
public class User {
.... /* Obviously Id would go here and all other attributes */
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "UserRole", joinColumns = { #JoinColumn(name="USER_ID") },
inverseJoinColumns = { #JoinColumn(name = "ROLE_ID") } )
private Set<Role> roles = new HashSet<Role>();
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "UserGroup", joinColumns = { #JoinColumn(name="USER_ID") },
inverseJoinColumns = { #JoinColumn(name = "GROUP_ID") } )
private Set<Group> groups = new HashSet<Group>();
Sure; lots of types have multiple many-to-many.
I think minimizing many-to-many relationships is a good idea, but it's a natural structure, and is found all over the place.