Hibernate Mapping - How to Join Three Tables [duplicate] - java

This question already has answers here:
Hibernate Criteria returns children multiple times with FetchType.EAGER
(8 answers)
Closed 6 years ago.
I have 3 Entity: PERSON, CAR, and PROFFESION.
#Entity
#Table(name = "PERSON")
public class Person implements Serializable {
#Id
private Long id;
#OneToMany(mappedBy = "person", fetch = FetchType.EAGER)
private List<Car> cars;
#OneToMany(mappedBy = "person", fetch = FetchType.EAGER)
private List<Profession> professions;
}
#Entity
#Table(name = "PROFESSION")
public class Profession implements Serializable {
#Id
private Long id;
#ManyToOne
#JoinColumn(name = "PERSON_ID")
private Person person;
}
#Entity
#Table(name = "CAR")
public class Car implements Serializable {
#Id
private Long id;
#ManyToOne
#JoinColumn(name = "PERSON_ID")
private Person person;
}
When i'm trying to retrieve a person with connections to two professions and one car the result is two professions and two duplicated cars.
Or if he is connected to five cars and one profession, the result is five cars and five duplicated professions.
How should I correct the mappings so I receive the right results?

Use a Set to map oneToMany If you don't want duplicates. Its mapped with the <set> element in mapping table. so first you make changes to these parts:
private Set<Car> car = new HashSet<Car>(0);
#OneToMany(fetch=FetchType.LAZY, mappedBy="persons")
public Set<Car> getCar() {
return this.car;
}
public void setCar(Set<Car> car) {
this.car = car;
}
Do the same with profession and another oneToMany you do not want duplicates. you can set fetchType based on loading preferences. Eager load all at once and Lazy load on demand which is usually best.

Related

Annotation to join columns with OR condition instead of AND condition

I have 2 java classes, Relation and Person, which both are present in my database.
Person:
#Entity
#Table(name = "persons")
public class Person {
#Id
#Column
private int id;
#Column
private String name;
#OneToMany(fetch = FetchType.EAGER)
#JoinColumns({
#JoinColumn(name = "slave_id", referencedColumnName="id"),
#JoinColumn(name = "master_id", referencedColumnName="id")
})
private List<Relation> relations;
//Getters and setters
}
Relation:
#Entity
#Table(name = "relations")
public class Relation {
#Id
#Column
private int id;
#Column
private int child_id;
#Column
private int parent_id;
#Column
private String type;
//Getters and setters
}
Each Person has a list of relations (or not), the relation should be added to the list when the child_id or the parent_id of the relation is equal to the id of the person.
TL;DR:
When relation.child_id OR relation.parent_id = person.id => add relation to list of relations to the person
The issue I am facing is that this annotation:
#JoinColumns({
#JoinColumn(name = "child_id", referencedColumnName="id"),
#JoinColumn(name = "parent_id", referencedColumnName="id")
})
creates following SQL (just the necessary part):
relations relations6_
on this_.id=relations6_.slave_id
and this_.id=relations6_.master_id
What is the correct annotation in Java Hibernate to generate an SQL statement saying OR instead of AND
Some of the options that you could utilize:
Database views. Create the view that does custom join for you and map the entity to the view.
Join formula. I managed to make them work only on many-to-one associations. Nevertheless, you could make the association bidirectional and apply the formula in the Relation entity.
#Subselect. This is a kind of Hibernate view, suitable if you can't afford to create a real database view or change the db schema to better suit the entity model structure.
This and this answer could also be helpful.
Also, you can always use two separate associations for slaves and masters:
public class Person {
#OneToMany
#JoinColumn(name = "slave_id"),
private List<Relation> slaves;
#OneToMany
#JoinColumn(name = "master_id"),
private List<Relation> masters;
public List<Relation> getRelations() {
List<Relation> result = new ArrayList<>(slaves);
result.addAll(masters);
return result;
}
}
However, keep in mind that joining all of them in a single query requires full Cartesian product between masters and slaves.
You can use #FilterDef and #Filter annotations.

Hibernate get remaining objects from many to one relation

I have a database which stores clubs and teams.
A club has multiple teams and a team is part of only one club.
Is it possible to query all teams which are not in a specific club using
the hibernate criteria api?
Here are the (stripped) Club.java and Team.java
Club.java
#Entity
#Table(name = "clubs")
public class Club {
#Id
#GeneratedValue
#Column(name = "club_id")
private int id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "club")
private java.util.Set<Team> teams;
[setters, constructors etc]
}
Team.java
#Entity
#Table(name = "team")
public class Team {
#Id
#GeneratedValue
#Column(name = "team_id")
private int id;
#Column(name = "name")
private String name;
#ManyToOne
#JoinColumn(name = "club_id")
private Club club;
[same here]
}
As a slight variation on the answer above, if you already have the Club object which you wish to find all teams not belonging to it, you should be able to do something like:
Criteria criteria = session.createCriteria(Team.class);
criteria.add(Restrictions.ne("club", myClub));
List<Team> teams = criteria.list();
This avoids having to query on the club name and IMHO is cleaner code.
It should be something like this:
Criteria criteria=session.createCriteria(Team.class);
.createAlias("club", "c")
.add( Restrictions.not(Restrictions.like("c.name", "nameOfTheClubIAmLookingFor", MatchMode.EXACT)) )
List<Team> teams=criteria.list();

How to force a zero to one relationship?

My requirement is to have a list of classes and students of each class. Each students must be in zero or at most one class. My code is as following, but in database, each student can be in many classes. How to keep each student in zero to one and only one class?
Student item table is as following
StudentItem
id student_id code
1 1 233
2 5 453
3 1 567
4 6 565
Entities
#Entity
public class MyClass{
#Id
#GeneratedValue
private long id;
#OneToMany( cascade = CascadeType.ALL)
#LazyCollection(LazyCollectionOption.FALSE)
private List<StudentItem> students;
private String season;
...
}
#Entity
public class StudentItem{
#Id
#GeneratedValue
private long id;
#OneToOne
private Student student;
private String code;
...
}
#Entity
public class Student{
#Id
#GeneratedValue
private long id;
private String fname;
private String lname;
....
}
It seems to me that it's a many-to-one relationship between MyClass and StudentItem, with MyClass owning the relationship between them. Further, it may be simpler and more straightforward to use a join table between these two entities; this way, you don't run the risk of creating more than one MyClass entry in your database.
Think of it like this: what is the actual effective relationship between a class and a student? From your description, one class may hold many students, but any given student may only be in one class.
Here's a rough-hand example from memory on how to create it. I'll assume that there is a joining table between StudentItem and MyClass called classroom_students, with columns student_item_id and class_id.
#Entity
public class MyClass {
#Id
#GeneratedValue
private Integer id;
#OneToMany
#JoinTable(name = "classroom_students",
joinColumns = #JoinColumn(name = "student_item_id"),
inverseJoinColumns = #JoinColumn(name = "class_id"))
private List<MyClass> students;
// getters and setters for entity
}
#Entity
public class StudentItem {
#Id
#GeneratedValue
private Integer id;
#OneToOne(mappedBy = "student_id")
private Student student;
#ManyToOne(targetEntity = MyClass.class,
mappedBy = "students")
private MyClass myClass;
}

How to find entity using JPA(Hibernate) with condition on joined tables?

I have 3 tables, each mapped to an entity. The entities are something like this:
#Entity
#Table(name = "person")
public class Person implements Serializable {
private int id;
//other fields
}
#Entity
#Table(name = "phone")
public class Phone implements Serializable {
private int id;
private Long price;
#ManyToOne
#JoinColumn(name = "personId")
private Person person;
#ManyToOne
#JoinColumn(name = "manufacturerId")
private Manufacturer manufacturer;
//other fields
}
#Entity
#Table(name = "manufacturer")
public class Manufacturer implements Serializable {
private int id;
private String name;
//other fields
}
What I want to do is to create a method that will return a list of Persons that have phones from a specified manufacturer with the price in a specified range.
EDIT: My dao class implements EntityJpaDao . I would need a solution that would work with this implementation.
Following query will return the Samsung mobile users with phone price range.
Criteria criteria = session.createCriteria(Phone.class, "phone");
criteria.createAlias("phone.person", "person")
criteria.add(Restrictions.between("phone.price", minPrice, maxPrice));
criteria.createAlias("phone.manufacturer","manufacturer");
criteria.add(Restrictions.eq("manufacturer.name", Samsung));
criteria.setProjection(Projections.property("person"));
List<Person> persons = criteria.list();

Why does this one-to-many mapping fails when using mappedBy?

#Entity
public class Person implements Serializable{
#Id
#GeneratedValue
private int id;
private String name;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "person")
private List<Car> cars;
//getters and setters
}
#Entity
public class Car implements Serializable{
#Id
#GeneratedValue
private int id;
private String name;
#ManyToOne
#JoinColumn(name = "person_id")
private Person person;
// getters and setters
}
And.. I use it thus..
Person per = new Person();
per.setName("some");
Car car1 = new Car();
car1.setName("Ford");
Car car2 = new Car();
car2.setName("WagonR");
//s.save(car1);
//s.save(car2);
per.setCars(new ArrayList<Car>());
per.getCars().add(car1);
per.getCars().add(car2);
s.save(per);
Now.. the table Car has a column person_id but its showing null for both the cars.. what am I doing wrong here ? The table Person is correctly being filled. If I remove the "mappedBy" from the Person table.. and instead include the #JoinColumn here... then it works fine.
With your mapping, the owner of the relation is the Car and not the Person ... That's why when saving the Person, the Car is not saved. If you remove the mappedBy then the owner of the relation becomes the Person and you get your expected behavior!
Try calling car.setPerson(per).

Categories