There are 3 types of Relationships in JPA & Hibernate:
- One to One
- One to Many
- Many to Many
Lets understand the above relationships with demo.
Create a spring boot application with below dependencies:
- Web
- JPA
- H2
- Lombok
First lets create 2 entity classes- Student and Passport like below:
Student.java:
@Data @AllArgsConstructor @NoArgsConstructor @Entity public class Student { @Id @GeneratedValue private Long id; @Column(nullable=false) private String name; }
Passport.java:
@Data @AllArgsConstructor @NoArgsConstructor @Entity public class Passport { @Id @GeneratedValue private Long id; private String number; }
Add below entries in \resources\data.sql file:
insert into student(id,name) values(2001,'Ashima'); insert into student(id,name) values(2002,'John'); insert into student(id,name) values(2003,'Michael'); insert into passport(id,number) values(4001,'E1234'); insert into passport(id,number) values(4002,'F1235'); insert into passport(id,number) values(4003,'G1236');
Once you run the application you can see respective 4 tables are created in H2 In-memory database.
And the data.sql file is also get executed and it will populate the respective tables.
Until now we have not created any relationship among the tables. Lets begin with One-to One relationship.
One-to-One Relationship:
Lets create an One-to-One relationship between Student and Passport table.
Changes in Entity class:
Add the below new attribute to the Student class:
@OneToOne private Passport passport;
By adding the above line you are putting the ownership of One-to-One relationship on the Student table. The same you can put it in the Passport table as well, but no need to put at both the classes.
So the full class would look like this:
@Data @AllArgsConstructor @NoArgsConstructor @Entity public class Student { @Id @GeneratedValue private Long id; @Column(nullable=false) private String name; @OneToOne private Passport passport; }
Once you restart the application and go to h2 console (http://localhost:8080/h2-console) you will see a new column – PASSPORT_ID would have added to the Student table. Although the values in the table would be null for now.
P.S. : We had given the attribute name as passport and not passport_id
So how to fix the above null data ?
Very simple, you need to manually add the data while creating the tables via data.sql:
insert into student(id,name, passport_id) values(2001,'Ashima',4001); insert into student(id,name, passport_id) values(2002,'John',4002); insert into student(id,name, passport_id) values(2003,'Michael',4003);
Make sure to have the Passport table first before Student table in data.sql, else you will get exception, since the Passport table data has to be inserted in Student table by Hibernate.
Once you restart the application you will see data is populated in PASSPORT_ID column in Student table.
In the logs you can see an Foreign Key constraint is added to the Student table- passport_id
Thats all !!..
One-To-Many Relationship:
A Course can have multiple Review, so here its a One-To-Many relationship.
Lets add the Course and Review entity class and update the data.sql file:
Course.java:
@Data @AllArgsConstructor @NoArgsConstructor @Entity public class Course{ @Id @GeneratedValue private Long id; private String name; }
Review.java:
@Data @AllArgsConstructor @NoArgsConstructor @Entity public class Review{ @Id @GeneratedValue private Long id; private String rating; private String description; }
insert into course(id,name) values(1001, 'Java'); insert into course(id,name) values(1002, 'Python'); insert into course(id,name) values(1003, 'NodeJs'); insert into review(id,rating,description) values(5001,'5', 'Awesome course'); insert into review(id,rating,description) values(5003,'4', 'Good course'); insert into review(id,rating,description) values(5004,'3', 'Ok course');
Changes in Entity class(es):
In Course entity add an “List” attribute having @OneToMany annotation with mappedBy property.
In Review entity add a attribute having @ManyToOne annotation.
Full Course.java:
@Data @AllArgsConstructor @NoArgsConstructor @Entity public class Course{ @Id @GeneratedValue private Long id; private String name; @OneToMany(mappedBy="course") //this value is from the Review entity. private List<Review> reviews=new ArrayList<>(); public void addReview(Review review){ reviews.add(review); } public void removeReview(Review review){ reviews.remove(review); } }
P.S.: Whenever your attribute is a List, don’t add an setter to it otherwise user will be able to add the whole list to it, rather have an addMethod() so that item can be added only one at a time, as we have done in above example.
Full Review.java class:
@Data @AllArgsConstructor @NoArgsConstructor @Entity public class Review{ @Id @GeneratedValue private Long id; private String rating; private String description; @ManyToOne private Course course; //This attribute is mapped in the Course entity. }
As usual a course_id column will be automatically added to Reviews table, although it will be having null values, so you have to update the data.sql file accordingly. As usual, we have only mentioned the attribute as course but in the db the column becomes course_id
P.S.: You won’t see any reviews_id column added in Course table.
insert into review(id,rating,description, course_id) values(5001,'5', 'Awesome course', 1001); insert into review(id,rating,description, course_id) values(5003,'4', 'Good course',1001); insert into review(id,rating,description, course_id) values(5004,'3', 'Ok course',1003);
That all….
Many-to-Many Relationship:
Many Students can take many Courses, so Student to Course can be an Many-to-Many relationship. For this we need to have a third table, and the table schema can be like this: Course_Student (course_id, student_id)
In Course.java:
@ManyToMany(mappedBy="courses") //Course entity have the relationship ownership List<Student> students=new ArrayList<>();
In Student.java
@ManyToMany List<Course> courses=new ArrayList<>();
Once you start the application you can see a new table called Course_Student will be created in DB. Although you won’t see any data in that table because we had not added any data to it.
If you see here, the Course entity would be owning the relationship so we had added a mappedBy attribute. If we don’t add mappedBy attribute there won’t be any error but we will see 2 new tables would be created instead of 1 table like this: STUDENT_COURSE and COURSE_STUDENT.
GitHub source code of this tutorial can be found here: https://github.com/heapsteep/relationship-demo