How to display Grade of Student in the Subject Using Spring Data Jpa #ManyToMany with 3 tables - spring

Hello I want to display the grade of the student in the specific subject, I want to combine the 3 tables student, subject and grade tables into a student_subject_grade and want it to be returned like this:
"studentList": [
{
"id": "2",
"username": "IN",
"password": "admin",
"fullName": "Depp",
"email": "i#live.com",
"subjectList": [
{
"id": 1,
"name": "Programim 1",
"markList": [
{
"mark": 10,
"description": "Excellent"
}
]
},
{
"id": 2,
"name": "Programim 2",
"markList": [
{
"mark": 9,
"description": "Very Good"
}
]
},
{
"id": 3,
"name": "Calculus",
"markList": [
{
"mark": 8,
"description": "Good"
}
]
},
{
"id": 4,
"name": "Discrete mathematics",
"markList": [
{
"mark": 7,
"description": "Well"
}
]
}
]
}
Here is the database schema:
Database Schema
Here are the models:
Student Model:
package springuniversity.Model;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.persistence.*;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
#Entity
#Table(name="Student")
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
//kjo column name duhet ta ket emrin ekzakt si eshte ne databaze emri i kolones perndryshe nuk punon si duhet dhe mund te krijoen kolona te reja ne tabele
#Column(name="user_id")
#JsonProperty
private Integer user_id;
#Column(name="username")
#JsonProperty
private String username;
#Column(name="password")
private String password;
#Column(name="fullname")
#JsonProperty
private String fullName;
#Column(name="email")
#JsonProperty
private String email;
#ManyToMany
#JoinTable(name = "student_subject", joinColumns = #JoinColumn(name = "student_id", referencedColumnName = "user_id"), inverseJoinColumns = #JoinColumn(name = "subject_id", referencedColumnName = "id"))
#JsonProperty
private List<Subject> subjectList;
public Student()
{
}
public Student(int id, String username, String password, String fullName, String email, List<Subject> subjectList) {
super();
this.user_id = id;
this.username = username;
this.password = password;
this.fullName = fullName;
this.email = email;
this.subjectList=subjectList;
}
public String getId() {
return String.valueOf(user_id);
}
public void setId(int user_id) {
this.user_id = user_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 String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public List<Subject> getSubjectList() {
return subjectList;
}
public void setSubjectList(List<Subject> subjectList) {
this.subjectList = subjectList;
}
}
**
Here is the Subject Model:**
package springuniversity.Model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.persistence.*;
import java.util.List;
import java.util.Set;
#Entity
#Table(name="Subject")
public class Subject {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id")
#JsonProperty
private Integer id;
#Column(name="name")
private String name;
#ManyToMany
#JoinTable(name = "student_subject_marks", joinColumns = #JoinColumn(name = "student_id", referencedColumnName = "user_id"), inverseJoinColumns = #JoinColumn(name = "subject_id", referencedColumnName = "id"))
#JsonProperty
private List<Subject> markList;
public Subject() {
this.id = id;
this.name=name;
}
public void setId(int id)
{
this.id=id;
}
public int getId()
{
return id;
}
public void setName(String name)
{
this.name=name;
}
public String getName()
{
return name;
}
public List<Subject> getMarkList() {
return markList;
}
public void setMarkList(List<Subject> markList) {
this.markList = markList;
}
}
Grade Model:
package springuniversity.Model;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.persistence.*;
import java.util.List;
#Entity
#Table(name="StudentMark")
public class StudentMark {
#Id
private int grade_id;
#Column(name="mark")
private int mark;
#Column(name="description")
private String description;
public int getGrade_id() {
return grade_id;
}
public void setGrade_id(int grade_id) {
this.grade_id = grade_id;
}
public int getMark() {
return mark;
}
public void setMark(int mark) {
this.mark = mark;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
What I've tried so far is to create a List markList in the subject Model, the problem is I don't know how to map 3 foreign keys with spring data jpa or HQL, so far I just managed to display the subjects of the student with 2 foreign keys, how should this kind of problem be approached, I've struggled to find articles that explain this kind of topic properly that's why I asked this question.

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.

Spring doesn't save List in oneToMany relationship

I've this Address Entity
package com.appdeveloperblog.app.ws.io.entity;
import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
#Entity(name = "addresses")
public class AddressEntity implements Serializable {
private static final long serialVersionUID = 3652691377296902875L;
#Id
#GeneratedValue
private long id;
#Column(length = 30, nullable = false)
private String addressId;
#Column(length = 15, nullable = false)
private String city;
#Column(length = 15, nullable = false)
private String country;
#Column(length = 100, nullable = false)
private String streetName;
#Column(length = 7, nullable = false)
private String postalCode;
#Column(length = 10, nullable = false)
private String type;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "users_id")
private UserEntity userDetails;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getAddressId() {
return addressId;
}
public void setAddressId(String addressId) {
this.addressId = addressId;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getStreetName() {
return streetName;
}
public void setStreetName(String streetName) {
this.streetName = streetName;
}
public String getPostalCode() {
return postalCode;
}
public void setPostalCode(String postalCode) {
this.postalCode = postalCode;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public UserEntity getUserDetails() {
return userDetails;
}
public void setUserDetails(UserEntity userDetails) {
this.userDetails = userDetails;
}
}
and this a Users Entity
package com.appdeveloperblog.app.ws.io.entity;
import java.io.Serializable;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
#Entity(name = "users")
public class UserEntity implements Serializable {
private static final long serialVersionUID = -3772691377276902875L;
#Id
#GeneratedValue
private long id;
#Column(nullable = false)
private String userId;
#Column(nullable = false, length = 50)
private String firstName;
#Column(nullable = false, length = 50)
private String lastName;
#Column(nullable = false, length = 120, unique = true)
private String email;
#Column(nullable = false)
private String encryptedPassword;
private String emailVerificationToken;
#Column(nullable = false)
private Boolean emailVerificationStatus = false;
#OneToMany(mappedBy = "userDetails", cascade = CascadeType.ALL)
private List<AddressEntity> addresses;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
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 getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getEncryptedPassword() {
return encryptedPassword;
}
public void setEncryptedPassword(String encryptedPassword) {
this.encryptedPassword = encryptedPassword;
}
public String getEmailVerificationToken() {
return emailVerificationToken;
}
public void setEmailVerificationToken(String emailVerificationToken) {
this.emailVerificationToken = emailVerificationToken;
}
public Boolean getEmailVerificationStatus() {
return emailVerificationStatus;
}
public void setEmailVerificationStatus(Boolean emailVerificationStatus) {
this.emailVerificationStatus = emailVerificationStatus;
}
}
and this function which I use it in service layer to save the data into the Mysql Database
#Override
public UserDto createUser(UserDto user) {
if (userRepository.findByEmail(user.getEmail()) != null)
throw new RuntimeException("Record already exists");
for(int i = 0 ; i < user.getAddresses().size() ; i++)
{
AddressDTO address = user.getAddresses().get(i);
address.setUserDetails(user);
address.setAddressId(utils.generateAddressId(30));
user.getAddresses().set(i, address);
}
ModelMapper modelMapper = new ModelMapper();
UserEntity userEntity = modelMapper.map(user, UserEntity.class);
String publicUserId = utils.generateUserId(30);
userEntity.setUserId(publicUserId);
userEntity.setEncryptedPassword(bCryptPasswordEncoder.encode(user.getPassword()));
UserEntity storedUserDetails = userRepository.save(userEntity);
// BeanUtils.copyProperties(storedUserDetails, returnValue);
UserDto returnValue = modelMapper.map(storedUserDetails, UserDto.class);
return returnValue;
}
after I post the data to the API using Postman POST request it save only the data into the users table and all the data in addresses table have been igonred
POST request Example:
{
"firstName" : "Sergey",
"lastName" : "Kargopolov",
"email" : "tno#test.com",
"password" : "123",
"addresses":[
{
"city":"Vancouver",
"country":"Canada",
"streetName":"123 Street name",
"postalCode": "ABCBA",
"type":"billing"
}
]
}
Below classes are the stripped down version of what you are trying to achieve. Please compare with your classes and it should work fine only difference is I have remove additional fields to test it easily. Check code in UserController map method.
UserEntity.java
#Entity
#Table(name = "users")
public class UserEntity implements Serializable {
private static final long serialVersionUID = 4865903039190150223L;
#Id
#GeneratedValue
private long id;
#Column(length = 50, nullable = false)
private String firstName;
#Column(length = 50, nullable = false)
private String lastName;
#OneToMany(mappedBy = "userDetails", cascade = CascadeType.ALL)
private List<AddressEntity> addresses;
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 List<AddressEntity> getAddresses() {
return addresses;
}
#Override
public String toString() {
return "UserEntity [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", addresses="
+ addresses + "]";
}
public void setAddresses(List<AddressEntity> addresses) {
this.addresses = addresses;
}
}
AddressEntity.java
#Entity(name = "addresses")
public class AddressEntity implements Serializable {
private static final long serialVersionUID = 3652691377296902875L;
#Id
#GeneratedValue
private long id;
#Column(length = 15, nullable = false)
private String city;
#Column(length = 15, nullable = false)
private String country;
#JsonIgnore
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "users_id")
private UserEntity userDetails;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public UserEntity getUserDetails() {
return userDetails;
}
public void setUserDetails(UserEntity userDetails) {
this.userDetails = userDetails;
}
#Override
public String toString() {
return "AddressEntity [id=" + id + ", city=" + city + ", country=" + country + "]";
}
}
UserDto.java
public class UserDto implements Serializable {
private static final long serialVersionUID = 6835192601898364280L;
private long id;
private String firstName;
private String lastName;
private List<AddressDTO> addresses;
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 List<AddressDTO> getAddresses() {
return addresses;
}
public void setAddresses(List<AddressDTO> addresses) {
this.addresses = addresses;
}
}
AddressDTO.java
public class AddressDTO {
private long id;
private String city;
private String country;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
UserController.java
#RestController
public class UserController {
#Autowired
UserRepository repository;
#PostMapping("map")
#ResponseBody
public UserEntity map(#RequestBody UserDto userDto) {
ModelMapper modelMapper = new ModelMapper();
UserEntity userEntity = modelMapper.map(userDto, UserEntity.class);
for (AddressEntity address : userEntity.getAddresses()) {
address.setUserDetails(userEntity);
}
repository.save(userEntity);
return userEntity;
}
}
Sample Request:
{
"firstName" : "Sergey",
"lastName" : "Kargopolov",
"addresses":[
{
"city":"Vancouver",
"country":"Canada"
}
]
}
Output:
{
"id": 7,
"firstName": "Sergey",
"lastName": "Kargopolov",
"addresses": [
{
"id": 8,
"city": "Vancouver",
"country": "Canada"
}
]
}

Hibernate connecting table unique on columns problem

I have problem with hibernate because he creates connecting table (I don't know why will be nice if can tell me where and why), and what is worst he using unique on columns and i cant add more then one user with the same role.
Whole project: https://github.com/dextep/sms/tree/master/src/main/java/pl/popiel/sms
Database look
User.class
package pl.popiel.sms.model.user;
import org.hibernate.validator.constraints.UniqueElements;
import javax.persistence.*;
import javax.validation.constraints.Email;
import java.util.Set;
#Entity
#Table(name="sms_users")
#SequenceGenerator(name = "sms_users_seq", sequenceName = "sms_users_seq", allocationSize = 1)
public class User {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sms_users_seq")
private long id;
#Email
private String email;
private String password;
private String firstName;
private String lastName;
#Column(name="mobile_nr")
private String mobileNumber;
#ElementCollection(targetClass=Role.class)
private Set<Role> roles;
public String getFullName() {
return firstName != null ? firstName.concat(" ").concat(lastName) : "";
}
public User() {
}
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 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 getMobileNumber() {
return mobileNumber;
}
public void setMobileNumber(String mobileNumber) {
this.mobileNumber = mobileNumber;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
Role.class
package pl.popiel.sms.model.user;
import org.hibernate.validator.constraints.UniqueElements;
import org.springframework.stereotype.Indexed;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="sms_roles")
#SequenceGenerator(name = "sms_roles_seq", sequenceName = "sms_roles_seq", allocationSize = 1)
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sms_roles_seq")
private long id;
private String role;
public 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;
}
}
I didn't notice that I miss the #ManyToMany annotation at 'roles' in User class.
Solve:
#ManyToMany
private Set<Role> roles;

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 Hibernate lazy load behavior

I am making a web based app in spring/hibernate. In database I am using onetomany and manytoone relations. I have manytoone relationship in users for location and onetomany relationship for users in location. In both entities I have lazy loading option turned on. As per my understanding if this is turned on, the database query to get the location should not be executed until explicitly called for it but whenever I do a get from the userdao the below query is executed which makes me think that even after having the lazy option turned on it is retrieving the location information. Can anyone let me know what am I doing wrong or is this the expected behavior.
Below is my user entity code
package com.kwisque.database.model;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.*;
#Entity
#Table(name = "USERS")
public class User implements Serializable{
#Id
#Column(name = "USER_ID", unique = true, nullable = false)
private String userId;
#Column(name = "NAME", nullable = true, length = 32)
private String name;
#Column(name = "PASSWORD", nullable = false, length = 64)
private String password;
#Column(name = "EMAIL_ID", nullable = true, length = 128)
private String emailId;
#Column(name = "ACTIVE", nullable = false, length = 1)
private Integer active;
#Column(name = "PROVIDER", nullable = false, length = 32)
private String provider;
#ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.ALL)
#JoinTable(
name = "USER_ROLE",
joinColumns = #JoinColumn(name = "USER_ID"),
inverseJoinColumns = #JoinColumn(name = "ROLE_ID")
)
private Set<Role> roles = new HashSet<>();
//#ManyToOne(fetch = FetchType.LAZY)
//#JoinColumn(name = "LOCATION_ID", nullable = true)
#ManyToOne(fetch=FetchType.LAZY, cascade={javax.persistence.CascadeType.ALL})
#JoinTable(name="USER_LOCATION_INFO",
joinColumns={#javax.persistence.JoinColumn(name="USER_ID")},
inverseJoinColumns={#javax.persistence.JoinColumn(name="LOCATION_ID")})
private Location location;
// #OneToMany(fetch = FetchType.LAZY)
// #JoinColumn(name = "SPECIALIZATION_ID", nullable = true)
#OneToMany(fetch=FetchType.LAZY, cascade={javax.persistence.CascadeType.ALL})
#JoinTable(name="USER_SPECIALIZATION_INFO",
joinColumns={#javax.persistence.JoinColumn(name="USER_ID")},
inverseJoinColumns={#javax.persistence.JoinColumn(name="SPECIALIZATION_ID")})
private Set<Specialization> specialization = new HashSet<>();
public User() {
}
public User(final String userId, final String name, final String password, final String emailId, final Integer active, final String provider, final Set<Role> roles, final Location location) {
this.userId = userId;
this.name = name;
this.password = password;
this.emailId = emailId;
this.active = active;
this.provider = provider;
this.roles = roles;
this.location = location;
}
public String getUserId() {
return userId;
}
public void setUserId(final String userId) {
this.userId = userId;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(final String password) {
this.password = password;
}
public Integer getActive() {
return active;
}
public void setActive(final Integer active) {
this.active = active;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(final Set<Role> roles) {
this.roles = roles;
}
public String getProvider() {
return provider;
}
public void setProvider(final String provider) {
this.provider = provider;
}
public String getEmailId() {
return emailId;
}
public void setEmailId(final String emailId) {
this.emailId = emailId;
}
public Location getLocation() {
return location;
}
public void setLocation(final Location location) {
this.location = location;
}
}
Location entity code
package com.kwisque.database.model;
import java.io.Serializable;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import com.fasterxml.jackson.annotation.JsonIgnore;
#Entity
#Table(name = "LOCATION")
public class Location implements Serializable {
private static final long serialVersionUID = -7153748534015057865L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "LOCATION_ID", unique = true, nullable = false)
private Integer locationId;
#Column(name = "ZIP_CODE", nullable = true, length = 132)
private String zipCode;
#Column(name = "STATE_ABBR", nullable = true, length = 132)
private String stateAbbr;
#Column(name = "LATITUDE", nullable = true, length = 132)
private double latitude;
#Column(name = "LONGITUDE", nullable = true, length = 132)
private double longitude;
#Column(name = "CITY", nullable = true, length = 132)
private String city;
#Column(name = "STATE", nullable = true, length = 132)
private String state;
#JsonIgnore
#OneToMany(fetch = FetchType.LAZY, mappedBy = "location")
private Set<User> users;
public double getLatitude() {
return this.latitude;
}
public void setLatitude(double latitude) {
this.latitude = latitude;
}
public double getLongitude() {
return this.longitude;
}
public void setLongitude(double longitude) {
this.longitude = longitude;
}
public Location() {
}
public Location(Integer locationId, long longitude, String city, long latitude, String zipCode, String state,
String stateAbbr, Set<User> users) {
this.locationId = locationId;
this.longitude = longitude;
this.latitude = latitude;
this.city = city;
this.state = state;
this.stateAbbr = stateAbbr;
this.users = users;
}
public Integer getLocationId() {
return this.locationId;
}
public void setLocationId(Integer locationId) {
this.locationId = locationId;
}
#JsonIgnore
public Set<User> getUser() {
return this.users;
}
#JsonIgnore
public void setUser(Set<User> users) {
this.users = users;
}
public String getCity() {
return this.city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return this.state;
}
public void setState(String state) {
this.state = state;
}
public String getZipCode() {
return this.zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
public String getStateAbbr() {
return this.stateAbbr;
}
public void setStateAbbr(String stateAbbr) {
this.stateAbbr = stateAbbr;
}
}
Query being executed at my get from USerDao
Hibernate: select user0_.USER_ID as USER_ID1_3_0_, user0_.ACTIVE as ACTIVE2_3_0_, user0_.EMAIL_ID as EMAIL_ID3_3_0_, user0_.NAME as NAME4_3_0_, user0_.PASSWORD as PASSWORD5_3_0_, user0_.PROVIDER as PROVIDER6_3_0_, user0_1_.LOCATION_ID as LOCATION1_4_0_, roles1_.USER_ID as USER_ID1_3_1_, role2_.ROLE_ID as ROLE_ID2_5_1_, role2_.ROLE_ID as ROLE_ID1_1_2_, role2_.NAME as NAME2_1_2_ from USERS user0_ left outer join USER_LOCATION_INFO user0_1_ on user0_.USER_ID=user0_1_.USER_ID left outer join USER_ROLE roles1_ on user0_.USER_ID=roles1_.USER_ID left outer join ROLE role2_ on roles1_.ROLE_ID=role2_.ROLE_ID where user0_.USER_ID=?

Resources