In this tutorial we will write a simple Java project to demonstrate Hibernate one to one Mapping using Java Annotations. We will first create a Java project using Maven and then will add Hibernate on it. If you have any questions, please post it in the comments section. If you are interested in receiving the future articles on Java topics, please subscribe here. If you are beginner for hibernate, please read the articles about introduction to hibernate, interceptors in hibernate and spring & hibernate integration. We also recommend good reference books for learning hibernate in our post about hibernate books.
- [download id=”14″]
One To One Relationship
A one-to-one relationships occurs when one entity is related to exactly one occurrence in another entity. Consider the following relationship:
According to the relationship each Person should have a unique address. Hence we will need two tables namely PERSON and ADDRESS to create this relation. In Hibernate, one-to-one relationship between entities can be created by 3 different techniques. In each technique though @OneToOne annotation has to be used. These techniques are:
- Using shared primary key: In this technique, hibernate will ensure that it will use a common primary key value in both the tables. This way primary key of entity PERSON can safely be assumed the primary key of entity ADDRESS also. The example demonstrated in this post is using shared primary key. The relational model is shown below:
- Using foreign key association: In this association, a foreign key column is created in the owner entity. For example, if we make PERSON as owner entity, then a extra column “ADDRESSID” will be created in PERSON table. This column will store the foreign key for ADDRESS table. The relational model is shown below:
- Using a common join table: In this association, the join table will have two foreign key columns (i.e) it will have both the primary key columns from the two entity tables (for example PERSONID and ADDRESSID from PERSON and ADDRESS entity tables respectively). One of the foreign keys will serve as the primary key for the join table. An unique constraint will be applied on the remaining foreign key column and hence both the foreign key columns will not have the duplicate values.
The relational model is shown below:
Technologies Used:
Following are the tools and technologies required for this project:
- Java JDK 5 or above
- Eclipse IDE 3.2 or above
- Maven 3.0 or above
- Hibernate 3.0 or above (Hibernate Downloads)
- MySQL 5 above (MySql Downloads)
1.Environment Setup
Refer post Hibernate, Maven and HSQL – Example Project (XML Mapping) for environment setup (follow steps 1,2 and 3). Pass the following command in the step.1, to create a java project using Maven:
mvn archetype:generate -DgroupId=net.javabeat.hibernate -DartifactId=HibernateOneToOneAnnotation -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
2. Adding dependencies to pom.xml
We need to add the Hibernate, MYSQL and some other dependencies to the Maven pom.xml as shown below:
<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>net.javabeat.hibernate</groupId> <artifactId>HibernateOneToOneAnnotation</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>HibernateOneToOneAnnotation</name> <url>http://maven.apache.org</url> <dependencies> <!-- Hibernate library dependency start --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate</artifactId> <version>4.1.9.Final</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.10</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-annotations</artifactId> <version>3.3.1.GA</version> </dependency> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.1</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>asm</groupId> <artifactId>asm</artifactId> <version>4.0</version> </dependency> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>ehcache</groupId> <artifactId>ehcache</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>hibernate-tools</groupId> <artifactId>hibernate-tools</artifactId> <version>3.2.3.GA</version> </dependency> <dependency> <groupId>jta</groupId> <artifactId>jta</artifactId> <version>1.1</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.11</version> </dependency> <dependency> <groupId>oscache</groupId> <artifactId>oscache</artifactId> <version>2.1</version> </dependency> <dependency> <groupId>persistence-api</groupId> <artifactId>persistence-api</artifactId> <version>1.0</version> </dependency> <!-- Hibernate library dependecy end --> </dependencies> </project>
As a next step, let’s execute the following command so that maven will download all the required JARs and add the same to eclipse classpath. The command line point to the directory C:\HibernateProject\HibernateOneToManyXML and execute the following command:
mvn eclipse:eclipse
3. Create Model Classes (Shared primary key technique)
As a next step let’s create the model classes
src\main\java\net\javabeat\hibernate\Person.java and src\main\java\net\javabeat\hibernate\Address.java using Annotations. @PrimaryKeyJoinColumn is the main annotation to be used in shared primary key technique. The model classes are as below
Person.java
package net.javabeat.hibernate; import java.io.Serializable; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToOne; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.Table; import javax.persistence.UniqueConstraint; /** * Model class for Person */ @Entity @Table(name = "PERSON", uniqueConstraints = { @UniqueConstraint(columnNames = "personId") }) public class Person implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "personId") private Long personId; @Column(name = "name") private String name; @OneToOne(cascade = CascadeType.ALL) @PrimaryKeyJoinColumn private Address address; public Person() { } public Person(String name, Address address) { super(); this.name = name; this.address = address; } public Long getPersonId() { return personId; } public void setPersonId(Long personId) { this.personId = personId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } }
In the above file we see that we have used @PrimaryKeyJoinColumn for the Address entity. Following if a piece of code from the above file:
@OneToOne(cascade = CascadeType.ALL) @PrimaryKeyJoinColumn private Address address;
Address.java
package net.javabeat.hibernate; import java.io.Serializable; 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; import javax.persistence.UniqueConstraint; import org.hibernate.annotations.GenericGenerator; import org.hibernate.annotations.Parameter; @Entity @Table(name = "ADDRESS", uniqueConstraints = { @UniqueConstraint(columnNames = "personId") }) public class Address implements Serializable { @Id @Column(name = "personId", unique = true, nullable = false) @GeneratedValue(generator = "gen") @GenericGenerator(name = "gen", strategy = "foreign", parameters = @Parameter(name = "property", value = "person")) private Long personId; @Column(name = "street") private String street; @Column(name = "city") private String city; @Column(name = "state") private String state; @Column(name = "zipcode") private String zipcode; @OneToOne(mappedBy = "address", cascade = CascadeType.ALL) private Person person; public Address() { } public Address(String street, String city, String state, String zipcode) { this.street = street; this.city = city; this.state = state; this.zipcode = zipcode; } 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 getState() { return state; } public void setState(String state) { this.state = state; } public String getZipcode() { return zipcode; } public void setZipcode(String zipcode) { this.zipcode = zipcode; } public Person getPerson() { return person; } public void setPerson(Person person) { this.person = person; } public Long getPersonId() { return personId; } public void setPersonId(Long personId) { this.personId = personId; } }
Here we see that Address entity will remain dependent on owner entity for the mapping. Following is a piece of code from the above file:
@OneToOne(mappedBy = "address", cascade = CascadeType.ALL) private Person person;
4. Adding Hibernate Configuration file
As a next step let’s add the hibernate.cfg.xml to the directory:/src/main/resources . Write the new file hibernate.cfg.xml in this directory. The hibernate.cfg.xml is as follows:
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- Database connection settings, Connect to MYSQL --> <property name="dialect">org.hibernate.dialect.MySQLDialect</property> <property name="connection.driver_class">com.mysql.jdbc.Driver</property> <property name="connection.url">jdbc:mysql://localhost:3306/manisha</property> <property name="connection.username">root</property> <property name="connection.password">root</property> <!-- JDBC connection pool (use the built-in) --> <property name="connection.pool_size">1</property> <property name="show_sql">true</property> <property name="format_sql">true</property> <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property> <!--create the database schema on startup if required --> <property name="hbm2ddl.auto">create</property> <mapping class="net.javabeat.hibernate.Person"></mapping> <mapping class="net.javabeat.hibernate.Address"></mapping> </session-factory> </hibernate-configuration>
In the above file we have set the database connection to MYSQL database . The show_sql option, if set to true will display all the executed SQL queries on the console. The property hbm2ddl.auto , if set to create, creates the schema, destroying the previous data.
Note : In case you want to use any other database then, you need to change these properties – “dialect”, “connection.driver_class”, “connection.url”, “connection.username”, and “connection.password”.
Also we have added the Annotation based entity classes Person.java and Address.java to the above file.
5. Create Utility class
Next, we will write a utility class to take care of Hibernate start up and retrieve the session easily. We will write the file src\main\java\net\javabeat\hibernate\HibernateUtil.java as below:
package net.javabeat.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("Initial SessionFactory creation failed." + ex); throw new ExceptionInInitializerError(ex); } } public static SessionFactory getSessionFactory() { return sessionFactory; } }
6. Revise the App class
Next we will revise the App.java (generated by Maven). This class tests the one-to-one relationship by creating and listing the person names and corresponding addresses as below:
package net.javabeat.hibernate; import java.util.Iterator; import java.util.List; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; /** * Main class * */ public class App { public static void main(String[] args) { System.out.println("Maven + Hibernate + MYSQL One to One Mapping Annotations"); Session session = HibernateUtil.getSessionFactory().openSession(); session.beginTransaction(); App app = new App(); Address address1 = new Address("M.G.Road", "Bangalore", "Karnataka", "56000"); Address address2 = new Address("Tilak Road", "Pune", "Maharashtra", "411207"); app.savePersonInfo("Jiya", address1); app.savePersonInfo("Manisha", address2); app.listPersonInfo(); } public Long savePersonInfo(String personName, Address address) { Session session = HibernateUtil.getSessionFactory().openSession(); session.beginTransaction(); Long personId = null; Transaction transaction = null; try { transaction = session.beginTransaction(); Person person = new Person(personName, address); address.setPerson(person); session.save(person); transaction.commit(); } catch (HibernateException e) { transaction.rollback(); e.printStackTrace(); } finally { session.close(); } return personId; } /* * Lists the person's from database table */ public void listPersonInfo() { Session session = HibernateUtil.getSessionFactory().openSession(); Transaction transaction = null; try { transaction = session.beginTransaction(); @SuppressWarnings("unchecked") List<Person> personList = session.createQuery("from Person").list(); for (Iterator<Person> iterator = personList.iterator(); iterator .hasNext();) { Person person = (Person) iterator.next(); System.out.println(person.getName()); System.out.println(person.getAddress().getStreet() + " " + person.getAddress().getCity() + " " + person.getAddress().getState() + " " + person.getAddress().getZipcode()); } transaction.commit(); } catch (HibernateException e) { transaction.rollback(); e.printStackTrace(); } finally { session.close(); } } }
Here the savePersonInfo() method is used to save a new Person object and Address object to the database. In the savePersonInfo() method a new object of the Person class is created and the name value and address is set. The personId value is auto generated so the value need not be set here. We then save the Person object to the Address object. The session.save() method is used to persist the value in the database and once the value is saved, the id value (Primary key) is returned. Once the object is saved, the transaction is committed. If any exception occurs then the transaction is rolledback. The transaction ends either through commit or rollback action. Once the transaction ends the session is closed.
The listPersonInfo() method is used to list name of each person in PERSON table and their corresponding addresses from ADDRESS table. Here we use Hibernate Query Language (HQL). The query “from Person” returns a list of all the data in the PERSON table and their corresponding addresses from ADDRESS table. Note that in the HQL we only specify the java class names and not the table names. Later, using the for loop we iterate the list the data from Person and corresponding Address table and hence display them on the console.
8. Final project structure
Once you have created all these source files, your project structure should look like following:
9. Execution of the above code
Now let us execute the code we created above. Let’s run the App class.
Right click on App.java >Run As > Java Application.
On start of each thread, a database schema will be created and the following actions will happen.
Output on the console:
Following output is displayed on the console:
Maven + Hibernate + MYSQL One to One Mapping Annotations log4j:WARN No appenders could be found for logger (org.hibernate.cfg.annotations.Version). log4j:WARN Please initialize the log4j system properly. Hibernate: insert into PERSON (name) values (?) Hibernate: insert into ADDRESS (city, state, street, zipcode, personId) values (?, ?, ?, ?, ?) Hibernate: insert into PERSON (name) values (?) Hibernate: insert into ADDRESS (city, state, street, zipcode, personId) values (?, ?, ?, ?, ?) Hibernate: select person0_.personId as personId0_, person0_.name as name0_ from PERSON person0_ Hibernate: select address0_.personId as personId1_2_, address0_.city as city1_2_, address0_.state as state1_2_, address0_.street as street1_2_, address0_.zipcode as zipcode1_2_, person1_.personId as personId0_0_, person1_.name as name0_0_, address2_.personId as personId1_1_, address2_.city as city1_1_, address2_.state as state1_1_, address2_.street as street1_1_, address2_.zipcode as zipcode1_1_ from ADDRESS address0_ left outer join PERSON person1_ on address0_.personId=person1_.personId left outer join ADDRESS address2_ on person1_.personId=address2_.personId where address0_.personId=? Hibernate: select address0_.personId as personId1_2_, address0_.city as city1_2_, address0_.state as state1_2_, address0_.street as street1_2_, address0_.zipcode as zipcode1_2_, person1_.personId as personId0_0_, person1_.name as name0_0_, address2_.personId as personId1_1_, address2_.city as city1_1_, address2_.state as state1_1_, address2_.street as street1_1_, address2_.zipcode as zipcode1_1_ from ADDRESS address0_ left outer join PERSON person1_ on address0_.personId=person1_.personId left outer join ADDRESS address2_ on person1_.personId=address2_.personId where address0_.personId=? Jiya M.G.Road Bangalore Karnataka 56000 Manisha Tilak Road Pune Maharashtra 411207
Using Foreign Key Technique
In such a association, refer the Address entity in Person class as follows:
@OneToOne @JoinColumn(name="addressId") private Address address;
If no @JoinColumn is declared on the owner entity, the defaults apply. A join column(s) will be created in the owner entity table and its name will be the concatenation of the name of the relationship in the owner side, _ (underscore), and the name of the primary key column(s) in the owned side.
In a bidirectional relationship, one of the sides has to be the owner entity. The owner is responsible for the association column(s) update. To declare a side as not responsible for the relationship, the attribute mappedBy is used. mappedBy refers to the property name of the association on the owner side. Following is the example code:
@OneToOne(mappedBy="address") private Person person;
Using a Common Join Table
In this technique, main annotation to be used is @JoinTable. This annotation is used to define the new table name (this is mandatory) and foreign keys from both of the tables. Folllowing is the example code:
@OneToOne(cascade = CascadeType.ALL) @JoinTable(name="PERSON_ADDRESS", joinColumns = @JoinColumn(name="personId"), inverseJoinColumns = @JoinColumn(name="addressId")) private Address address;
@JoinTable annotation is used in Person class. It declares that a new table PERSON_ADDRESS will be created with two columns personId (primary key of PERSON table) and addressId (primary key of ADDRESS table).
Summary
- [download id=”14″]
In this post we demonstrated one-to-one relation using Java annotations. We discussed about various techniques of one-to-one associations using annotations. We initially created a Java project using Maven, made it compatible with eclipse. Then we used annotations and created the model classes (primary key sharing association). We then set the values in our main class(App.java), which in turn added the values to respective tables. We also listed the data from the tables. In the next post we shall see an example of Hibernate One-to-Many mapping(XML mapping).If you have any questions, please post it in the comments section. If you are interested in receiving the future articles on Java topics, please subscribe here