Implement relationships for models using Hibernate annotation - Tuong-Nguyen/Spring GitHub Wiki
We can map a model object into table with the help of @Entity annotation.
@Entity
@Table(name="LESSON")
public class Lesson {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="LESSON_ID")
private int id;
@Column(name="TITLE")
private String title;
@Column(name="TIME")
private int time;The fields of model object corresponds to the columns of table.
@Id defines a primary key into the database.
@GeneratedValue specifies that this primary key would be auto generated by database.
There are three types of relationship degrees:
- One to One (1:1) - "one employee works for one department".
- One to Many (1:N) or Many to One (N:1) - depending upon from which perspective we are looking at the relationship. "One employee works in many projects.
- Many to Many (N:M) - "Many books are written by many authors."
Corresponding to above, we have the following annotations:
- @OneToOne.
- @OneToMany.
- @ManyToOne.
- @ManyToMany.
For example, we assume that a Course object contains many Lesson objects and a Lesson can be linked to many Course.
Between Course and Lesson is a Many To Many relationship and implemented as follows:
- Course model
@Entity
@Table(name="COURSE")
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="COURSE_ID")
private int id;
@Column(name="TITLE")
private String title;
@Column(name="DESCRIPTION")
private String description;
@Column(name="START_DATE")
private Date startDate;
@Column(name="END_DATE")
private Date endDate;
@Column(name="PAX")
private int pax;
@Column(name="ACTIVE")
private boolean active;
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(name = "COURSE_LESSON",
joinColumns = @JoinColumn(name = "COURSE_ID"),
inverseJoinColumns = @JoinColumn(name = "LESSON_ID"))
private List<Lesson> lessons;- Lesson model
@Entity
@Table(name="LESSON")
public class Lesson {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="LESSON_ID")
private int id;
@Column(name="TITLE")
private String title;
@Column(name="TIME")
private int time;
@ManyToMany(mappedBy = "lessons")
private List<Course> courses;
public Lesson(){
}
public Lesson(int id, String title, int time){
this.id = id;
this.title = title;
this.time = time;
}@ManyToMany annotation is annotated to lessons and courses field to represent a Many To Many relationship.
Course model class contains a list of lessons and vice versa.
@JoinTable indicates that there is a link table which joins two tables using foreign key constraints to their primary keys. This annotation is mainly used on the owning side of the relationship.
joinColumns refers to the column name of owning side (COURSE_ID).
inverseJoinColumns refers to the column of inverse side of relationship (LESSON_ID).
Pay attention to fetch = FetchType.LAZY argument in the @ManyToMany annotation. Here we are informing Hibernate to lazy load the list of lessons. With this setup, a query to load the list will be fired only when it is first accessed.
It 's a good way avoid fetching all connected object graph which is an expensive operation.
With JPA, not only we can fetch entities from database, but we can also fetch entity associations as well. JPA defines two FetchType strategies:
- EAGER
- LAZY
EAGER fetching means that associations are always retrieved along with their parent entity. It is very bad for performance.
So, it is better to use LAZY fetching but it can result in LazyInitializationException if getting the list of lessons isn't in transaction with Hibernate.
Solution:
- Getting lessons in method
Hibernate.intialize(Course.getLessons()). - Using
@Transactionalannotation for the Controller class or Service classes. - Using
JOIN FETCHdirective like in the following query:
courses = entityManager.createQuery(
"select c " +
"from Course c " +
"join fetch c.lessons " +
"where c.title = :title", Course.class)
.setParameter("title", title)
.getResultList();