Hibernate many-to-many withe extracolumn criteria problems - java

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 :)

Related

Spring boot lazy loaded entity not loading all properties

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.

Spring Boot JPA, Repository don't delete record

I make method (acceptUseroffermapping) in a REST-controller (UserOfferController) in which I want to delete record in the DB (UserOfferMapping table). But the problem is that record not deleted and relation also saved after I run this method.
I have also UserOfferMapping class which maps to User class. In UserOfferController I manipulate with UserOfferMapping: creating, selecting records from DB and also trying to delete records but have fail.
UserOfferController.java:
/*...*/
#POST
#RequestMapping("/acceptUserOfferMapping")
public void acceptUseroffermapping(#RequestBody Map<String,
String> body) throws ParseException {
String userId = body.get("userId");
String offerId = body.get("offerId");
Optional<User> user = userRepository.findById(Integer.parseInt(userId));
UserOfferMapping mapping = userOfferMappingRepository.getById(Integer.parseInt(userId));
user.get().getUserOfferMapping().remove(mapping);
userRepository.save(user.get());
userOfferMappingRepository.deleteById(Integer.parseInt(offerId));
}
/*...*/
User.java:
/*some imports*/
#Entity
#Table(name = "User")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int id;
/* ...
* a lot of fields
* ...
*/
// Important section which describes all Role Project and Skill mapping
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<UserUserrolemapping> userrolemapings = new HashSet<>();
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<Userprojectmapping> userprojectmappings = new HashSet<>();
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<UserOfferMapping> userOfferMapping = new HashSet<>();
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
#OrderBy
private Set<Userskillmapping> userskillmappings = new HashSet<>();
/* ...
* a lot of fields too
* ...
*/
/* getter and setters */
}
UserOfferMappingRepository.java:
public interface UserOfferMappingRepository extends JpaRepository<UserOfferMapping, Integer> {
public List<UserOfferMapping> getAllByUser(Optional<User> user);
public UserOfferMapping getUserOfferMappingByUserAndProjectAndUserRole(User user, Userproject userproject, Userrole userrole);
public UserOfferMapping getById(int id);
public void deleteById(int id);
}
UserOfferMapping.java:
#Entity
public class UserOfferMapping {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne
#JoinColumn(name = "userid")
#JsonBackReference
private User user;
#ManyToOne
#JoinColumn(name = "roleid")
private Userrole userRole;
#ManyToOne
#JoinColumn(name = "projectid")
private Userproject project;
#Column(name = "fromdate", nullable = true)
private Date fromdate;
#Column(name = "todate", nullable = true)
private Date todate;
#Column(name = "chance")
private int chance;
#Column(name = "percent")
private int percent;
public int getId() {
return id;
}
public User getUser() {
return user;
}
public Userrole getUserRole() {
return userRole;
}
public Userproject getProject() {
return project;
}
public Date getFromdate() {
return fromdate;
}
public int getChance() {
return chance;
}
public int getPercent() {
return percent;
}
public void setId(int id) {
this.id = id;
}
public void setUser(User user) {
this.user = user;
}
public void setUserRole(Userrole userRole) {
this.userRole = userRole;
}
public void setProject(Userproject project) {
this.project = project;
}
public void setFromdate(Date fromdate) {
this.fromdate = fromdate;
}
public void setChance(int chance) {
this.chance = chance;
}
public void setPercent(int percent) {
this.percent = percent;
}
public void setTodate(Date todate) {
this.todate = todate;
}
public Date getTodate() {
return todate;
}
}
Can you try to use this
public interface UserOfferMappingRepository extends JpaRepository<UserOfferMapping, Integer> {
public List<UserOfferMapping> getAllByUser(Optional<User> user);
public UserOfferMapping getUserOfferMappingByUserAndProjectAndUserRole(User user, Userproject userproject, Userrole userrole);
public UserOfferMapping getById(int id);
// public void deleteById(int id);
#Modifying(clearAutomatically = true)
#Query(value = "Delete from UserOfferMapping c WHERE c.id=:id")
public void deleteById(#Param("id") int id);
}
So, you have bidirectional entity association.
Try to add mapping.setUser(null); before userRepository.save.
Persisting and deleting objects requires a transaction in JPA so that is why you have to define #Transactional annotation before the method in Repository for example `
#Transactional
public void deleteById(#Param("id") int id);

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 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.

One-To-Many Relationship in Spring Data JPA

I would like to have a One-to-many relationship between 2 Entities, Consumer and Policy. One consumer should have several policies.
This is an example of a Consumer JSON object I would like to have:
{
id : 1,
name : "Peter",
endpoint: "123.456.778",
policies: [
{
id : 1,
name: "policy 01"
},
{
id : 2,
name: "policy 02"
}
]
}
This is what I have so far:
Policy Entity
#Entity
public class Policy {
#Id
#GeneratedValue
#Column(name = "id")
private Integer id;
#Column(name = "name")
private String name;
//getters and setters
}
Consumer Entity
#Entity
public class Consumer {
#Id
#GeneratedValue
#Column(name = "consumer_id")
private Integer id;
#Column(name = "name")
private String name;
#Column(name = "endpoint")
private String endpoint;
#OneToMany
#JoinColumn(??)
private List<Policy> policies;
//getters and setters
}
It's not that hard I think, but im trying now for several hours and can't get it done. I'm new to Spring, so if someone is able to help me, I would be very thankfull!
#Entity
public class Consumer {
#OneToMany(mappedBy = "consumer")
private List<Policy> policies;
}
#Entity
public class Policy {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn("consumer_id")
private Consumer consumer;
}
fetch = FetchType.LAZY is not necessary, but desirable.
I have provided some basics here
what is #JoinColumn and how it is used in Hibernate
If you want to a Policy don't have a Consumer:
You can use a join table
#Entity
public class Consumer {
#OneToMany
private List<Policy> policies;
}
#Entity
public class Policy {
}
A unidirectional relation (a Policy table will have consumer_id column, but a Policy class doesn't have a Consumer)
#Entity
public class Consumer {
#OneToMany
#JoinColumn("consumer_id")
private List<Policy> policies;
}
#Entity
public class Policy {
}
Also, keep in mind, that if you want to use a Policy as tabular data (from a dictionary) you will need #ManyToMany.
Try this code :)
Your Consumer Class
#Entity
public class Consumer {
#Id
#GeneratedValue
#Column(name = "consumer_id")
private Integer id;
#Column(name = "name")
private String name;
#Column(name = "endpoint")
private String endpoint;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "idPolicy")
private List<Policy> policies;
public Consumer() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEndpoint() {
return endpoint;
}
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
}
public List<Policy> getPolicies() {
return policies;
}
public void setPolicies(List<Policy> policies) {
this.policies = policies;
}
}
Be aware that in the mapped by, you should use the name of the column that references Policy in your database, so if it's no policyId, use the name you gave to it
Your Policy Class
#Entity
public class Policy {
#Id
#GeneratedValue
#Column(name = "id")
private Integer id;
#Column(name = "name")
private String name;
#ManyToOne(optional = false)
private Consumer consumer;
public Policy() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

Categories

Resources