In this tutorial, we will learn about how to use Hibernate One-To-One Bidirectional Shared primary key mapping using annotation based configuration.
In One-To-One Bidirectional Shared primary key mapping, two tables share the same primary key.
The Bidirectional relationship means navigation is possible in both direction.
We are discussing an example of Student and Address relationship. A student lives on one Address. And one address can be occupied by only one student. Let’s get going.
Following technologies being used:
Let’s begin.
Open MySQL terminal / workbench terminal and execute following MySQL script :
create table STUDENT ( student_id BIGINT NOT NULL AUTO_INCREMENT, first_name VARCHAR(30) NOT NULL, last_name VARCHAR(30) NOT NULL, section VARCHAR(30) NOT NULL, PRIMARY KEY (student_id) ); create table ADDRESS ( address_id BIGINT NOT NULL, street VARCHAR(30) NOT NULL, city VARCHAR(30) NOT NULL, country VARCHAR(30) NOT NULL, PRIMARY KEY (address_id), CONSTRAINT student_address FOREIGN KEY (address_id) REFERENCES STUDENT ( student_id) ON DELETE CASCADE );
Here we have created Student and Address tables. Student table is pretty obvious. But while creating Address table, we have also specified a foreign key constraint from Address table primary key to Student table primary key.Have a look on table schema figure above.
Please visit MySQL installation on Local PC in case you are finding difficulties in setting up MySQL locally.
Following will be the final project structure:
Now let’s add/update the content mentioned in above project structure.
Following is the updated minimalistic pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.websystique.hibernate</groupId> <artifactId>OneToOneBiSharedPrimaryKey</artifactId> <version>1.0.0</version> <packaging>jar</packaging> <name>OneToOneBiSharedPrimaryKey</name> <properties> <hibernate.version>4.3.6.Final</hibernate.version> <mysql.connector.version>5.1.31</mysql.connector.version> </properties> <dependencies> <!-- Hibernate --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>${hibernate.version}</version> </dependency> <!-- MySQL --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.connector.version}</version> </dependency> </dependencies> <build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.2</version> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </pluginManagement> </build> </project>
On saving pom.xml with above content, Eclipse will download all the dependencies.
Model class Student & Address are simple POJO class which is annotated with JPA annotations to map it to a database tables(created in step 1).
package com.websystique.hibernate.model; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToOne; import javax.persistence.Table; @Entity @Table(name = "STUDENT") public class Student { @Id @GeneratedValue @Column(name = "STUDENT_ID") private long id; @Column(name = "FIRST_NAME") private String firstName; @Column(name = "LAST_NAME") private String lastName; @Column(name = "SECTION") private String section; @OneToOne(mappedBy="student", cascade = CascadeType.ALL) private Address address; public Student() { } public Student(String firstName, String lastName, String section) { this.firstName = firstName; this.lastName = lastName; this.section = section; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getSection() { return section; } public void setSection(String section) { this.section = section; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } @Override public String toString() { return "Student [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", section=" + section + ", address=" + address + "]"; } }
package com.websystique.hibernate.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.OneToOne; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.Table; import org.hibernate.annotations.Parameter; import org.hibernate.annotations.GenericGenerator; @Entity @Table(name = "ADDRESS") public class Address { @Id @Column(name="ADDRESS_ID") @GeneratedValue(generator="gen") @GenericGenerator(name="gen", strategy="foreign",parameters=@Parameter(name="property", value="student")) private long id; @Column(name = "STREET") private String street; @Column(name = "CITY") private String city; @Column(name = "COUNTRY") private String country; @OneToOne @PrimaryKeyJoinColumn private Student student; public Address() { } public Address(String street, String city, String country) { this.street = street; this.city = city; this.country = country; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public Student getStudent() { return student; } public void setStudent(Student student) { this.student = student; } @Override public String toString() { return "Address [id=" + id + ", street=" + street + ", city=" + city + ", country=" + country + "]"; } }
Note that now we have a student property in Address class and address property in Student class, which means we can now navigate in either direction.In hibernate, for bidirectional relationships like this, we have a concept of ownership, means who is the owner of this relationship.Put simply, who is responsible for updating the column in DB on which this relationship depends on. In our case it’s the student_id of Student table which is driving the complete relationship truck. So we should tell hibernate that it’s the Student class which will manage the relationship.
To do that, we can use mappedBy
attribute. mappedBy attribute are always put(annotated) on the inverse side of relation ship and specifies with it’s attribute value, the owner of the relationship.
@OneToOne(mappedBy="student", cascade = CascadeType.ALL) private Address address;
With this declaration, we ask hibernate to go and find the student property of Address class to know how to manage the relationship/perform some operation. Now in Address class , we have following declaration
@OneToOne @PrimaryKeyJoinColumn private Student student;
Which simply says that both the Address table and Student table share the same primary key.
But how would you make sure to that Address class use the same id value as used by Student? You have to make sure in your code that you set the id of Address with the id of saved Student before you save address(as we did in one-to-one unidirectional tutorial).If you don’t want to do that, another option is to use hibernate specific annotation @GenericGenerator
(that’s what we are doing now in Address class). In Address class, @GenericGenerator ensures that id value of Address property value will be taken from the id of Student table.
We need to inform hibernate about how to connect to database, which database dialect we will be using so that hibernate can generate the instruction specific to that database.
We define all these information in hibernate.cfg.xml
. Create this file with below content and save it in src/main/resources
folder.
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-configuration SYSTEM "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property> <property name="hibernate.connection.username">myuser</property> <property name="hibernate.connection.password">mypassword</property> <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/websystique</property> <property name="show_sql">true</property> <property name="format_sql">false</property> <mapping class="com.websystique.hibernate.model.Student"/> <mapping class="com.websystique.hibernate.model.Address"/> </session-factory> </hibernate-configuration>
dialect
property informs hibernate to generate database specific (MySQL here) instructions. driver_class
defines the database specific driver hibernate will use to make connection. next 3 properties corresponds to database connection. show_sql will instruct hibernate to log all the statements on console and format_sql instructs it to display properly formatted sql. mapping
tag instructs hibernate to perform mapping for classes refereed by class attribute.
This class is well-known in hibernate community, and used for configuring hibernate on startup and managing session factory.
package com.websystique.hibernate; import org.hibernate.SessionFactory; import org.hibernate.cfg.AnnotationConfiguration; public class HibernateUtil { private static final SessionFactory sessionFactory; static{ try{ sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory(); }catch (Throwable ex) { System.err.println("Session Factory could not be created." + ex); throw new ExceptionInInitializerError(ex); } } public static SessionFactory getSessionFactory() { return sessionFactory; } }
package com.websystique.hibernate; import java.util.List; import org.hibernate.Session; import com.websystique.hibernate.model.Address; import com.websystique.hibernate.model.Student; public class HibernateStandAlone { @SuppressWarnings("unchecked") public static void main(String[] args) { Student student = new Student("Sam","Disilva","Maths"); Address address = new Address("10 Silver street","NYC","USA"); Session session = HibernateUtil.getSessionFactory().openSession(); session.beginTransaction(); student.setAddress(address); address.setStudent(student); session.save(student); List<Student> students = (List<Student>)session.createQuery("from Student ").list(); for(Student s: students){ System.out.println("Details : "+s); } session.getTransaction().commit(); session.close(); } }
Look at how we have set address property of student and student property of address.Then we have called save only on student, thanks to Cascade
attribute on address property of Student class, address will be saved automatically.
Execute above class as Java application. You will see following output
Hibernate: insert into STUDENT (FIRST_NAME, LAST_NAME, SECTION) values (?, ?, ?) Hibernate: select student0_.STUDENT_ID as STUDENT_1_1_, student0_.FIRST_NAME as FIRST_NA2_1_, student0_.LAST_NAME as LAST_NAM3_1_, student0_.SECTION as SECTION4_1_ from STUDENT student0_ Details : Student [id=1, firstName=Sam, lastName=Disilva, section=Maths, address=Address [id=1, street=10 Silver street, city=NYC, country=USA]] Hibernate: insert into ADDRESS (CITY, COUNTRY, STREET, ADDRESS_ID) values (?, ?, ?, ?)
Below is the snapshot of MySQL database after execution of above program.
You can see that address was saved with the same id as student.
That’s it.
If you like tutorials on this site, why not take a step further and connect me on Facebook , Google Plus & Twitter as well? I would love to hear your thoughts on these articles, it will help improve further our learning process.
In this post we will be developing a full-blown CRUD application using Spring Boot, AngularJS, Spring Data, JPA/Hibernate and MySQL,…
Spring Boot complements Spring REST support by providing default dependencies/converters out of the box. Writing RESTful services in Spring Boot…
Being able to start the application as standalone jar is great, but sometimes it might not be possible to run…
Spring framework has taken the software development industry by storm. Dependency Injection, rock solid MVC framework, Transaction management, messaging support,…
Let's secure our Spring REST API using OAuth2 this time, a simple guide showing what is required to secure a…
This post shows how an AngularJS application can consume a REST API which is secured with Basic authentication using Spring…
View Comments
can you provide the one to one as many to one bidirectional with annotation
LAZY LOAD OneToOne and save entity
For more information and examples checkout below link.
https://codedeal.wixsite.com/codedeal/single-post/Hibernate-One-To-One-Relationship-Using-Annotations-Bidirectional