In a one-to-one relationship, each row in the first table is linked to exactly one row in another table. If this relationship is applied, we can say that both the tables have an exactly equal number of rows any time.
We will take a look at the unidirectional and bidirectional ways to show a one-to-one relationship between the tables.
Here, we will consider the Person
and PersonDetail
classes to show a demo. So, let's first create the classes and tables for both.
Use the following script to create the tables if you are not using hbm2dll=create|update
:
Use the following script to create the passport_detail
table:
CREATE TABLE `passport_detail` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `passportno` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) );
Use the following script to create the person
table:
CREATE TABLE `person` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `passport_detail_id` bigint(20) DEFAULT NULL, PRIMARY KEY (`id`), KEY `FK_PERSON_ID` (`passport_detail_id`), CONSTRAINT `FK_PERSON_ID` FOREIGN KEY (`passport_detail_id`) REFERENCES `passport_detail` (`id`) );
Use the following code to create the classes:
Source file: PassportDetail.java
@Entity @Table(name = "passport_detail") public class PassportDetail { @Id @GeneratedValue @Column(name = "id") private long id; @Column(name = "passportno") private String passportNo; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getPassportNo() { return passportNo; } public void setPassportNo(String passportNo) { this.passportNo = passportNo; } }
@Entity @Table(name = "person") public class Person { @Id @GeneratedValue @Column(name = "id") private long id; @Column(name = "name") private String name; @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "passport_detail_id") private PassportDetail passportDetail; 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 PassportDetail getPassportDetail() { return passportDetail; } public void setPassportDetail(PassportDetail passportDetail) { this.passportDetail = passportDetail; } }
In this section, we will take a look at how to insert a record step by step.
Using the following code, we will insert a Person
object with a PassportDetail
one:
PassportDetail detail = new PassportDetail(); detail.setPassportNo("G51546645"); Person person = new Person(); person.setName("Vishal"); person.setPassportDetail(detail); Transaction transaction = session.getTransaction(); transaction.begin(); session.save(person); transaction.commit();
Here, the one-to-one relationship is not directly known to the database, but it is created for simplicity purposes and is useful to define a user-specific scenario. This means that each Person
has one and only one PassportDetail
object, and PassportDetail
does not exist without Person
.
As we used the @OneToOne
annotation in the preceding code, hibernate will consider that we want to have a one-to-one relationship between both the tables.
Let's take a look at an option used in the preceding code in detail:
cascade=CascadeType.ALL:
This option in the @OneToOne
annotation shows that hibernate uses cascading for all database operations. Here, we save a Person
record, but before saving a Person
object, it saves a PassportDetail
object because PassportDetail
is referred to by the Person
object. If the PassportDetail
object is not persisted at the time of saving the Person
object, and the appropriate CascadeType
option is not used, it throws an error similar to "Exception in thread "main" org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: "PassportDetail
".
The @JoinColumn
annotation is used to define the relationship between tables—in our case, between the person
table and a column created with the name "passport_detail_id"
—and it refers to the primary key of the " "passport_detail "
table, which is "id"
.
In other words, it creates a foreign key reference.
Here, we will take a look at the bidirectional way to achieve the relationship.
The logic behind this technique is that each row in the parent table knows its child record identity, and each row from child table knows its parent record identity. For example, in our case, Person
knows its PassportDetail
record, and PassportDetail
knows its Person
record; so, we can get the detail for both using any one table.
Here, we will use the same tables/classes structure as the one in the previous section with minor changes.
Use the following script to create the tables if you are not using hbm2dll=create|update
:
Use the following script to create the passport_detail
table:
CREATE TABLE `passport_detail` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `passportno` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) );
Use the following script to create the passport_detail
table:
CREATE TABLE `person` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `passport_detail_id` bigint(20) DEFAULT NULL, PRIMARY KEY (`id`), KEY `FK_PERSON_ID` (`passport_detail_id`), CONSTRAINT `FK_PERSON_ID` FOREIGN KEY (`passport_detail_id`) REFERENCES `passport_detail` (`id`) );
Use the following code to create the classes:
Source file: Person.java
@Entity @Table(name = "person") public class Person { @Id @GeneratedValue @Column(name = "id") private long id; @Column(name = "name") private String name; @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "passport_detail_id") private PassportDetail passportDetail; 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 PassportDetail getPassportDetail() { return passportDetail; } public void setPassportDetail(PassportDetail passportDetail) { this.passportDetail = passportDetail; } @Override public String toString() { return "Person" +" Id: " + this.id +" Name: " + this.name +" Passport Detail " + " Id: " + this.passportDetail.getId() + " PassportNo: " + this.passportDetail.getPassportNo(); } }
Source file: PersonDetail.java
@Entity @Table(name = "passport_detail") public class PassportDetail { @Id @GeneratedValue @Column(name = "id") private long id; @Column(name = "passportno") private String passportNo; @OneToOne(mappedBy = "passportDetail", cascade = CascadeType.ALL) private Person person; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getPassportNo() { return passportNo; } public void setPassportNo(String passportNo) { this.passportNo = passportNo; } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } @Override public String toString() { return "Passport Detail" +" Id: " + this.id +" Name: " + this.getPassportNo() +" Person " + " Id: " + this.person.getId() + " PassportNo: " + this.person.getName(); }
Here, we will insert a Person
object with a PassportDetail
:
PassportDetail detail = new PassportDetail(); detail.setPassportNo("G54624512"); Person person = new Person(); person.setName("Yogesh"); person.setPassportDetail(detail); Transaction transaction = session.getTransaction(); transaction.begin(); session.save(person); transaction.commit();
Now, we will try to get a PassportDetail
(child) record using a Person
(parent) record. Execute the following code:
Criteria criteria = session.createCriteria(Person.class); Person person = (Person) criteria.uniqueResult(); System.out.println(person.toString());
Hibernate: select this_.id as id1_1_1_, this_.name as name2_1_1_, this_.passport_detail_id as passport3_1_1_, passportde2_.id as id1_0_0_, passportde2_.passportno as passport2_0_0_ from person this_ left outer join passport_detail passportde2_ on this_.passport_detail_id=passportde2_.id Hibernate: select person0_.id as id1_1_1_, person0_.name as name2_1_1_, person0_.passport_detail_id as passport3_1_1_, passportde1_.id as id1_0_0_, passportde1_.passportno as passport2_0_0_ from person person0_ left outer join passport_detail passportde1_ on person0_.passport_detail_id=passportde1_.id where person0_.passport_detail_id=? Person Id: 1 Name: Yogesh Passport Detail Id: 1 PassportNo: G54624512
Now, we will do the inverse of the preceding example and try to get a Person
(parent) record using a PassportDetail
(child) record:
Criteria criteria = session.createCriteria(PassportDetail.class); PassportDetail passportDetail = (PassportDetail) criteria.uniqueResult(); System.out.println(passportDetail.toString());
Hibernate: select this_.id as id1_0_1_, this_.passportno as passport2_0_1_, person2_.id as id1_1_0_, person2_.name as name2_1_0_, person2_.passport_detail_id as passport3_1_0_ from passport_detail this_ left outer join person person2_ on this_.id=person2_.passport_detail_id Passport Detail Id: 1 PassportNoName: G54624512 Person Id: 1 PassportNoName: Yogesh