Violation of PRIMARY KEY With User Registration in Authorization Server - java

A simple application is created by using oauth2 password flow. The Authorization Server is working as the identity provider and when a new user is registered then the jwt token should be received as a response.
Relationships between entities
User (M) ------------------ Role (M)
Role (M) ------------------ Permission (M)
when I signup with a new user the following error has thrown
Caused by: java.sql.SQLSyntaxErrorException: ORA-02275: such a referential constraint already exists in the table
How can I resolve the primary key violation with JPA? If I use Native Hibernate API then we can use session.merge() but in JPA there is no option like that.
Request
{
"userName": "Nafaz Benzema",
"password": "stackoverflow",
"email": "benz#gmail.com",
"active": "y",
"accNonExpired": "y",
"credentialNonExpired": "y",
"accNonLocked": "y",
"roles" : [
{
"id": 101,
"name": "ROLE_USER",
"permissions": [
{
"id": 10,
"name": "CAN_CREATE"
},
{
"id": 40,
"name": "CAN_READ"
}
]
},
{
"id": 102,
"name": "ROLE_ADMIN",
"permissions": [
{
"id": 10,
"name": "CAN_CREATE"
},
{
"id": 40,
"name": "CAN_READ"
},
{
"id": 20,
"name": "CAN_UPDATE"
},
{
"id": 40,
"name": "CAN_DELETE"
}
]
}
]
}
Entity classes
User class
#Entity
#Table(name = "USER90",schema = Schema.TESTDB,uniqueConstraints = {
#UniqueConstraint(name = "userName",columnNames = "USER_NAME"),
#UniqueConstraint(name = "email",columnNames = "EMAIL")
})
#Getter
#Setter
public class User {
#Id
#SequenceGenerator(name = "USER_ID_GEN",sequenceName = Schema.TESTDB+".USER_ID_SEQ",initialValue = 1003,allocationSize = 1)
#GeneratedValue(generator = "USER_ID_GEN",strategy = GenerationType.SEQUENCE)
#Column(name = "USER_ID")
private int userId;
#Column(name = "USER_NAME",nullable = false)
private String userName;
#Column(name = "PASSWORD",nullable = false)
private String password;
#Column(name = "EMAIL",nullable = false)
private String email;
#Column(name = "ACTIVE",nullable = false)
private String active;
#Column(name = "ACC_NON_EXPIRED")
private String accNonExpired;
#Column(name = "CREDENTIAL_NON_EXPIRED")
private String credentialNonExpired;
#Column(name = "ACC_NON_LOCKED")
private String accNonLocked;
#ManyToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinTable(name = "USER_ROLE",joinColumns = {#JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID")},
inverseJoinColumns = {#JoinColumn(name = "ROLE_ID",referencedColumnName = "ID")})
private Set<Role> roles;
}
Role class
#Entity
#Table(name = "ROLE",schema = Schema.TESTDB,uniqueConstraints = {
#UniqueConstraint(name = "name",columnNames = "NAME")
})
#Getter
#Setter
public class Role {
#Id
#Column(name = "ID")
private long id;
#Enumerated(EnumType.STRING)
#Column(name = "NAME")
private ERole name;
// bi-directional
/* #ManyToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private Set<User> users;*/
// uni-directional
#ManyToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinTable(name = "PERMISSION_ROLE",joinColumns = {#JoinColumn(name = "ROLE_ID",referencedColumnName = "ID")},
inverseJoinColumns = {#JoinColumn(name = "PERMISSION_ID",referencedColumnName = "ID")})
private Set<Permission> permissions;
}
Permission class
#Entity
#Table(name = "PERMISSION",schema = Schema.TESTDB,uniqueConstraints = {
#UniqueConstraint(name = "name",columnNames = "NAME")
})
#Getter
#Setter
public class Permission {
#Id
#Column(name = "ID")
private long id;
#Enumerated(EnumType.STRING)
#Column(name = "NAME")
private EPermission name;
}
Service class
public Response userRegistration(SignupRequest signup) {
if(!Objects.isNull(userDAO.findUserByEmail(signup.getEmail()).orElse(null)))
throw new UserIsExistedException(String.format("User is existed with %s",signup.getEmail()));
try {
User user=new User();
String password="{bcrypt}";
password = password.concat(BCrypt.hashpw(signup.getPassword(),BCrypt.gensalt(12)));
user.setUserName(signup.getUserName());
user.setEmail(signup.getEmail());
user.setPassword(password);
user.setActive("y");
user.setAccNonExpired("y");
user.setCredentialNonExpired("y");
user.setAccNonLocked("y");
user.setRoles(signup.getRoles());
Set<Role> roles = new HashSet<>();
user.getRoles().forEach(role->{
roles.add(role);
Set<Permission> permissions=new HashSet<>();
role.getPermissions().forEach(perm->{
permissions.add(perm);
});
role.setPermissions(permissions);
});
user.setRoles(roles);
userDAO.save(user);
LOGGER.info("user is saved and response is returned successfully");
return new Response(user.getEmail(), authenticationProvider.obtainToken(user.getEmail(), user.getPassword()).toString());
}catch (NumberFormatException ex){
LOGGER.error("NumberFormat Exception");
throw new NumberFormatException("NumberFormat Exception");
}
catch (Exception ex){
LOGGER.error("invalid username or password");
throw new BadCredentialsException("invalid username or password",ex);
}
}
Note - If you need more resource here GitHub link
github_link
config file
spring:
datasource:
url: jdbc:oracle:thin:#localhost:1521:orcl
username: TESTDB
password: 14292
driver-class-name: oracle.jdbc.OracleDriver
jpa:
database-platform: org.hibernate.dialect.Oracle10gDialect
hibernate:
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
ddl-auto: update

I have solved it by changing the CascadeType.
#ManyToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinTable(name = "USER_ROLE",joinColumns = {#JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID")},
inverseJoinColumns = {#JoinColumn(name = "ROLE_ID",referencedColumnName = "ID")})
private Set<Role> roles;
when I use CascadeType.ALL then it gives priority to PERSIST (save) which throws Primary Key Violation in my case. When roles assigned to the user and if it is available in the role table then I need to Merge it rather than Save. So I have changed the CascadeType from ALL to MERGE.
#ManyToMany(cascade = CascadeType.MERGE,fetch = FetchType.EAGER)
#JoinTable(name = "USER_ROLE",joinColumns = {#JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID")},
inverseJoinColumns = {#JoinColumn(name = "ROLE_ID",referencedColumnName = "ID")})
private Set<Role> roles;

Related

Design entities to join three tables using a mapping table Using JPA

Please help me write entities the proper way, so that it can be easily fetched using JPA. I have a DB design as below image:
Table Design Structure
I have created entities
#Entity
#Table(name = "ROLE")
public class Role {
#Id
#GeneratedValue()
#GenericGenerator(name = "uuid2", strategy = "uuid2")
#Column(name = "ROLE_ID")
private UUID roleId;
#Column(name = "ROLE_NAME")
private String roleName;
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE })
#JoinTable(name = "ROLE_MODULE_PERMISSION_MAP",
joinColumns = #JoinColumn(name = "ROLE_ID"),
inverseJoinColumns = #JoinColumn(name = "MODULE_ID"))
private List<Module> modules;
}
#Entity
#Table(name = "MODULE")
public class Module {
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "uuid2")
#Column(name = "MODULE_ID", columnDefinition = "BINARY(16)")
private UUID uuid;
#Column(name = "MODULE_NAME")
private String moduleName;
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE })
#JoinTable(name = "ROLE_MODULE_PERMISSION_MAP",
joinColumns = #JoinColumn(name = "MODULE_ID"),
inverseJoinColumns = #JoinColumn(name = "PERMISSION_ID"))
private List<Permission> permission;
}
#Entity
#Table(name = "PERMISSION")
public class Permission {
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "uuid2")
#Column(name = "PERMISSION_ID", columnDefinition = "BINARY(16)")
private UUID permissionId;
#Column(name = "PERMISSION_TYPE")
private String permissionType;
#ManyToMany
#JoinTable(name = "ROLE_MODULE_PERMISSION_MAP",
joinColumns = #JoinColumn(name = "PERMISSION_ID"),
inverseJoinColumns = #JoinColumn(name = "ROLE_ID"))
#MapKeyJoinColumn(name="MODULE_ID")
#ElementCollection
private Map<Role, Module> modulePermissions;
}
#Entity
#Table(name = "ROLE_MODULE_PERMISSION_MAP")
public class RoleModulePermissionMap implements Serializable {
#Id
#Column(name = "ROLE_ID", columnDefinition = "BINARY(16)")
private UUID roleId;
#Id
#Column(name = "MODULE_ID", columnDefinition = "BINARY(16)")
private UUID moduleId;
#Id
#Column(name = "PERMISSION_ID", columnDefinition = "BINARY(16)")
private UUID permissionId;
}
I am trying to fetch using:
Role role = roleRepository.findByroleName(roleName)
Where roleRepository is
#Repository
public interface RoleRepository extends JpaRepository<Role, UUID> {
Role findByroleName(String roleName);
}
I want to fetch the Module and Permissions for a specific Role. something like:
{
"roleName": "Development",
"roleAcronym": "DEV",
"permissionGroup": "AdminUser",
"modules": [
{
"moduleName": "Agreement",
"permission": [
{
"permissionName": "CREATE",
"permissionType": "C"
},
{
"permissionName": "UPDATE",
"permissionType": "U"
},
{
"permissionName": "READ",
"permissionType": "R"
}
]
},
{
"moduleName": "Reports",
"permission": [
{
"permissionName": "DELETE",
"permissionType": "C"
},
{
"permissionName": "UPDATE",
"permissionType": "U"
},
{
"permissionName": "READ",
"permissionType": "R"
}
]
}
]
}
I am using Spring Boot Starter JPA - 2.6.2 version.

many-to-many relationship springboot mysql

i have 2 models User and roles , its a many to many relation ship.
i need to add a user and give him a specific role already present in my data base.
------User------
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="user_Id")
private int userId;
#Column(name="name")
private String name;
#Column(name="lastname")
private String lastname;
#Column(name="email")
private String email;
#Column(name="password")
private String password;
#Column(name="isActive")
private boolean isActive;
#Column(name="lastActive")
private String lastActive;
#Column(name="createdDate")
private String createdDate;
#Column(name="isBlocked")
private boolean isBlocked;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "institution_id", nullable = false)
#JsonIgnoreProperties(value = {"user"})
private Institution institution;
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.DETACH,CascadeType.MERGE,CascadeType.REFRESH})
#JoinTable(name = "user_has_role",
joinColumns = {
#JoinColumn(name = "user_id", referencedColumnName = "user_id",
nullable = false, updatable = true)},
inverseJoinColumns = {
#JoinColumn(name = "role_id", referencedColumnName = "role_id",
nullable = false, updatable = true)})
#JsonIgnoreProperties(value = {"users"})
private Set<Role> roles = new HashSet<>();
}
--------Roles--------
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Entity
#Table(name = "role")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="role_Id")
private int roleId;
#Column(name="name")
private String name;
#Column(name="description")
private String description;
#ManyToMany(mappedBy = "roles", fetch = FetchType.LAZY)
#JsonIgnoreProperties(value = {"roles"})
private Set<User> users = new HashSet<>();
}
and the application's controller
#PostMapping("/addUser")
public String addUser(#RequestBody User user) {
userrepository.save(user);
return "user saved with name: " + user.getName();
}
and this is the json body i send with the api request.
{
"userId" : 7,
"name": "test",
"lastname": "testlast",
"email": "testtest#yahoo.com",
"password": "test123",
"lastActive": "04/05/21",
"createdDate": "02/04/20",
"institution": {
"institutionId": 4
},
"roles": [
{
"roleId": 2
}
],
"active": false,
"blocked": true
}
everything worls just fine to my user-has-role table a record is added with the userId = 7 and roleId=2
but the problem is that the table role is getting updated and the fields name and description are getting erased and replaced by null values...
any ideas please
You have added CascadeType.PERSIST to User and Role #ManyToMany join.
When the User entity is persisted to the EntityManager, it will also persist the Role entity. As you are passing the primary key in the request payload for Role it will create/update the Role table.
You need to remove the CascadeType.PERSIST from joining and it will work as expected.
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH })
#JoinTable(name = "user_has_role",
joinColumns = {
#JoinColumn(name = "user_id", referencedColumnName = "user_id",
nullable = false, updatable = true)},
inverseJoinColumns = {
#JoinColumn(name = "role_id", referencedColumnName = "role_id",
nullable = false, updatable = true)})
#JsonIgnoreProperties(value = {"users"})
private Set<Role> roles = new HashSet<>();

spring-boot-starter-data-jpa#ManyToMany collection not populating

I'm getting a problem with the #ManyToMany collections not populating on data load. I've tried FetchType.LAZY and FetchType.EAGER with no changes in the result.
When I am printing the User Object the collection Object of Roles is empty.
User [userId=2, firstName=Ajay, lastName=C, email=admin.demo#gmail.com, password=12345, roles=[]]
Also tried by adding referenced columns. But not worked.
Please assist in this.
User and Roles Entities as follows.
#Entity
#Table(name = "\"USER\"", schema = "\"PLATFORM_PROD_IOT\"", uniqueConstraints = {
#UniqueConstraint(columnNames = { "\"EMAIL_ID\"" }) })
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Size(min = 1, max = 50)
#Column(name = "\"USER_ID\"")
private Long userId;
#NotBlank
#Size(min = 3, max = 50)
#Column(name = "\"FIRST_NAME\"")
private String firstName;
#NotBlank
#Size(min = 3, max = 50)
#Column(name = "\"LAST_NAME\"")
private String lastName;
#NaturalId
#NotBlank
#Size(max = 50)
#Email
#Column(name = "\"EMAIL_ID\"")
private String email;
#NotBlank
#Size(min = 3, max = 100)
#Column(name = "\"PASSWORD\"")
private String password;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "\"USER_ROLE_MAPPING\"", schema = "\"\PLATFORM_PROD_IOT\"", joinColumns = #JoinColumn(name = "\"USER_ID\""), inverseJoinColumns = #JoinColumn(name = "\"ROLE_ID\""))
private Set<Role> roles = new HashSet<>();
//Getters and Setters
}
#Entity
#Table(name = "\"ROLE\"",schema="\"PLATFORM_PROD_IOT\"")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="\"ROLE_ID\"")
private Long roleId;
#Column(name="\"ROLE_NAME\"")
private RoleName name;
//Getters and Setters
}
You could try this -
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "\"USER_ROLE_MAPPING\"", catalog = "\"PLATFORM_PROD_IOT\"", joinColumns = {
#JoinColumn(name = "\"USER_ID\"", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "\"ROLE_ID\"",
nullable = false, updatable = false) })
private Set<Role> roles = new HashSet<>();
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
Here I have added
cascade = CascadeType.ALL
catalog = "\"PLATFORM_PROD_IOT\"" instead of schema = "\"PLATFORM_PROD_IOT\""
nullable = false, updatable = false in #JoinColumn
Also have found an related -
collection not populating in many to many relationship

"Save transient object before flushing" error [duplicate]

This question already has answers here:
How to fix the Hibernate "object references an unsaved transient instance - save the transient instance before flushing" error
(33 answers)
Closed 7 years ago.
I tried to save one of my entity Site containing a User, this User is registered in database and I don't want to save it too. The problem is when I try to save the site, I get the following error :
org.springframework.dao.InvalidDataAccessApiUsageException: object references an unsaved transient instance - save the transient instance before flushing: com.project.netmg.bo.impl.User; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.project.netmg.bo.impl.User
I think, it try to save the user too but it's not that I want. I just want to search the good user and assign it to the site.
java code :
public String saveSite(#Valid #ModelAttribute SiteForm siteForm, BindingResult bindingResult, Model uiModel) {
populateSiteforSave(siteForm);
_siteService.saveSite(siteForm.getSite());
return WebConstants.REDIRECT_TO_VIEW_SITE_URL + siteForm.getSite().getSiteName();
}
private void populateSiteforSave(SiteForm siteForm) {
siteForm.getSite().setCountry((Country) _countryService.getCountryByName(siteForm.getSite().getCountry().getName()));
siteForm.getSite().setBusiness((Business) _businessService.getBusinessById(siteForm.getSite().getBusiness().getId()));
siteForm.getSite().setStatus((Status) _statusService.getStatusById(siteForm.getSite().getStatus().getId()));
if (!siteForm.getLocalItFullName().isEmpty()) {
siteForm.getSite().setLocalIt(_userService.findUserByUserFullName(siteForm.getLocalItFullName())); // user
} else {
siteForm.getSite().setLocalIt(null);
}
if (!siteForm.getRifFullName().isEmpty()) {
siteForm.getSite().setRif(_userService.findUserByUserFullName(siteForm.getRifFullName())); //user
} else {
siteForm.getSite().setRif(null);
}
if (siteForm.getSite().getLocalContact().getId() != null) {
siteForm.getSite().setLocalContact((User) _userService.findUserByUsername(siteForm.getSite().getLocalContact().getUsername())); //user
}
}
Site class:
#Entity
#Table(name = "SITE", uniqueConstraints = { #UniqueConstraint(columnNames = { "SITE_COUNTRY_ID", "SITE_NAME" }) })
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED, withModifiedFlag = true)
public class Site implements ISite {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = -390717603276436784L;
/** The id. */
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "SITE_ID", unique = true, nullable = false)
private long id;
/** The site address. */
#Column(name = "SITE_ADDRESS", length = BusinessConstants.SITE_ADDRESS)
private String address;
/** The site analog phone number. */
#Column(name = "SITE_ANALOG_PHONE_NUMBER", length = BusinessConstants.SITE_ANALOG_PHONE_NUMBER)
private String analogPhoneNumber;
/** The site comment. */
#Column(name = "SITE_COMMENT", length = BusinessConstants.SITE_COMMENT)
private String comment;
/** The site entity code. */
#Digits(integer = 3, fraction = 0, message = "Please enter max 3 digits")
#Column(name = "SITE_ENTITY_CODE", nullable = false)
private long entityCode;
/** The site invoice code. */
#Digits(integer = 10, fraction = 0, message = "Please enter max 10 digits")
#Column(name = "SITE_INVOICE_CODE", nullable = false)
private long invoiceCode;
/** The site local it phone. */
#Column(name = "SITE_LOCAL_IT_PHONE", length = BusinessConstants.SITE_LOCAL_IT_PHONE)
private String localItPhone;
/** The site name. */
#NotBlank
#Column(name = "SITE_NAME", nullable = false, length = BusinessConstants.SITE_NAME)
private String siteName;
/** The site subnet. */
#NotBlank
#Column(name = "SITE_SUBNET", nullable = false, length = BusinessConstants.SITE_SUBNET)
private String subnet;
/** The site user number. */
#Digits(integer = 4, fraction = 0, message = "Please enter max 4 digits")
#Column(name = "SITE_USER_NUMBER")
private Long userNumber;
/** The business. */
#Valid
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SITE_BUSINESS_ID", nullable = false)
private Business business;
/** The country. */
#Valid
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "SITE_COUNTRY_ID")
private Country country;
/** The local contact. */
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SITE_LOCAL_CONTACT", nullable = true)
private User localContact;
/** The local it. */
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SITE_LOCAL_IT", nullable = true)
private User localIt;
/** The rif. */
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SITE_RIF", nullable = true)
private User rif;
/** The status. */
#Valid
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SITE_STATUS_ID", nullable = false)
private Status status;
/** The end date. */
#Column(name = "SITE_END_DATE")
#Temporal(TemporalType.TIMESTAMP)
#DateTimeFormat(iso = ISO.DATE_TIME)
private Date endDate = null;
#ManyToMany(targetEntity = User.class, mappedBy = "userSites", fetch = FetchType.LAZY)
#NotAudited
private Set<IUser> siteUsers;
User class :
#Entity
#Table(name = "USERS")
public class User implements IUser {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 6741623705511494367L;
private static final String USER_ID = "USER_ID";
/** The user id. */
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = USER_ID)
private Long id;
/** The user first name. */
#Column(name = "FIRSTNAME", length = BusinessConstants.USER_FIRSTNAME, nullable = false)
#NotNull
private String userFirstName;
/** The user last name. */
#Column(name = "LASTNAME", length = BusinessConstants.USER_LASTNAME, nullable = false)
#NotNull
private String userLastName;
/** The user email. */
#Column(name = "EMAIL", length = BusinessConstants.USER_EMAIL)
#NotNull
private String userEmail;
/** The user uid. */
#Column(name = "LOGIN", length = BusinessConstants.USER_LOGIN, nullable = false, unique = true)
#NotNull
private String username;
#Valid
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PROFILE_ID", referencedColumnName = "PROF_ID", nullable = false)
private Profile profile;
#BatchSize(size = 20)
#ManyToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST }, fetch = FetchType.LAZY,targetEntity=Site.class)
#JoinTable(name = "USER_SITE",
joinColumns = { #JoinColumn(name = USER_ID, nullable = false) },
inverseJoinColumns = { #JoinColumn(name = "SITE_ID", nullable = false) })
private Set<ISite> userSites;
#ManyToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST }, fetch = FetchType.LAZY,targetEntity=Region.class)
#JoinTable(name = "USER_REGION",
joinColumns = { #JoinColumn(name = USER_ID, nullable = false) },
inverseJoinColumns = { #JoinColumn(name = "REGION_ID", nullable = false) })
private Set<IRegion> userRegions;
#BatchSize(size = 20)
#ManyToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST }, fetch = FetchType.LAZY,targetEntity=Zone.class )
#JoinTable(name = "USER_ZONE",
joinColumns = { #JoinColumn(name = USER_ID, nullable = false) },
inverseJoinColumns = { #JoinColumn(name = "ZONE_ID", nullable = false) })
private Set<IZone> userZones;
#BatchSize(size = 20)
#ManyToMany(cascade = { CascadeType.MERGE, CascadeType.PERSIST }, fetch = FetchType.LAZY,targetEntity=Country.class )
#JoinTable(name = "USER_COUNTRY",
joinColumns = { #JoinColumn(name = USER_ID, nullable = false) },
inverseJoinColumns = { #JoinColumn(name = "COUNTRY_ID", nullable = false) })
private Set<ICountry> userCountries;
#Transient
private Collection<? extends GrantedAuthority> authorities;
#Transient
private String userFullName;
You can't really have it both ways. If the object graph includes the User, then it will (have to) be persisted if changed in your code. Have you considered what it evens means to fetch a Site (including a User), then change the localContact and persist the Site again?
If you want the localContact to be settable in the object graph, but not persisted, then it can be annotated with #Transient:
/** The local contact. */
#Transient
private User localContact;
Hope that helps.
Cheers,
Have you annotate cascade=CascadeType.ALL to your entity object mapping
eg :
#ManyToOne(cascade = CascadeType.ALL)
private String entityType;

Hibernate deletes Lazy loaded many-to-many collection when a get call is made on the collection

I am trying to fetch roles from database using hibernate in java application. I am undergoing a many to many mapping for the same.
After fetching the data, it is getting deleted from database. I am not calling the delete method either still the deletion occurs.
Role.java
#Entity
#Table(name = "ROLE")
public class Role extends BaseModel implements java.io.Serializable {
private Long id;
private Set<User> users = new HashSet<User>(0);
#Id
#Column(name = "ID", unique = true, nullable = false, scale = 0)
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "roles")
#JoinTable(name = "USERROLEMAP", joinColumns = { #JoinColumn(name = "ROLEID", nullable = false, updatable = false) }, inverseJoinColumns = { #JoinColumn(name = "USERID", nullable = false, updatable = false) })
public Set<User> getUsers() {
return this.users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
}
User.java
#Cacheable
#Entity
#Table(name = "USER", uniqueConstraints = #UniqueConstraint(columnNames = "LOGINID"))
public class User extends BaseModel implements java.io.Serializable {
private Long id;
private Set<Role> roles = new HashSet<Role>(0);
#Id
#Column(name = "ID", unique = true, nullable = false, scale = 0)
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "USERROLEMAP", joinColumns = { #JoinColumn(name = "USERID", nullable = false, updatable = false) }, inverseJoinColumns = { #JoinColumn(name = "ROLEID", nullable = false, updatable = false) })
public Set<Role> getRoles() {
return this.roles;
}
}
We are trying to get the roles from database using the following code snippet
public List<String> rolesAsGA() {
List<String> proxyUserRoles = new ArrayList<String>();
Iterator<Role> itr = getRoles().iterator();
while (itr.hasNext()) {
proxyUserRoles.add(new SimpleGrantedAuthority(itr.next()
.getRoleName()));
}
return proxyUserRoles;
}
after fetching the roles the data (corresponding role) is getting deleted simultaneously, can anyone tell me why?
Edit - We are debugging in eclipse and hibernate is marking the collection for removal since, currentPersistor becomes null for the collection entity. Will debug further and post any update to it.
Edit 1 We missed to mention that, User was #Cacheable, and was being fetched from ehcache, When the getRole call was being made, the collection gets loaded by then prompty queued for deletion. Removing the #Cacheable annotation fixes the problem.
Do i create a seperate question regarding #Cacheable and manytomany or shall i just update this question and hope for a proper solution?
I guess this is causing an issue
private Set<Role> roles = new HashSet<Role>(0);
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "USERROLEMAP", joinColumns = { #JoinColumn(name = "USERID", nullable = false, updatable = false) }, inverseJoinColumns = { #JoinColumn(name = "ROLEID", nullable = false, updatable = false) })
public Set<Role> getRoles() {
return this.roles;
}
It should be
private Set<Role> roles;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "USERROLEMAP", joinColumns = { #JoinColumn(name = "USERID", nullable = false, updatable = false) }, inverseJoinColumns = { #JoinColumn(name = "ROLEID", nullable = false, updatable = false) })
public Set<Role> getRoles() {
if(roles == null){
return new HashSet<Role>;
}
return this.roles;
}
Because whenever getRoles is called, it will always return an empty set. So hibernate is assuming that you have actually emptied/deleted the set and deleting them in Database

Categories

Resources