Mapping Associations: One to One
On a few of my websites, I provide some online, multiple choice exams to help people test their portal skills, or Java skills, hopefully helping people evaluate whether or not they're ready to write an IBM or Sun certification exam. I have a few different exams that people can take, so a very regular request from my application is a listing of all of the exams that I've made available. That triggers a quick query against the Exam table in my database.
Less frequently requested, but equally important, is detailed information on a particular exam, namely, information about the number of question on an exam, the passing score, and even the full, official name of the exam. These pieces of information are stored in a separate database table that is queried much less frequently, and as such, the table is optimized accordingly.
In my online exam application, my
class to database table mapping is pretty direct, with a class named Exam mapping to the exam table, and a class named ExamDetail mapping to the exam_detail table.
In this tutorial, my goal is to use the Exam and ExamDetail classes as an example, and demonstrate how to map a one
to one association between Java classes using Hibernate and JPA annotations, just as I have done on my own websites that provide free, online, mock certification exams.
:)
One to One Hibernate Mappings
By the way, in an earlier tutorial, we mapped a philosophically similar one to one mapping between a User and a UserDetail. You may be asking yourself how the Exam and the ExamDetail scenario is different from the User and the UserDetail scenario. Well, if you recall, the User and UserDetail had all of their fields assembled into one, single, monolithic table named User. However, I'm actually not a huge fan of that type of branching, so for the Exam and the ExamDetail, I'm going to map the Java classes to two independent tables in the examscam database named exam and exam_detail.
The ExamDetail Class
The ExamDetail class is fairly anemic, simply maintaining extra information about an exam. The properties of the ExamDetail class include an id of type int, an int property to keep track of the number of questions on the exam, and an int property that tells you what the passing score is for the exam. There is also a String property named fullName to keep track of the actual, full name of the exam. For example, SCJA is the short name for the entry level Java certification exam from Sun, whereas the full name is 'Sun Certified Java Associate.'
The Exam Class
The Exam class isn't necessarily chock full of properties either, with a simple id of type int to maintain the primary key, and a String property called shortName to keep track of the abbreviated description of the exam. However, each Exam instance must keep track of its associated ExamDetail instance, so the Exam class defines a property of type ExamDetail, simply named detail. The ExamDetail instance defined in the Exam class also comes with the requisite setters and getters.
Also, since the Exam maintains a reference to the ExamDetail, we call this a unidirectional association, as the Exam knows about its associated detail, but the detail does not have any knowledge of its encapsulating exam.
The Un-Annotated Exam
package com.examscam.hib.exam;
public class Exam {
private int id;
private String shortName;
private ExamDetail detail;
public int getId() {
return id;
}
private void setId(int id) {
this.id = id;
}
public String getShortName() {
return shortName;
}
public void setShortName(String shortName) {
this.shortName = shortName;
}
/* Notice that the Exam maintains an
instance of the ExamDetail class. */
public ExamDetail getDetail() {
return detail;
}
public void setDetail(ExamDetail detail) {
this.detail = detail;
}
}
The Un-Annotated ExamDetail Class
package com.examscam.hib.exam;
public class ExamDetail {
private int id;
private String fullName;
private int numberOfQuestions;
private int passingPercentage;
public int getId() {
return id;
}
private void setId(int id){
this.id = id;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public int getNumberOfQuestions() {
return numberOfQuestions;
}
public void setNumberOfQuestions(int numberOfQuestions){
this.numberOfQuestions = numberOfQuestions;
}
public int getPassingPercentage() {
return passingPercentage;
}
public void setPassingPercentage(int passingPercentage){
this.passingPercentage = passingPercentage;
}
}New Tables: exam and exam_detail
So, we have defined two classes, the Exam and the ExamDetail classes, with the Exam class maintaining a reference to the ExamDetail class through an instance variable that is very uncreatively named detail. Now, the goal of this chapter is to map these two associated classes to two database tables named exam and exam_detail. Here's the ddl for the exam and the exam_detail tables if you'd like to create them from scratch.
exam table ddl
CREATE TABLE 'examscam '. 'exam ' (
'id ' int(11) NOT NULL auto_increment,
'shortName ' varchar(255) default NULL,
'detail_id ' int(11) default NULL,
PRIMARY KEY ( 'id '),
KEY 'FK2FB81FB83A97F5 ' ( 'detail_id '),
CONSTRAINT 'FK2FB81FB83A97F5 '
FOREIGN KEY ( 'detail_id ')
REFERENCES 'exam_detail ' ( 'id ')
)
exam_detail table ddl
DROP TABLE IF EXISTS 'examscam '. 'exam_detail ';
CREATE TABLE 'examscam '. 'exam_detail ' (
'id ' int(11) NOT NULL auto_increment,
'fullName ' varchar(255) default NULL,
'numberOfQuestions ' int(11) NOT NULL default '0',
'passingPercentage ' int(11) NOT NULL default '0',
PRIMARY KEY ( 'id ')
)
Entity Mappings: The Basics
We need to add the appropriate JPA annotations to properly describe the mapping between our Java classes to Hibernate and the underlying database tables. Starting off, our Java classes require the standard @Entity, @Table and @Id annotations that describe their basic database mapping semantics. Note that the ExamDetail class is mapped to a table named exam_detail.
@Entity
@Table(name = "exam", schema = "examscam")
public class Exam { }

@Entity
@Table(name = "exam_detail", schema="examscam")
public class ExamDetail { }
Annotating the getId() Methods
Remember that both classes need the getId() method to be decorated with the appropriate JPA annotations, as below:
/* this will be identical for the getId() method in BOTH the Exam and ExamDetail classes*/
@Id
@GeneratedValue
@Column(name = "id")
public int getId() {return id;}
The @OneToOne JPA Annotation
We have defined a unidirectional association between the Exam and the ExamDetail object by declaring an instance variable of type ExamDetail, named detail, in the Exam class. To tell Hibernate about this association, we mark the getter method, getDetail(), with the special @OneToOne annotation.
@OneToOne
public ExamDetail getDetail(){ return detail; }
The @OneToOne annotation essentially tells Hibernate to enforce the assertion that one Exam object is associated with one, and only one, ExamDetail object.
Mapping the Foreign Key
Now, in order to map and manage the @OneToOne relationship between database tables, Hibernate expects the table for which the enclosing class is mapped, which in this case is the Exam, to have a foreign key that maps to a primary key in the enclosed table, which in this case would be the ExamDetail class, which maps to the exam_detail table.

The @JoinColumn AnnotationSomething to note is that by default, Hibernate uses the name of the instance variable in the enclosing class when looking for a mapped foreign key to the enclosed class. So, even though the enclosed instance in the Exam class is of type ExamDetail, the instance is simply named detail, which causes Hibernate to look for a foreign key in the Exam table which will be named detail_id, as opposed to examdetail_id. Note, that there is no property in the Exam Java class called detail_id . detail_id only exists in the exam table in the database as a foreign key, not in the object model.
To specify a custom name for the foreign key that maps the Exam to it's ExamDetail in the database, you can use the @JoinColumn annotation:
@JoinColumn(name="detail_id")
For our Exam class, the @JoinColumn annotation would look something like this:
@OneToOne
@JoinColumn(name="detail_id")
public ExamDetail getDetail() {
return detail;
}
Saving @OneToMany Mapped Records
So, lets say you've created your Exam and ExamDetail POJOs, and you've added the @OneToMany annotation to the getExamDetail() method of the Exam class. How would you go about saving an instance of your Exam class? Well, first off, you would create and initialize an instance of the Exam:
Exam exam = new Exam();
exam.setShortName("SCJA");Then, you would create and initialize an instance of the ExamDetail class:
ExamDetail detail = new ExamDetail();
detail.setFullName("Sun Certified Associate");
detail.setNumberOfQuestions(50);
detail.setPassingPercentage(60);
Then, you need to associate the ExamDetail instance with the Exam, so the instance of the ExamDetail is passed to the setDetail() method of the exam.
exam.setDetail(detail); This would successfully create an instance of an Exam, along with an ExamDetail instance that is associated with the Exam.

Saving Associated Objects with
Hibernate
So, now you've got the Exam and ExamDetail instances initialized appropriately, and you have the ExamDetail instance associated with the Exam. Assuming you've gone through all the plumbing code to appropriately initialize the Hibernate Session, how would you save your data? Well, you have two JPA annotated instances floating around, and as such, both must touch the Hibernate Session. So, to properly save the instance of the Exam, and the associated instance of the ExamDetail, your code would look like this:
Saving the Exam and the ExamDetail
session.save(exam);
session.save(detail);
A Common Mistake with Default Settings
A common mistake when persisting entities to the database, when default settings are in effect, is to assume that since you are persisting the enclosing entity by passing it to the save method of the Hibernate Session, that all of the enclosed or associated entities will be persisted as well. This is not the case. Since both the ExamDetail and Exam are separately defined JPA annotated Java classes, the default behavior of Hibernate is to require all instances to touch the Hibernate Session at some point in order to have their state persisted to the database. Failure to do so will generate a TransientObjectException, and a little message such as object
references an unsaved transient instance. Of course, if your application has associated entities, you'd probably like the ability to pass an enclosing entity, such as an instance of the Exam class, to the save method of the Hibernate Session, and have all of the associated entities persisted as well. To do this, all you have to do is provide a cascade attribute to the @OneToOne annotation, and set the cascade attribute to one of the five CascadeType values.
The JPA CascadeType EnumerationWhen we decorate our Java code with the @OneToOne annotation, we really should put in a special attribute named cascade, and explicitly set it to at least one of the five possible CascadeType values.
For example, if we wanted session.save(exam); to save not only the properties of the exam instance, but also the properties of all of the associated mapped instances, which would include the associated ExamDetail, we could use the cascade setting of CascadeType.PERSIST.
@OneToOne(cascade=CascadeType.PERSIST)
@JoinColumn(name="detail_id")
public ExamDetail getDetail() {return detail;}
CascadeType.PERSIST
So, with the getDetail() method of the Exam class annotated with the cascade attribute being set to CascadeType.PERSIST, whenever the Hibernate framework saves an exam, all of the associated ExamDetail data will be persisted as well. But be warned, that only works for saving; it doesn't help you out with refreshes, deletes and merges. There are other CascadeType options for that.
The javax.persistence.CascadeType Enum
The CascadeType is an enumeration type that defines five possible states:
public enum CascadeType{
ALL,
PERSIST,
MERGE,
REMOVE,
REFRESH
}
javax.persistence.CascadeType.REMOVE
Setting the cascade attribute on the @OneToOne annotation to CascadeType.PERSIST helps you out during the creation of a database record, but it doesn't cover you when you want to delete a record. If, when you delete an record in the Exam table, you also want to delete the associated ExamDetail record, well, you have to add another CascadeType option to your annotation. The CascadeType associated with a deletion is appropriately named CascadeType.REMOVE.
@OneToOne(cascade={CascadeType.PERSIST,
CascadeType.REMOVE})
@JoinColumn(name="detail_id")
public ExamDetail getDetail() {return detail;}
With a setting of CascadeType.REMOVE set on the getDetail() method in the Exam class, when an Exam record is deleted from the database, the associated ExamDetail record will be deleted as well.
javax.persistence.CascadeType.REFRESH
Along with cascading the persistence and removal functions, you can also ensure that an associated, or linked property, is refreshed at the same time the associating object is refreshed. To ensure that our ExamDetail is refreshed from the database every time our Exam is refreshed, we can add the CascadeType.REFRESH attribute to the @OneToOne annotation.
@OneToOne(cascade={CascadeType.PERSIST,
CascadeType.REMOVE,
CascadeType.REFRESH})
@JoinColumn(name="detail_id")
public ExamDetail getDetail() {return detail;}
Updating: The JPA CascadeType.MERGE
For reassociating a detached object with the Hibernate Session, there is the CascadeType.MERGE setting you can place on your @OneToOne relationships. Basically, the MERGE option ensures when an encapsulating instance, such as the Exam, is reattached to its persistent storage, any changes or updates to the encapsulated class, which would be the ExamDetail, would be merged as well.
@OneToOne(cascade={CascadeType.PERSIST,
CascadeType.REMOVE,
CascadeType.REFRESH,
CascadeType.MERGE})
@JoinColumn(name="detail_id")
public ExamDetail getDetail() {return detail;}
javax.persistence.CascadeType.All
Finally, if you want to take advantage of
all of the PERSIST, REMOVE, REFRESH and MERGE cascade options, you can
eliminate a lot of code by simply adding in the ALL option for the
CascadeType. In fact, CascadeType.ALL is probably the most commonly
used type in a Hibernate/JPA based application.
@OneToOne(cascade={CascadeType.ALL)
@JoinColumn(name="detail_id")
public ExamDetail getDetail() {return detail;}
The FetchType and Lazy Loading
Another important attribute you can set for an association is the fetch attribute, which determines how an associated property is loaded. The fetch attribute can be set to one of two values, FetchType.LAZY or FetchType.EAGER.
Essentially, if the FetchType is set to EAGER, when an encapsulating object is loaded, all of the EAGERly associated objects will be loaded into memory as well. When the FetchType is set to LAZY, when an encapsulating object is loaded, only the attributes defined directly in the class itself are loaded into memory, and any properties mapped through LAZY associations will not be loaded into memory until they are explicitly requested; essentially, the associated properties will be detached.
Default Fetch Settings
For performance reasons, it's always preferential to minimize the load placed on a system's memory. As such, most associations are set to a FetchType of LAZY. This is especially true for one to many and many to many relationships, where the impact of loading a large number of associated properties can be significant.
For one-to-many and many-to-many relationships, the default FetchType is LAZY. However, for one-to-one mappings, the default is EAGER, meaning that a class, and all of its one-to-one associations, are loaded into memory when it is invoked by the client. If this is not the behavior you want, you can set the FetchType to LAZY in your @OneToOne annotations.
@OneToOne(cascade=CascadeType.ALL,
fetch=FetchType.LAZY)
@JoinColumn(name="detail_id")
public ExamDetail getDetail() {
return detail;
}
The @OneToOne Owned Entity
In our application, the Exam is considered the owning entity, and the ExamDetail is considered the owned entity.
Our Exam class keeps track of its associated ExamDetail through the definition of an instance variable. From a database perspective, this association manifests itself as a foreign key named detail_id in the exam table.
Unidirectional vs. Bi-Directional
Associations
From the standpoint of our Java code so far, the Exam and the ExamDetail have a uni-directional relationship, where the Exam knows about it's associated ExamDetail, but an ExamDetail does not know about it's associated Exam. But what if this uni-directional relationship wasn't enough?
Implementing Bi-Directional
RelationshipsTo implement a bi-directional relationship between the Exam and the ExamDetail, we simply add an instance variable of type Exam, along with the corresponding setters and getters, in the ExamDetail class, and then decorate that getExam() method with the @OneToOne annotation, using a special mappedBy attribute to link the enclosed class back to the instance variable used to link to it in the enclosing class. So, since the Exam class defines an instance variable of type ExamDetail named detail, the ExamDetail's @OneToOne annotation would include a mappedBy attribute set to detail.
Here's how it would look in code:
public class ExamDetail {
/* an instance variable of type Exam is needed */
private Exam exam;
/*detail is the instance variable name in the Exam */
@OneToOne(mappedBy="detail",cascade=CascadeType.ALL)
public Exam getExam(){return exam;}
public void setExam(Exam exam){this.exam=exam;}
}
The Fully Annotated Exam Class
package com.examscam.hib.exam;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
@Entity
@Table(name = "exam", schema = "examscam")
public class Exam {
/* @Basic annotation has been
added to all basic fields*/
private int id;
private String shortName;
private ExamDetail detail;
@Id
@GeneratedValue
@Column(name = "id")
public int getId() { return id; }
private void setId(int id) {this.id = id;}
@OneToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
@JoinColumn(name="detail_id")
public ExamDetail getDetail() {return detail; }
public void setDetail(ExamDetail detail) {
this.detail = detail;
}
@Basic
public String getShortName() { return shortName;}
public void setShortName(String shortName) {
this.shortName = shortName;
}
}
The Fully Annotated ExamDetail Class
package com.examscam.hib.exam;
import javax.persistence.Basic;
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 = "exam_detail", schema = "examscam")
public class ExamDetail {
/* @Basic annotation has been
added to all basic fields*/
private int id;
private String fullName;
private int numberOfQuestions;
private int passingPercentage;
private Exam exam;
@Id
@GeneratedValue
@Column(name = "id")
public int getId() {return id;}
private void setId(int id) {this.id = id;}
@OneToOne(cascade=CascadeType.ALL, mappedBy="detail")
public Exam getExam(){return exam;}
public void setExam(Exam exam){this.exam=exam;}
@Basic
public String getFullName() {return fullName;}
public void setFullName(String fullName) {
this.fullName = fullName;
}
@Basic
public int getNumberOfQuestions() {
return numberOfQuestions;
}
public void setNumberOfQuestions(int numberOfQuestions){
this.numberOfQuestions = numberOfQuestions;
}
@Basic
public int getPassingPercentage() {
return passingPercentage;
}
public void setPassingPercentage(int passingPercentage){
this.passingPercentage = passingPercentage;
}
}
Testing the Exam & ExamDetail
Let's take a final look at how instances of the bi-directionally related Exam and ExamDetail classes could be created in a Java application, and subsequently persisted to the database:
Notice that the last iteration of the Exam and ExamDetail classes changed the CascadeType to CascadeType.ALL. With a CascadeType of ALL, when an instance of the Exam class is saved to the database, the associated ExamDetail will be persisted as well.
public static void main(String args[]){
HibernateUtil.recreateDatabase();
Exam exam = new Exam();
exam.setShortName("SCJA");
ExamDetail detail = new ExamDetail();
detail.setFullName("Sun Certified Java Associate");
detail.setPassingPercentage(62);
detail.setNumberOfQuestions(55);
exam.setDetail(detail);
Session session = HibernateUtil.beginTransaction();
//possible due to CascadeType.ALL
session.save(exam);
HibernateUtil.commitTransaction();
}As you can see from a quick inspection of the database after running this code, the exam and exam_detail tables have been populated appropriately, as the detail_id column of the exam table points to the corresponding record in the exam_detail table.
Class Diagram for Exam and ExamDetail
ERD for Exam and ExamDetail Tables
(Again)

|