hibernate

Hibernate One-to-One Relationship Example (XML Mapping and Annotation)

In this example we are going to see how to map classes to databases tables which have one-to-one relationships. We are going to see the mapping both with XML Mapping and with Annotations.

So these are the tools we are going to use on a Windows 7 platform:

  • JDK 1.7
  • Maven 3.0.5
  • Hibernate 3.6.3.Final
  • MySQL JDBC driver 5.1.9
  • Eclipse 4.2 Juno

Mapping the Classes using XML Mapping

1. Download the project from the previous tutorial

Take a close look at Hibernate 3 with Maven 2 and MySQL 5 Example (XML Mapping and Annotation) tutorial to learn how to create a project with Maven and to create the basic structure of the project. This is the project we are going to use here. So follow the steps to create the project.

Make sure the project structure looks like this:

project-structure

2. One-to-One relationships

Imagine that you have tables A and B in a database. Each tuple in A contains basic information about entities. And we want a second, bigger table B that holds all the information we need to keep about each entity enlisted in A. So, this is the basic strategy you have to follow in order to crate the tables in the database:

DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `STUDENT_ID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `STUDENT_NAME` VARCHAR(10) NOT NULL,
  `STUDENT_AGE` VARCHAR(20) NOT NULL,
  PRIMARY KEY (`STUDENT_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `student_information`;
CREATE TABLE  `student_information` (
 `STUDENT_ID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
 `ADDRESS` VARCHAR(100) NOT NULL,
 `REGISTRY_NUMBER` VARCHAR(255),
 `PHONE_NUMBER` VARCHAR(255),
 `ENLISTED` DATE NOT NULL,
 PRIMARY KEY (`STUDENT_ID`) ,
 CONSTRAINT `FK_STUDENT_ID` FOREIGN KEY (`STUDENT_ID`) REFERENCES `student` (`STUDENT_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

This is a simple diagram of the tables, that shows this relationship, created with MySQL Workbech (which I personally like a lot).

diagram

As you can see “student_information” holds “STUDENT_ID“, which is the primary key (and therefore the identifier) of the “student” table. “STUDENT_ID” is also the primary key of “student_information“, but it is a foreign key to the “student” table as well. And that’s what connected these tables with the one-to-one relationship. It means that, for every student tuple in  “student” table, there will be only one corresponding tuple in “student_information” .

3. Edit pom.xml

Make sure pom.xml contains these dependencies:

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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.javacodegeeks.enterprise.hibernate</groupId>
  <artifactId>HibernateMySQLExample</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>HibernateMySQLExample</name>
  <url>http://maven.apache.org</url>
  <!-- JBoss repository for Hibernate -->
	<repositories>
		<repository>
			<id>JBoss repository</id>
			<url>http://repository.jboss.org/nexus/content/groups/public/</url>
		</repository>
	</repositories>

	<dependencies>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.8.2</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>1.7.0</version>
			<scope>runtime</scope>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.0</version>
			<scope>runtime</scope>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.0</version>
			<scope>runtime</scope>
		</dependency>

		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.14</version>
		</dependency>

		<!-- MySQL database driver -->

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.9</version>
		</dependency>

		<!-- Hibernate framework -->

		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>3.6.3.Final</version>
		</dependency>

		<dependency>
			<groupId>javassist</groupId>
			<artifactId>javassist</artifactId>
			<version>3.12.1.GA</version>
		</dependency>

	</dependencies>

</project>

3. Create the classes

So, these are the classes that will be mapped to database tables:

You have to update Student.java file to this:

Student.java:

package com.javacodegeeks.enterprise.hibernate;

public class Student implements java.io.Serializable {

	private static final long serialVersionUID = 1L;

	private Integer studentId;
	private String  studentName;
	private String  studentAge;
	private StudentInformation studentInfo;

	public Student() {
	}

	public Student(String studentName, String studentAge) {
		this.studentName = studentName;
		this.studentAge = studentAge;
	}

	public Integer getStudentId() {
		return this.studentId;
	}

	public void setStudentId(Integer studentId) {
		this.studentId = studentId;
	}

	public String getStudentName() {
		return this.studentName;
	}

	public void setStudentName(String studentName) {
		this.studentName = studentName;
	}

	public String getStudentAge() {
		return this.studentAge;
	}

	public void setStudentAge(String studentAge) {
		this.studentAge = studentAge;
	}

	public StudentInformation getStudentInfo() {
		return studentInfo;
	}

	public void setStudentInfo(StudentInformation studentInfo) {
		this.studentInfo = studentInfo;
	}

	public static long getSerialversionuid() {
		return serialVersionUID;
	}

}

The above class will be mapped, of course to “student” table. Note that Student class holds a StudentInformation instance.

Now you have to create a new StudentInformation class. Go to the Package explorer and find the main source package of our project:

project-structure

Right Click -> New -> Class:

newclass

StudentInformation.java:

package com.javacodegeeks.enterprise.hibernate;

import java.util.Date;

public class StudentInformation {

	private Integer studentId;
	private String address;
	private String registryNumber;
	private String phoneNumber;
	private Date enlisted;

	private Student student;

	public Integer getStudentId() {
		return studentId;
	}

	public void setStudentId(Integer studentId) {
		this.studentId = studentId;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public String getRegistryNumber() {
		return registryNumber;
	}

	public void setRegistryNumber(String registryNumber) {
		this.registryNumber = registryNumber;
	}

	public String getPhoneNumber() {
		return phoneNumber;
	}

	public void setPhoneNumber(String phoneNumber) {
		this.phoneNumber = phoneNumber;
	}

	public Date getEnlisted() {
		return enlisted;
	}

	public void setEnlisted(Date enlisted) {
		this.enlisted = enlisted;
	}

	public Student getStudent() {
		return student;
	}

	public void setStudent(Student student) {
		this.student = student;
	}
}

Note that the above class holds a Student instance.

4. XML Mapping files

Go to /src/main/resources/com/javacodegeeks/enterprise/hibernate folder and create the following xml files:

Student.hbm.xml:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    <class name="com.javacodegeeks.enterprise.hibernate.Student" table="student" catalog="tutorials">
        <id name="studentId" type="java.lang.Integer">
            <column name="STUDENT_ID" />
            <generator class="identity" />
        </id>
        <property name="studentName" type="string">
            <column name="STUDENT_NAME" length="10" not-null="true" unique="true" />
        </property>
        <property name="studentAge" type="string">
            <column name="STUDENT_Age" length="20" not-null="true" unique="true" />
        </property>

        <one-to-one name="studentInfo" class="com.javacodegeeks.enterprise.hibernate.StudentInformation"
			cascade="save-update"></one-to-one>
    </class>
</hibernate-mapping>

The intersting bit here is:

<one-to-one name="studentInfo" class="com.javacodegeeks.enterprise.hibernate.StudentInformation"cascade="save-update">
</one-to-one>

where we describe that the studentInfo attribute of the class has a one-to-one relationship with StudentInformation. We also state that when the student table is updated, the update should be cascaded to student_information table.

StudentInformation.hbm.xml:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 25 April 2011 7:52:33 PM by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
	<class name="com.javacodegeeks.enterprise.hibernate.StudentInformation" table="student_information" 	catalog="tutorials">
		<id name="studentId" type="java.lang.Integer">
			<column name="STUDENT_ID" />
			<generator class="foreign">
				<param name="property">student</param>
			</generator>
		</id>

		<property name="address" type="string">
			<column name="ADDRESS" length="100" not-null="true" />
		</property>
		<property name="registryNumber" type="string">
			<column name="REGISTRY_NUMBER" length="255"  />
		</property>
		<property name="phoneNumber" type="string">
			<column name="PHONE_NUMBER" length="255" />
		</property>
		<property name="enlisted" type="date">
			<column name="ENLISTED" length="10" not-null="true" />
		</property>

		<one-to-one name="student" class="com.javacodegeeks.enterprise.hibernate.Student"
			constrained="true"></one-to-one>
	</class>
</hibernate-mapping>

Again, here the interesting part is:

<id name="studentId" type="java.lang.Integer">
<column name="STUDENT_ID" />
	<generator class="foreign">
		<param name="property">student</param>
	</generator>
</id>

Here we state that studentId attribute of the StudentInformation class is a “foreign key” to the student attribute of the class. This relationship is reflected to the corresponding database tables as well.

5. Update Hibernate configuration file:

Go to the package explorer and find hibernate.cfg.xml file and paste the following code:

hibernate.cfg.xml:

<?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>
		<property name="hibernate.bytecode.use_reflection_optimizer">false</property>
		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="hibernate.connection.username">root</property>
		<property name="hibernate.connection.password"></property>
		<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/tutorials</property>
		<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
		<property name="show_sql">true</property>
		<mapping resource="com/javacodegeeks/enterprise/hibernate/Student.hbm.xml"></mapping>
		<mapping resource="com/javacodegeeks/enterprise/hibernate/StudentInformation.hbm.xml"></mapping>
	</session-factory>
</hibernate-configuration>

This is pretty much the same as the previous tutorial. We simply added the mapping relationships of the two classes.

6. Code the Application.

Go to App.java file and paste the following code:

App.java:

package com.javacodegeeks.enterprise.hibernate;

import java.util.Date;

import org.hibernate.Session;
import com.javacodegeeks.enterprise.hibernate.utils.HibernateUtil;;

public class App 
{
    public static void main( String[] args )
    {
        Session session = HibernateUtil.getSessionFactory().openSession();

        session.beginTransaction();
        Student student = new Student();

        student.setStudentName("JavaFun");
        student.setStudentAge("19");

        StudentInformation studentInfo = new StudentInformation();

        studentInfo.setAddress("1st Street");
        studentInfo.setPhoneNumber("982349823");
        studentInfo.setRegistryNumber("ax203");
        studentInfo.setEnlisted(new Date());

        studentInfo.setStudent(student);
        student.setStudentInfo(studentInfo);

        session.save(student);
        session.getTransaction().commit();
        System.out.println("Great! Student was saved");
    }
}

The above code has some notable parts. First of all we obtain a Session from the SessionFactory instance of our HibernateUtils class. Then we start a transaction with the database. We simply create one instance of each class and we assign each one to the corresponding attribute of the other. Then, we save to the Session just the student instance and finally commit the transaction. As you can see, every time we insert a new student tuple, automatically a new student_information tuple will be inserted. Upon transaction commit the Hibernate session is flushed/synchronized with the database. So the newly created Student intance residing in the Session is persisted to the database.

7. Run the Application

This is the output of the program:

log4j:WARN No appenders could be found for logger (org.hibernate.type.BasicTypeRegistry).
log4j:WARN Please initialize the log4j system properly.
Hibernate: insert into tutorials.student (STUDENT_NAME, STUDENT_Age) values (?, ?)
Hibernate: insert into tutorials.student_information (ADDRESS, REGISTRY_NUMBER, PHONE_NUMBER, ENLISTED, STUDENT_ID) values (?, ?, ?, ?, ?)
Great! Student was saved

This was an example on Hibernate One-to-One Relationship Example using XML Mapping. Download the Eclipse project of this part : Hibernate1to1XMLExample.zip

Mapping the Classes using Annotations

For this part we just have to do some updates to the previous project. The main difference is that we are not going to use Student.hbm.xml and StudentInformation.hbm.xml to map the classes to the corresponding tables in the database. We will use special annotations in the java classes that will dictate the mapping.

1. Delete Student.hbm.xml and StudentInformation.hbm.xml

We don’t need them any more.

2. Update the pom.xml file to include Hibernate Annotation library

Since Hibernate version 3.6, the annotation framework is included into the hibernate-core.jar module, so no update for us.

But if you face any problems updated the pom.xml file to include these libraries as well:

<dependency>
	<groupId>hibernate-annotations</groupId>
	<artifactId>hibernate-annotations</artifactId>
	<version>3.6.3.Final</version>
</dependency>

<dependency>
	<groupId>hibernate-commons-annotations</groupId>
	<artifactId>hibernate-commons-annotations</artifactId>
	<version>3.6.3.Final</version>
</dependency>

And then run mvn eclipse:eclipse to download the necessary jars and update the classpath of your project.

3. Update the classes to include Hibernate Annotations

This is how the annotated Student.java file should look like:

package com.javacodegeeks.enterprise.hibernate;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@Entity
@Table(name = "student", catalog = "tutorials")
public class Student implements java.io.Serializable {

	private static final long serialVersionUID = 1L;

	private Integer studentId;
	private String  studentName;
	private String  studentAge;
	private StudentInformation studentInfo;

	public Student() {
	}

	public Student(String studentName, String studentAge) {
		this.studentName = studentName;
		this.studentAge = studentAge;
	}

	@Id
	@GeneratedValue(strategy = IDENTITY)
	@Column(name = "STUDENT_ID", unique = true, nullable = false)
	public Integer getStudentId() {
		return this.studentId;
	}

	public void setStudentId(Integer studentId) {
		this.studentId = studentId;
	}

	@Column(name = "STUDENT_NAME", nullable = false, length = 10)
	public String getStudentName() {
		return this.studentName;
	}

	public void setStudentName(String studentName) {
		this.studentName = studentName;
	}

	@Column(name = "STUDENT_AGE", nullable = false, length = 20)
	public String getStudentAge() {
		return this.studentAge;
	}

	public void setStudentAge(String studentAge) {
		this.studentAge = studentAge;
	}

	@OneToOne(fetch = FetchType.LAZY, mappedBy = "student", cascade = CascadeType.ALL)
	public StudentInformation getStudentInfo() {
		return studentInfo;
	}

	public void setStudentInfo(StudentInformation studentInfo) {
		this.studentInfo = studentInfo;
	}

	public static long getSerialversionuid() {
		return serialVersionUID;
	}	
}

StudentInformation.java:

package com.javacodegeeks.enterprise.hibernate;

import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;

@Entity
@Table(name = "student_information", catalog = "tutorials")
public class StudentInformation {

	private Integer studentId;
	private String address;
	private String registryNumber;
	private String phoneNumber;
	private Date enlisted;

	private Student student;

	@GenericGenerator(name = "generator", strategy = "foreign", parameters = @Parameter(name = "property", value = "student"))
	@Id
	@GeneratedValue(generator = "generator")
	@Column(name = "STUDENT_ID", unique = true, nullable = false)
	public Integer getStudentId() {
		return studentId;
	}

	public void setStudentId(Integer studentId) {
		this.studentId = studentId;
	}

	@Column(name = "ADDRESS", nullable = false, length = 100)
	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	@Column(name = "REGISTRY_NUMBER", length = 255)
	public String getRegistryNumber() {
		return registryNumber;
	}

	public void setRegistryNumber(String registryNumber) {
		this.registryNumber = registryNumber;
	}

	@Column(name = "PHONE_NUMBER", length = 255)
	public String getPhoneNumber() {
		return phoneNumber;
	}

	public void setPhoneNumber(String phoneNumber) {
		this.phoneNumber = phoneNumber;
	}

	@Temporal(TemporalType.DATE)
	@Column(name = "ENLISTED", nullable = false)
	public Date getEnlisted() {
		return enlisted;
	}

	public void setEnlisted(Date enlisted) {
		this.enlisted = enlisted;
	}

	@OneToOne(fetch = FetchType.LAZY)
	@PrimaryKeyJoinColumn
	public Student getStudent() {
		return student;
	}

	public void setStudent(Student student) {
		this.student = student;
	}
}

These are the basic things you need to know about Hibernate annotations :

  • @Entity : used to mark the specific class as a Hibenrate entity class that will be mapped to a database table.
  • @Table : used to specify the database table that this class is mapped to. If @Table annotation is not specified, the class name will be considered as the table name.
  • @Id : used to specify the attribute that corresponds to the primary key of the databse table.
  • @GeneratedValue : used to specify the primary key generation strategy and used for auto genarated ids (e.g auto increment in this example).
  • @Column : used to specify the the column to which a field will be mapped. If it is not specified the attribute name and type will be considered as the column name and type respectively.
  • @OneToOne : used to define a one-to-one relationship with the Class of that specific attribute. The fetch = FetchType.LAZY attribute is a hint to the persistence provider runtime that data should be fetched when it is first accessed. If no fetch strategy is declared, then lazy is used by default.On the other hand, EAGER strategy is a requirement on the persistence provider runtime that data must be fecthed when asked and not only when accessed. When we have 2 linked tables, only 1 of those tables has a foreign key constraint to the other one. The mappedBy = "student" attributes denotes that you can still link from the table not containing the foreign key to the other table
  • @GenericGenerator : generator annotation describing any kind of Hibernate generator in a detyped manner. The strategy (in StudentInformation.java) has value foreign because the attribute is a foreign key to student table
  • @Temporal : used to denote temporal types e.g dates.
  • @PrimaryKeyJoinColumn : Defines mapping for composite foreign keys

4. Update Hibernate configuration file

Go to the package explorer and find hibernate.cfg.xml file and paste the following code:

hibernate.cfg.xml:

<?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>
		<property name="hibernate.bytecode.use_reflection_optimizer">false</property>
		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="hibernate.connection.username">root</property>
		<property name="hibernate.connection.password"></property>
		<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/tutorials</property>
		<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
		<property name="show_sql">true</property>
		<mapping class="com.javacodegeeks.enterprise.hibernate.Student"></mapping>
		<mapping class="com.javacodegeeks.enterprise.hibernate.StudentInformation"></mapping>
	</session-factory>
</hibernate-configuration>

5. Run the application

You don’t have to change anything to App.java. Simply, run the program.

This is the output of the program:

log4j:WARN No appenders could be found for logger (org.hibernate.type.BasicTypeRegistry).
log4j:WARN Please initialize the log4j system properly.
Hibernate: insert into tutorials.student (STUDENT_NAME, STUDENT_Age) values (?, ?)
Hibernate: insert into tutorials.student_information (ADDRESS, REGISTRY_NUMBER, PHONE_NUMBER, ENLISTED, STUDENT_ID) values (?, ?, ?, ?, ?)
Great! Student was saved

This was an example on Hibernate One-to-One Relationship Example using Annotations. Download the Eclipse project of this part : Hibernate1to1AnnotExample.zip

Nikos Maravitsas

Nikos has graduated from the Department of Informatics and Telecommunications of The National and Kapodistrian University of Athens. During his studies he discovered his interests about software development and he has successfully completed numerous assignments in a variety of fields. Currently, his main interests are system’s security, parallel systems, artificial intelligence, operating systems, system programming, telecommunications, web applications, human – machine interaction and mobile development.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button