mapped entity saved again - issue - spring-boot

whenever i am using cartRepository.save(cart) user is again inserted in user table ?(Cart is having a field User).
how not to save the user again?
Users class:
#Entity
#Table(name="users")
public class Users {
#Id
#GeneratedValue
private int id;
#Column(name="nameofuser")
//#UniqueElements
private String nameofuser;
#Column(name="password")
private String password;
#OneToOne(mappedBy="user",cascade = {CascadeType.ALL})
Cart cart;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getNameofuser() {
return nameofuser;
}
public void setNameofuser(String nameofuser) {
this.nameofuser = nameofuser;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Cart Entity:
#Entity
#Table(name="cart")
public class Cart {
#Id
#GeneratedValue
#Column(name="id")
int id;
#OneToOne(cascade = CascadeType.ALL)
Users user;
public Users getUser() {
return user;
}
public void setUser(Users user) {
this.user = user;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
CartService class:
package com.praful.lastTry.Services;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.praful.lastTry.MenuRepository.CartRepository;
import com.praful.lastTry.Models.Cart;
import com.praful.lastTry.Models.Users;
#Service
public class CartService {
#Autowired CartRepository cartRepository;
public void bindingWithUser(Users user) {
Cart cart=new Cart();
Users user1=new Users();
cart.setUser(user1);
System.out.println(" from here==================================");
cartRepository.save(cart);
System.out.println("till here ==================================");
}
}

whenever i am using cartRepository.save(cart) user is again inserted in user table
Because of this code :
Users user1=new Users();
Every time you are sending a new user.
Change it to something:
public void bindingWithUser(Users user) {
Cart cart=new Cart();
Users user1= getUser();
cart.setUser(user1);
System.out.println(" from here==================================");
cartRepository.save(cart);
System.out.println("till here ==================================");
}
private Users getUser(){
//return logged in user or your logic to get user which is already there in Database
}

Seems like in your bindingWithUser function you are passing a User object user but inside the function, you are initializing a new User object user1 and setting it to the cart object before saving it

Related

Relationship CRUD API Spring Boot

I am creating a crud api with a many to many relationship betwen role and user. When i make a Get HTTP Request, i get the mesage below but When i delete all relationship and make findall on single table, it works percfecttly. Where do you think the problem is?
Error Message in postman
{
"timestamp": "2021-07-10T04:28:24.877+0000",
"status": 500,
"error": "Internal Server Error",
"message": "JSON mapping problem: java.util.ArrayList[0]->com.notyfyd.entity.User[\"roles\"]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: com.notyfyd.entity.User.roles, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->com.notyfyd.entity.User[\"roles\"])",
"path": "/user/all"
}
Role Entity
package com.notyfyd.entity;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import javax.persistence.*;
import java.util.List;
#Entity
#Table(name = "t_role")
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
#ManyToMany(targetEntity = User.class, mappedBy = "roles", cascade = {CascadeType.PERSIST, CascadeType.DETACH,CascadeType.MERGE,CascadeType.REFRESH})
private List<User> users;
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
}
User Entity
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import javax.persistence.*;
import java.util.List;
#Entity
#Table(name = "t_user")
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
private String mobile;
#Column(unique = true)
private String email;
#ManyToMany(targetEntity = Role.class, cascade = {CascadeType.PERSIST, CascadeType.DETACH,CascadeType.MERGE,CascadeType.REFRESH} )
#JoinTable(
name="t_user_roles",
joinColumns=
#JoinColumn( name="user_id", referencedColumnName="id"),
inverseJoinColumns=#JoinColumn(name="role_id", referencedColumnName="id"))
private List<Role> roles;
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
here is the log error on MSSQL Server
2021-07-10 11:20:59.333 WARN 3124 --- [nio-6120-exec-4] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: fdsa.edu.PNUFDSA.Model.AnneeAcademique.paiements, could not initialize proxy - no Session; nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: fdsa.edu.PNUFDSA.Model.AnneeAcademique.paiements, could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->fdsa.edu.PNUFDSA.Model.AnneeAcademique["paiements"])]
the Entity is:
* "Visual Paradigm: DO NOT MODIFY THIS FILE!"
*
* This is an automatic generated file. It will be regenerated every time
* you generate persistence class.
*
* Modifying its content may cause the program not work, or your work may lost.
*/
/**
* Licensee:
* License Type: Evaluation
*/
package fdsa.edu.PNUFDSA.Model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.*;
#Entity
#Data
//#org.hibernate.annotations.Proxy(lazy=false)
#Table(name="AnneeAcademique")
public class AnneeAcademique implements Serializable {
public AnneeAcademique() {
}
#Column(name="ID", nullable=false, length=10)
#Id
#GeneratedValue(generator="PNU_ANNEEACADEMIQUE_ID_GENERATOR")
#org.hibernate.annotations.GenericGenerator(name="PNU_ANNEEACADEMIQUE_ID_GENERATOR", strategy="native")
private int id;
#Column(name="Debut", nullable=true)
#Temporal(TemporalType.DATE)
private java.util.Date debut;
#Column(name="Fin", nullable=true)
#Temporal(TemporalType.DATE)
private java.util.Date fin;
#JsonIgnore
#ManyToMany(mappedBy="anneeAcademiques", targetEntity=fdsa.edu.PNUFDSA.Model.Cours.class)
#org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.LOCK})
#org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.TRUE)
private java.util.Set cours = new java.util.HashSet();
#JsonIgnore
#OneToMany(mappedBy="anneeAcademique", targetEntity=fdsa.edu.PNUFDSA.Model.Paiement.class)
#org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.LOCK})
#org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.TRUE)
private List paiements = new ArrayList();
private void setId(int value) {
this.id = value;
}
public int getId() {
return id;
}
public int getORMID() {
return getId();
}
public void setDebut(java.util.Date value) {
this.debut = value;
}
public java.util.Date getDebut() {
return debut;
}
public void setFin(java.util.Date value) {
this.fin = value;
}
public java.util.Date getFin() {
return fin;
}
public void setCours(java.util.Set value) {
this.cours = value;
}
public java.util.Set getCours() {
return cours;
}
public void setPaiements(List value) {
this.paiements = value;
}
public List getPaiements() {
return paiements;
}
public String toString() {
return String.valueOf(getId());
}
}
Are you returning that entity directly as json. Can add #JsonIgnore on below field.
#JsonIgnore
private List<Role> roles;
You can set fetch type to FetchType.EAGER on your many to many relations in your entities.
Try this and let me know what happened.

SpringBoot+Neo4J OGM update record

I am getting very weird problem when trying to update the record in database .Main Node is updating properly but Relationship not creating after deleting it.
I have Node with relationship in database i am trying to update it via this code
Role roleRecord = findByUuid(uuid);//Get Role Record
Role roleData = new Role();//Create a new role object and update values
roleData.setDescription(role.getDescription());
roleData.setUuid(roleRecord.getUuid());
roleData.setRoleName(roleRecord.getRoleName());
roleData.setLabels(updatedLabelRecord);
deleteRole(roleRecord);// Delete existing role from database
for (Labels label : dbRecord) { //Delete relationship Node
deleteLabel(label);
}
createRole(roleData);// Then Create role and Label with new Data set
This code creating Role record but not the Label Node(Which is a relationship),Relationship something like this
Role->FILTERS_ON->Label
EDIT 1-
Role is a Neo4j Entity
deleteRole is method
public void deleteRole(Role roleEntity) {
roleRepository.delete(roleEntity);
}
deleteLabel is a method
public void deleteLabel(com.nokia.nsw.uiv.uam.entities.Labels label) {
labelRepository.delete(label);
}
createRole is a method
public Role createRole(Role role) {
return roleRepository.save(role);
}
EDIT 2 -
Role Entity Class
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.neo4j.ogm.annotation.GeneratedValue;
import org.neo4j.ogm.annotation.Id;
import org.neo4j.ogm.annotation.NodeEntity;
import org.neo4j.ogm.annotation.Relationship;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.Api;
#Api(
tags = "Role",
description = ""
)
#NodeEntity(label = "com.model.Role")
public class Role implements Serializable {
private static final long serialVersionUID = -8010543109475083169L;
private String roleName = null;
private String description = null;
// #Relationship(type = "HAS_ROLE", direction="INCOMING")
// private Tenant tenant;
#Relationship(type = "FILTERS_ON")
private List<Labels> labels = new ArrayList<>();
#JsonIgnore
private Long id;
#Id
#GeneratedValue(strategy = UivUuidStrategy.class)
#JsonProperty("id")
private String uuid;
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
// public Tenant getTenant() {
// return tenant;
// }
//
// public void setTenant(Tenant tenant) {
// this.tenant = tenant;
// }
public List<Labels> getLabels() {
return labels;
}
public void setLabels(List<Labels> labels) {
this.labels = labels;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
Label Entity class
import java.io.Serializable;
import java.util.Map;
import java.util.Objects;
import org.neo4j.ogm.annotation.GeneratedValue;
import org.neo4j.ogm.annotation.Id;
import org.neo4j.ogm.annotation.NodeEntity;
import org.neo4j.ogm.annotation.Properties;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
#NodeEntity(label = "com.model.role.Filter")
public class Labels implements Serializable {
private static final long serialVersionUID = 1L;
private String labelName;
#Properties
private Map<String, String> match;
private String access;
#JsonIgnore
private Long id;
#Id
#GeneratedValue(strategy = UivUuidStrategy.class)
#JsonProperty("id")
private String uuid;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public Map<String, String> getMatch() {
return match;
}
public void setMatch(Map<String, String> match) {
this.match = match;
}
public String getLabelName() {
return labelName;
}
public void setLabelName(String labelName) {
this.labelName = labelName;
}
public String getAccess() {
return access;
}
public void setAccess(String access) {
this.access = access;
}
#Override
public String toString() {
return "labelName : " + this.labelName;
}
#Override
public boolean equals(Object obj) {
return (obj instanceof Labels) && this.labelName.equals(((Labels) obj).getLabelName());
}
#Override
public int hashCode() {
return Objects.hash(labelName);
}
}

null values inserted while auditing

My AuditListener
public class EmployeeAuditListeners {
#PrePersist
public void prePersist(Employee employee){
perform(employee,Action.INSERTED);
}
#PreUpdate
public void preUpdate(Employee employee){
perform(employee,Action.UPDATED);
}
#PreRemove
public void preRemove(Employee employee){
perform(employee,Action.DELETED);
}
#Transactional
public void perform(Employee emp, Action action){
EntityManager em = BeanUtil.getBean(EntityManager.class);
CommonLogs commonLogs = new CommonLogs();
commonLogs.setQuery("new query");
em.persist(commonLogs);
}
}
and My Auditable.class
#MappedSuperclass
#EntityListeners(AuditingEntityListener.class)
public abstract class Auditable<U> {
#CreatedBy
protected U createdBy;
#CreatedDate
#Temporal(TemporalType.TIMESTAMP)
protected Date createdDate;
#LastModifiedBy
protected U lastModifiedBy;
#LastModifiedDate
#Temporal(TemporalType.TIMESTAMP)
protected Date lastModifiedDate;
}
My CommonLogs.class
#Entity
#EntityListeners(AuditingEntityListener.class)
public class CommonLogs extends Auditable<String> {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String query;
public CommonLogs() {
}
public CommonLogs(String query) {
this.query = query;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
}
My Employee.java class
#Entity
#EntityListeners(EmployeeAuditListeners.class)
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private String address;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
and I have a simple Rest Controller
#RestController
#RequestMapping("/api")
public class EmployeeController {
#Autowired
private EmployeeRepository employeeRepository;
#PostMapping("/employees")
public Employee createEmployee(#RequestBody Employee employee){
return employeeRepository.save(employee);
}
}
I want to log it on my table (common_logs) every time i perform some crud operations on my Employee Entity.
the above given example is working to some extent as it successfully stores employee and invokes EmployeeAuditListeners.
but now while saving CommongLog entity i expect it's parent class Auditable to automatically insert createdBy, createdDate etc. for now only query and id is inserted on common_logs table and remaining columns are null.
You can review the documentation for Auditing in here.
To enable the automatic Auditing, you must add the annotation #EnableJpaAuditing in your Application class:
#SpringBootApplication
#EnableJpaAuditing
class Application {
static void main(String[] args) {
SpringApplication.run(Application.class, args)
}
}
If you want the fields #CreatedBy and #LastModifiedBy too, you will also need to implement the AuditorAware<T> interface. For example:
class SpringSecurityAuditorAware implements AuditorAware<User> {
public User getCurrentAuditor() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return null;
}
return ((MyUserDetails) authentication.getPrincipal()).getUser();
}
}

Spring (Hibernate) - incomplete serialization result / many-to-many

Rest Application / Spring MVC - 3 entities: User, AccessRole, AccessPermision.
Each user has only one role, each role has one or more privileges.
The problem occurs during serialization of users with the same role.
In such case, the JSON serialization result, contains permissions only for the first user.
User Entity
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import socialcreek.access.model.AccessRole;
import socialcreek.user.views.UserViews;
import javax.persistence.*;
import java.util.Set;
#Entity
#Table(name = "users")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id")
public class User {
/**-----------------------------------------------------
* Constructor
-------------------------------------------------------*/
public User(){ }
public User(String username, String password, AccessRole accessRole) {
this.username = username;
this.password = password;
this.userAccessRole = accessRole;
}
/**-----------------------------------------------------
* Entity Properties
-------------------------------------------------------*/
#Column()
#Id
#JsonView(UserViews.BasicView.class)
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#JsonView(UserViews.BasicView.class)
private String username;
private String password;
#JsonView(UserViews.BasicView.class)
#ManyToMany(mappedBy = "users",fetch=FetchType.EAGER)
private Set<UsersGroup> usersGroups;
#ManyToOne(targetEntity = AccessRole.class, optional = false,fetch = FetchType.EAGER, cascade=CascadeType.MERGE)
#JoinColumn(name = "user_role")
#JsonView(UserViews.BasicView.class)
private AccessRole userAccessRole;
/**-----------------------------------------------------
* Setters & Getters
-------------------------------------------------------*/
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Set<UsersGroup> getUsersGroups() {
return usersGroups;
}
public void setUsersGroups(Set<UsersGroup> usersGroups) {
this.usersGroups = usersGroups;
}
public AccessRole getUserAccessRole() {
return userAccessRole;
}
public void setUserAccessRole(AccessRole userAccessRole) {
this.userAccessRole = userAccessRole;
}
}
AccessRole Entity
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import socialcreek.user.views.UserViews;
import javax.persistence.*;
import java.util.Set;
#Entity
#Table(name = "access_role")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id")
public class AccessRole {
/**-----------------------------------------------------
* Entity Properties
-------------------------------------------------------*/
#Id
#JsonView(UserViews.BasicView.class)
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#JsonView(UserViews.BasicView.class)
private String roleName;
#JsonView(UserViews.BasicView.class)
#ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
#JoinTable(name = "access_role_permissions")
private Set<AccessPermission> accessPermissions;
/**-----------------------------------------------------
* Setters & Getters
-------------------------------------------------------*/
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public Set<AccessPermission> getAccessPermissions() {
return accessPermissions;
}
public void setAccessPermissions(Set<AccessPermission> accessPermissions) {
this.accessPermissions = accessPermissions;
}
}
AccessPermission Entity
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import socialcreek.user.views.UserViews;
import javax.persistence.*;
#Entity
#Table(name = "access_permission")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id")
public class AccessPermission {
/**-----------------------------------------------------
* Entity Properties
-------------------------------------------------------*/
#Id
#JsonView(UserViews.BasicView.class)
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#JsonView(UserViews.BasicView.class)
private String permissionName;
/**-----------------------------------------------------
* Setters & Getters
-------------------------------------------------------*/
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getPermissionName() {
return permissionName;
}
public void setPermissionName(String permissionName) {
this.permissionName = permissionName;
}
}
Serialization Result:
[ { "id":70, "username":"admin", "usersGroups":[], "userAccessRole":{
"id":68, "roleName":"ROLE_ADMIN", "accessPermissions":[
{
"id":69,
"permissionName":"FULL_ACCESS"
}]} },
{ "id":71, "username":"admin2", "usersGroups":[], "userAccessRole":68}
]
Please, have a look at accessRole and accessPermision information - it's complete only for the user:admin. In case of user:admin2 there is only information about accessRoleId ( no information about roleName, accessPermision)
It happens only when both users have the same accessRole. If I change accessRole of user:admin2 to another role - everythnink will be ok.
I found the similar issue with the correct answer (https://stackoverflow.com/a/27117097/4694022).
The problem is caused by #JsonIdentityInfo. After I removed it - it works ...now I need to find the solution to handle serialization for recursive structure but it's another story ...

spring JPA users, roles authentication - how do I avoid duplicate role entries?

Hi I'm new to spring JPA and spring security, I've spent a while trying to get authentication working based on users and roles.
A user should be able to have many roles, and a role can belong to many users.
There should only be about 4 roles.
I've tried to achieve this with a #Manytomany mapping and a join table.
It more or less works, but when I try to persist a new user with a set of Roles it will add duplicate entries to the roles table (or if I put a unique key on the roles table hits a UK violation).
here's the code:
user
import java.util.Set;
import javax.persistence.*;
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String email;
private String password;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
#ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.ALL)
#JoinTable(joinColumns = #JoinColumn(name = "id"),inverseJoinColumns = #JoinColumn(name = "roleId"))
private Set<Role> roles;
public User(String email, String password, Set<Role> roles) {
super();
this.email = email;
this.password = password;
this.roles = roles;
}
}
role
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
#Entity
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String role;
#ManyToMany(mappedBy = "roles",fetch = FetchType.LAZY)
private Set<User> users;
public Role(String role) {
super();
this.role = role;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public Set<User> getUsers() {
return users;
}
public void setUser(Set<User> users) {
this.users = users;
}
}
ManyToMany is the correct logical relationship type. The reason you are getting too many roles is probably that you create new instances of Role, but if the role already exist in the database, you must load it into the persistence context, and only create a new role if it does not exist.
It would look something like this if you managed the transactions in code, if you're using container managed persistence with #Transactional you should remove the transaction code, and the try-finally block.
try {
List<String> roleNames = Lists.newArrayList("user, admin, superuser");
User user = new User("Peter", "Pan");
List<Role> roles = new ArrayList<>();
entityManager.getTransaction().begin();
for (String roleName : roleNames) {
List<Role> found = entityManager.createQuery("select r from Roles r where r.name = :roleName", Role.class)
.setParameter("roleName", roleName).getResultList();
if (found.isEmpty()) {
Role role = new Role(roleName);
entityManager.persist(role);
roles.add(role);
} else {
roles.addAll(found);
}
}
user.setRoles(roles);
entityManager.persist(user);
entityManager.getTransaction().commit();
} finally {
entityManager.close();
}
Personally I almost never use JPA's ManyToMany, I typically break it down to 2 OneToManyrelations, that way I have an Entity for the mapping table which is otherwise magically created by JPA. This also gives you additional control, for instance if you need to modify or delete a role from a user, you can delete the UserRoleMapping entity directly, instead of having to manipulate list on Role and User. It also giver you more control over cascading.

Resources