Spring boot lazy loaded entity not loading all properties - java

I have a Spring Boot 2.1.13 (Java 8) project with MySQL 8.0.21 and mysql-connector-java version: 8.0.22 with the following classes.
#Entity
#Table(name = "user")
#JsonIgnoreProperties({"type", "location", "hibernateLazyInitializer", "handler"})
public class User implements java.io.Serializable {
private UserType userType;
private Location location;
private long id;
private String name;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "usertypeid")
public UserType getUserType() {
return this.userType;
}
public void setUserType(UserType userType) {
this.userType = userType;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "locationid", nullable = false)
public Location getLocation() {
return this.location;
}
public void setLocation(Location location) {
this.location = location;
}
#Column(name = "name", nullable = false)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
#Entity
#Table(name = "usertype")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class UserType implements java.io.Serializable {
private long id;
private String name;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
#Column(name = "name", nullable = false)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
#Entity
#Table(name = "location")
public class Location implements java.io.Serializable {
private long id;
private String name;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
#Column(name = "name", nullable = false)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
I fetch the user record using the following code
User user = userRepository.findById(userId).orElse(null);
Now when I try to fetch the UserType object from my user object, I don't get any values, except the ID. For example, when I run the following command, it gives me a null value
user.getUserType().getName()
However, when I run the following code, I get the expected not null value.
user.getLocation().getName()
Both userType and location are lazy-loaded, however, the values of only location are available in the code.
I have checked the values in DB tables. They are not null for both user type and location.
I have checked the values in the debugger as well. The Location object seems to be a proxy, however, the UserType not a hibernate proxy object. I am not sure if this is an issue.

Related

Child entity elements not persisting in one to many mapping with hibernate and spring data jpa

I have used spring boot with hibernate. And swagger to generate the dtos and the api interface.
There are two entities. The project entity is the parent and application entity is the child. Have create a onetomany relationship. But when i try to persist. I see not applications getting added for a project.
Project Entity:
#Entity
#Table(name="ProjectEntity")
public class ProjectEntity {
#Id
#Column(name = "ProjectGuid", length = 36, nullable = false, unique = true)
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#Column(name = "Name")
private String name;
#OneToMany(mappedBy="projectApp", cascade = CascadeType.ALL)
private List<ApplicationEntity> apps=new ArrayList<>();
public ProjectEntity() {
}
public ProjectEntity(Long id, String name) {
this.id = id;
this.name = name;
}
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 List<ApplicationEntity> getApps() {
return apps;
}
public void setApps(List<ApplicationEntity> apps) {
this.apps = apps;
}
}
Application Entity:
#Entity
#Table(name="ApplicationEntity")
public class ApplicationEntity {
#Id
#Column(name = "Name", length = 36, nullable = false, unique = true)
private String name;
private String repositoryUrl;
#ManyToOne
#Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE)
#JoinColumn(name = "ProjectGuid")
private ProjectEntity projectApp;
public ApplicationEntity() {
}
public ApplicationEntity(String name, String repositoryUrl) {
this.name = name;
this.repositoryUrl = repositoryUrl;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRepositoryUrl() {
return repositoryUrl;
}
public void setRepositoryUrl(String repositoryUrl) {
this.repositoryUrl = repositoryUrl;
}
public ProjectEntity getProjectApp() {
return projectApp;
}
public void setProjectApp(ProjectEntity projectApp) {
this.projectApp = projectApp;
}
}
Controller operation:
ProjectEntity project = projectService.getProject(projectName);
List<ApplicationEntity> appList = new ArrayList<>();
ApplicationEntity appEntity = new ApplicationEntity(app.getName(), app.getRepositoryUrl());
applicationRepository.save(appEntity);
appList.add(appEntity);
project.setApps(appList);
projectRepository.save(project);
You need to set the id of the ProjectEntity on the owning side (which is the ApplicationEntity)
appEntity.setProjectApp(project);
Otherwise hibernate (and your database) does not know to which parent a ApplicationEntity belongs.
Here is an example many to one relation with spring data jpa :
#Data
#MappedSuperclass
public class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
}
#Data
#Entity
public class Question extends BaseEntity{
private String questionText;
private int anketId;
private int subjectId;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "question")
List<Answer> answers;
}
#Data
#Entity
public class Answer extends BaseEntity{
private String answerText;
private String code;
private int score;
private int priority;
private boolean isValidAnswer;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "question_id", referencedColumnName = "id", insertable = false, updatable = false)
private Question question;
}
#DataJpaTest
public class QuestionRepositoryTest {
#Autowired
TestEntityManager entityManager;
#Autowired
QuestionRepository sut;
#Test
public void it_should_create_question_wiht_answers() {
Question question = new Question();
question.setSubjectId(1);
question.setAnketId(1);
question.setQuestionText("test question");
Answer answer = new Answer();
answer.setAnswerText("answer");
answer.setCode("1a");
answer.setPriority(0);
answer.setValidAnswer(true);
question.setAnswers(Arrays.asList(answer));
entityManager.persistAndFlush(question);
List<Question> questionList = sut.findAll();
assertThat(questionList).containsExactly(question);
assertThat(questionList.get(0).getAnswers().size()).isGreaterThan(0);
}
}

Hibernate #ManyToMany relationship with composite keys that share a property

So I am working with a database that uses a lot of composite keys. I am trying to use JPA/hibernate to set up an JoinTable for one of the relationships. Here is a trimmed down example of what is being done
Parent Class
#Entity
#Table(name = "PROTAGONIST")
public class Protagonist {
private Integer id;
private String name;
#Id
#Column(name = "id", nullable = false)
public Integer getId() {return id;}
public void setId(Integer id) { this.id = id;}
#Column(name = "pro_name", nullable = false, length = 50)
public String getName() {return name;}
public void setName(String name) { this.name = name;}
}
Item ID Class
#Embeddable
public class ItemId {
private int pId;
private short invSlot;
#Column(name = "P_Id", nullable = false)
public int getpId() {return pId;}
public void setpId(int pId) { this.pId = pId;}
#Column(name = "Inv_SlotNum", nullable = false)
public short getInvSlot() {return invSlot;}
public void setInvSlot(short invSlot) { this.invSlot = invSlot;}
}
Item Class
public class Item {
private ItemId id;
private String itemName;
private Double cost;
private Set<Buff> buffs;
#EmbeddedId
public ItemId getId() {return id;}
public void setId(ItemId id) { this.id = id;}
#Column(name = "Item_Name", nullable = false, length = 100)
public String getItemName() {return itemName;}
public void setItemName(String name) { this.itemName = name;}
#Column(name = "Item_Cost")
public Double getCost() {return cost;}
public void setCost(Double cost) { this.cost = cost;}
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "BUFFSONITEMS",
joinColumns = {
#JoinColumn(name = "P_Id", referencedColumnName = "P_Id"),
#JoinColumn(name = "Inv_SlotNum", referencedColumnName = "Inv_SlotNum")
},
inverseJoinColumns = {
#JoinColumn(name = "P_Id", referencedColumnName = "P_Id"),
#JoinColumn(name = "Buff_SlotNum", referencedColumnName = "Buff_SlotNum")
}
)
public Set<Buff> getBuffs() {return buffs;}
public void setBuffs(Set<Buff> buffs) { this.buffs = buffs;}
}
Buff ID Class
#Embeddable
public class BuffId {
private Integer pId;
private Short buffSlotNum;
#Column(name = "P_Id", nullable = false)
public Integer getpId() {return pId;}
public void setpId(Integer pId) { this.pId = pId;}
#Column(name = "Buff_SlotNum", nullable = false)
public Short getBuffSlotNum() {return buffSlotNum;}
public void setBuffSlotNum(Short buffSeqNum) { this.buffSlotNum = buffSeqNum;}
}
Buff Class
#Entity
#Table(name = "BUFF")
public class Buff {
private BuffId id;
private String buffName;
private Long duration;
private Set<Item> buffedItems;
#EmbeddedId
public BuffId getId() {return id;}
public void setId(BuffId id) { this.id = id;}
#Column(name = "Buff_Name", nullable = false, length = 100)
public String getBuffName() {return buffName;}
public void setBuffName(String name) { this.buffName = name;}
#Column(name = "Duration", nullable = false)
public Long getDuration() {return duration;}
public void setDuration(Long duration) { this.duration = duration;}
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "BUFFSONITEMS",
joinColumns = {
#JoinColumn(name = "P_Id", referencedColumnName = "P_Id"),
#JoinColumn(name = "Buff_SlotNum", referencedColumnName = "Buff_SlotNum")
},
inverseJoinColumns = {
#JoinColumn(name = "P_Id", referencedColumnName = "P_Id"),
#JoinColumn(name = "Inv_SlotNum", referencedColumnName = "Inv_SlotNum")
}
)
public Set<Item> getBuffedItems() {return buffedItems;}
public void setBuffedItems(Set<Item> buffedItems) { this.buffedItems = buffedItems;}
}
Whenever I attempt to start Spring Boot I get the following exception org.hibernate.MappingException: Repeated column in mapping for collection: com.blankd.composite.key.Item.buffs column: P_Id. The BUFFSONITEMS table uses all 3 columns as part of the primary key for each row in that table. All three columns also have forgein key constraints on the respective tables. This means that P_Id has a forgein key constraint on both Buff and Item.
I am not sure what I am doing wrong as I need the P_Id in order to uniquely identify the row in each table.
You have to choose one side of the many-to-many relationship to be the "owning" side. The "inverse" side must then use the mappedBy element.
If you were to choose Item.buffs to be the owning side, you would map Buff.buffedItems like this:
#ManyToMany(mappedBy="buffs")
public Set<Item> getBuffedItems() {return buffedItems;}

Hibernate many-to-one mapping set previous foreign keys as NULL

I am new in Hibernate association mapping. When I tried to implement a small mapping logic all the previous foreign key is automatically update to Null
File Employee.java
#Entity
public class Employee extends CommonFields implements Serializable {
private static final long serialVersionUID = -723583058586873479L;
#Id
#Column(name = "id", insertable = false, updatable = false)
#GeneratedValue
private Long id;
private String employeeName;
#OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.LAZY)
#JoinColumn(name = "emp_id", referencedColumnName = "id")
private List<Education> education;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public List<Education> getEducation() {
return education;
}
public void setEducation(List<Education> education) {
this.education = education;
}
}
File Education.java
#Entity
public class Education extends CommonFields implements Serializable {
public Education() {
}
private static final long serialVersionUID = -723583058586873479L;
#Id
#Column(name = "id", insertable = false, updatable = false)
#GeneratedValue
private Long id;
private String course;
#ManyToOne
#JoinColumn(name = "emp_id")
private Employee employee;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCourse() {
return course;
}
public void setCourse(String course) {
this.course = course;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
}
Please anyone suggest a solution. I really stuck on this part last two days.

How to fix org.hibernate.MappingException?

I'm new to JPA and getting this error when trying to set UserContact Entity.
Caused by: org.hibernate.MappingException: Could not determine type for: java.util.Set, at table: USER_ID, for columns: [org.hibernate.mapping.Column(userContact)]
I have 2 Entity Classes and one #Embeddable class for composite key. There seems to be many solutions to this problem so I've mixed and matched attributes along getters/setters and fields. I've tried #JsonBackReference and #JsonManagedReference, #ElementCollection and other annotations. Using #Access(AccessType.PROPERTY) did start the server correctly but gave me this error when trying to perform db operation.
org.codehaus.jackson.map.JsonMappingException: failed to lazily initialize a collection of role:
Any help would be appreciated. Here are my Entities.
User
#Entity
#Table(name = "USER_RECORD")
public class User {
private UserRecordId id;
private String name;
private String address;
#Column
#ElementCollection(targetClass=UserContact.class)
private Set<UserContact> userContact = new HashSet<UserContact>(0);
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "userId", column = #Column(name = "USER_ID", nullable = false)),
#AttributeOverride(name = "userId2", column = #Column(name = "USER_ID2", nullable = false)) })
public UserRecordId getId() {
return this.id;
}
public void setId(UserRecordId id) {
this.id = id;
}
#OneToMany(fetch = FetchType.EAGER, mappedBy = "user")
public Set<UserContact> getUserContact() {
return this.userContact;
}
public void setUserContact(Set<UserContact> userContact) {
this.userContact = userContact;
}
#Column(name = "USER_NAME", nullable = false)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#Column(name = "USER_ADDRESS", nullable = false)
public String getAddress() {
return this.address;
}
public void setAddress(String address) {
this.address = address;
}
UserContact
#Entity
#Table(name = "USER_CONTACT")
public class UserContact {
private String userContactId;
private String name;
private String country;
private User user;
#Id
#Column(name = "USER_CONTACT_ID", unique = true, nullable = false)
public String getUserContactId() {
return this.userContactId;
}
public void setUserContactId(String userContactId) {
this.userContactId = userContactId;
}
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumns({
#JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID"),
#JoinColumn(name = "USER_ID2", referencedColumnName = "USER_ID2") })
public User getUser() {
return this.user;
}
public void setUser(User user) {
this.user = user;
}
#Column(name = "CONTACT_NAME", nullable = false)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#Column(name = "CONTACT_COUNTRY", nullable = false)
public String getCountry() {
return this.country;
}
public void setCountry(String country) {
this.country = country;
}
UserRecordId/Embeddable
#Embeddable
public class UserRecordId
private String userId;
private String userId2;
#Column(name = "USER_ID", nullable = false)
public String getUserId() {
return this.userId;
}
.../////getUserid2
......
.....
override equals & hash code
You seem to have annotated a field AND a getter (userContact). You should use either FIELD or PROPERTY access but not both (particularly for the same field!).
Also you have annotated it once as ElementCollection and once as OneToMany. Can't be both, and certainly can't be ElementCollection when the element is an Entity.

Hibernate many-to-many withe extracolumn criteria problems

I followed this tutorial to implement in my domain model a many-to-many relationship with an extra column. It works great but I'm unable to create a criteria to query a field within the left side of my relation.
Taking this code
#Entity
#Table( name = "projects")
public class Project implements Cloneable, Serializable{
private Long id;
private String name;
private Set<ProjectOrganization> projectOrganizations = new HashSet<ProjectOrganization>(0);
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false)
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "name", length = 255, nullable = false)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.project")
#Cascade(value = { CascadeType.ALL })
public Set<ProjectOrganization> getProjectOrganizations() {
return this.projectOrganizations;
}
public void setProjectOrganizations(Set<ProjectOrganization> organizationProjects) {
this.projectOrganizations = organizationProjects;
}
}
#Entity
#Table(name = "projects_has_organizations")
#AssociationOverrides({ #AssociationOverride(name = "pk.project", joinColumns = #JoinColumn(name = "projects_id")),
#AssociationOverride(name = "pk.organization", joinColumns = #JoinColumn(name = "organizations_id"))
})
public class ProjectOrganization implements Cloneable, Serializable {
private ProjectOrganizationPK pk = new ProjectOrganizationPK();
private OrganizationRolesEnum role;
public ProjectOrganization() {
}
#Transient
public Organization getOrganization() {
return getPk().getOrganization();
}
public void setOrganization(Organization organization) {
getPk().setOrganization(organization);
}
#EmbeddedId
public ProjectOrganizationPK getPk() {
return pk;
}
public void setPk(ProjectOrganizationPK pk) {
this.pk = pk;
}
#Transient
public Project getProject() {
return getPk().getProject();
}
public void setProject(Project project) {
getPk().setProject(project);
}
#Enumerated(EnumType.STRING)
#Column(nullable = false, length = 50)
public OrganizationRolesEnum getRole() {
return role;
}
public void setRole(OrganizationRolesEnum role) {
this.role = role;
}
}
#Embeddable
public class ProjectOrganizationPK implements Cloneable, Serializable {
/** Generated serial version UID */
private static final long serialVersionUID = -4534322563105003365L;
private Organization organization;
private Project project;
#ManyToOne
public Organization getOrganization() {
return organization;
}
public void setOrganization(Organization organization) {
this.organization = organization;
}
#ManyToOne
public Project getProject() {
return project;
}
public void setProject(Project project) {
this.project = project;
}
}
#Entity
#Table(name = "organizations")
public class Organization implements Cloneable, Serializable {
private Long id;
private String name;
private Set<ProjectOrganization> projectOrganizations = new HashSet<ProjectOrganization>(0);
public Organization() {
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false)
#Override
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
#Column(name = "name", nullable = false, length = 255)
#NotNull(message = "A name is required!")
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.organization")
public Set<ProjectOrganization> getProjectOrganization() {
return this.projectOrganizations;
}
public void setProjectOrganization(Set<ProjectOrganization> projectOrganizations) {
this.projectOrganizations = projectOrganizations;
}
}
I want is to create a criteria to select a Project which has an organization with a requested name.
final Criteria crit = getSession().createCriteria(Project.class);
crit.createCriteria("projectOrganizations", "projectOrganization").
createAlias("pk.organization", "organization").
add( Restrictions.like("organization.name", "TEST"));
But when i run this code i have this error
2012-10-19 10:38:43,095 ERROR [org.hibernate.util.JDBCExceptionReporter] Unknown column 'organizati2_.name' in 'where clause'
and the sql query generated by hibernate is incomplete, doesn't join projects_has_organizations.organization with organization.id.. So it can't find column organization.name
SELECT
....
FROM
projects this_
INNER JOIN projects_has_organizations projectorg1_ ON this_.id = projectorg1_.projects_id
WHERE
projectorg1_.role =?
AND organizati2_. NAME LIKE ?
ORDER BY
this_.publish_date DESC
What's wrong with this code? How can i build query using criteria ?
I suspect that the problem is due to lazy fetching, try explicitly telling hibernate to eagerly fetch the property you need. This is done with the method
.setFetchMode("propertyName", FetchMode.EAGER)
So, in otherwords, try eagerly fetch the organisation property :)

Categories

Resources