JPA One To One Mapping

If our application database is having a large number of tables ,in most of the applications ,  data model will be complex. So the tables needs to be related each other. In conventional Java applications , this is achieved by writing complex join queries. But it is less effective , since the debugging is also getting complex.JPA provides options to map database tables through  mapping between entities. The various relationships are :One To One, One To Many ,Many To One and many To Many. In this chapter we are discussing a One To One Relationship in JPA with suitable example.

JPA One To One Mapping

If a source object has an attribute that references to a target object . Then , that is a one to one relationship. In this case inverse relation ship can also One To One. But there is no guarantee for the inverse relationship.

Consider an  Employee class which has an attribute of class Passport. Each employee is having a unique Passport object in it. And an Employee object has one and only one Passport object in it. The class diagram can be represented as .

onetoOneCdiagram

When we consider the database , these two entities can be represented in two separate database tables.We can relate those two tables by storing a unique value from second table as foreign key in each row of first table.Our Employee and Passport entities are mapped in the example given below.

JPA One To One Mapping  Example

Tools & Softwares Required

1)Eclipse Indigo – Download Link

2)Java EE SDK 1.6 –Download Link

3)MySQL Server 5.6 Community edition – Download Link

4)SQLYog Community Edition-Download Link

5)OpenJPA libray 2.1 – Download Link

In this example we are using OpenJPA as provider implementation. The steps in creating a JPA project in eclipse is  explained in an  earlier chapter.We are using the same set up  here also.

Let us create the database JPASAMPLE and tables  EMPLOYEE_TABLE and PASSPORT_TABLE in it.We can either use SQLYOG or the command line client of MySQL for creating the database and tables.

CREATE DATABASE JPASAMPLEDB;

USE JPASAMPLEDB;

CREATE TABLE EMPLOYEE_TABLE( `EMP_ID` INT NOT NULL, `EMP_NAME` VARCHAR(50) NOT NULL, `PASSPORT_NUMBER` INT NOT NULL, PRIMARY KEY (`EMP_ID`) );

 CREATE TABLE PASSPORT_TABLE( `PASSPORT_NUMBER` INT NOT NULL, `ADDRESS_LINE1` VARCHAR(100) NOT NULL, `ADDRESS_LINE2` VARCHAR(100) NOT NULL,`STATE_NAME` VARCHAR(50) NOT NULL, `COUNTRY_NAME` VARCHAR(50) NOT NULL, PRIMARY KEY (`PASSPORT_NUMBER`) );

Let us see the Employee.java and Passport.java entity classes.We are doing the entity to table mapping through annotations.Primary key generation strategy is specified as AUTO.Database column mapping is done above the getter method.

Employee.java

import java.io.Serializable;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@Entity(name = "Employee")
@Table(name = "EMPLOYEE_TABLE")
public class Employee implements Serializable {

/**
*
*/
private static final long serialVersionUID = 1L;

private int employeeId;
private String name;
private Passport passportDetails;

public Employee() {

}

@Id
@Column(name = "EMP_ID")
@GeneratedValue(strategy = GenerationType.AUTO)
public int getEmployeeId() {
return employeeId;
}

public void setEmployeeId(int employeeId) {
this.employeeId = employeeId;
}

@Column(name = "EMP_NAME")
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "PASSPORT_NUMBER")
public Passport getPassportDetails() {
return passportDetails;
}

public void setPassportDetails(Passport passportDetails) {
this.passportDetails = passportDetails;
}

public String toString() {
return "ID = " + getEmployeeId() + " ; Name = " + getName()
+ " ; Passport Details : " + getPassportDetails();

}

}

The passport details are storing in a separate table. For doing the one to one mapping the PASSPORT_NUMBER is storing as a foreign key in EMPLOYEE_TABLE.And the mapping is done through the OneToOne annotation.In case of xml mapping , this can be  done using element . The @JoinColumn annotation is doing the mapping between the two tables.

The fetch type is specified as LAZY.This means that the object is fetched lazily from the database. Other option is EAGER.If we specify the fetch type as EAGER , then the data will be fetched from database eagerly.

The cascade value is specified as ALL. It means that the Passport object will be cascaded with parent object for all operations : detach , merge,persist,remove and refresh operations.The CascadeType enum defines all possible options.

The various options are:

CascadeType.DETACH :- Target object will be cascaded with parent object for detach operation.

CascadeType.MERGE : – Target object will be cascaded with parent object for merge operation.

CascadeType.PERSIST :- Target object will be cascaded with parent object for persist operation.

CascadeType.REFRESH :-Target object will be cascaded with parent object for refresh operation.

cascadeType.REMOVE :- Target object will be cascaded with parent object for remove operation.

CascadeType.ALL :- Target object will be cascaded with the parent object for the above operations.

Passport.java

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity(name = "Passport")
@Table(name = "PASSPORT_TABLE")
public class Passport implements Serializable {

/**
*
*/
private static final long serialVersionUID = 1L;
private int passportNumber;
private String addressLine1;
private String addressLine2;
private String state;
private String country;

public Passport() {

}

@Id
@Column(name = "PASSPORT_NUMBER")
@GeneratedValue(strategy = GenerationType.AUTO)
public int getPassportNumber() {
return passportNumber;
}

public void setPassportNumber(int passportNumber) {
this.passportNumber = passportNumber;
}

@Column(name = "ADDRESS_LINE1")
public String getAddressLine1() {
return addressLine1;
}

public void setAddressLine1(String addressLine1) {
this.addressLine1 = addressLine1;
}

@Column(name = "ADDRESS_LINE2")
public String getAddressLine2() {
return addressLine2;
}

public void setAddressLine2(String addressLine2) {
this.addressLine2 = addressLine2;
}

@Column(name = "STATE_NAME")
public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}

@Column(name = "COUNTRY_NAME")
public String getCountry() {
return country;
}

public void setCountry(String country) {
this.country = country;
}

public String toString() {
return "Passport Number = " + getPassportNumber()
+ " ; Address LIne 1 = " + getAddressLine1()
+ " ; Address Line 2 = " + getAddressLine2()
+ " ; State Name = " + getState() + " ; Country Name = "
+ getCountry();
}

}

persistence.xml

org.apache.openjpa.persistence.PersistenceProviderImpl com.jpa.entity.Employee
com.jpa.entity.Passport

Now lets see the class with main. There are two methods :   one is for inserting the data and the other is for fetching the data  from database

OnetoOneSample.java

import java.util.Iterator;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;

import com.jpa.entity.Address;
import com.jpa.entity.Employee;
import com.jpa.entity.Passport;

public class OnetoOneSample {

public OnetoOneSample() {

}

public void insertRecord() {
EntityManagerFactory entityManagerFactory = Persistence
.createEntityManagerFactory("OpenJPASample");
EntityManager entitymanager = entityManagerFactory
.createEntityManager();
if (null != entitymanager) {
Employee employee = new Employee();
employee.setName("Bijoy");
Passport passportDetails = new Passport();
passportDetails.setAddressLine1("SKM");
passportDetails.setAddressLine2("Trivandrum");
passportDetails.setState("Kerala");
passportDetails.setCountry("India");
employee.setPassportDetails(passportDetails);
EntityTransaction transaction = entitymanager.getTransaction();
transaction.begin();
entitymanager.persist(employee);
transaction.commit();
System.out.println("The object " + employee + " is persisted");

}

}

public void readRecord() {
EntityManagerFactory entityManagerFactory = Persistence
.createEntityManagerFactory("OpenJPASample");
EntityManager entitymanager = entityManagerFactory
.createEntityManager();
if (null != entitymanager) {
EntityTransaction readTransaction = entitymanager.getTransaction();
readTransaction.begin();
Query query = entitymanager
.createQuery("select employee FROM Employee employee");
List list = query.getResultList();
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Employee employee = (Employee) iterator.next();
System.out.println("Employee Details : " + employee);
}
readTransaction.commit();
System.out.println("Read is over..");
}
}

public static void main(String[] args) {
OnetoOneSample sample = new OnetoOneSample();
sample.insertRecord();
sample.readRecord();
}

}

Output

The object : ID = 501 ; Name = Bijoy ; Passport Details : Passport Number = 551 ; Address LIne 1 = SKM ; Address Line 2 = Trivandrum ; State Name = Kerala ; Country Name = India : is persisted

Employee Details :  ID = 501 ; Name = Bijoy ; Passport Details : Passport Number = 551 ; Address LIne 1 = SKM ; Address Line 2 = Trivandrum ; State Name = Kerala ; Country Name = India

Read is over..

Bidirectional One To One Mapping

If each Employee object has a unique  Passport object in it and if each Passport object has a unique Employee object  in it  , then it is a bidirectional relation.So the Passport.java entity class should have the inverse mapping.There is no need for separate foreign keys in each table. Foreign key is needed only in the source objects table. But the mapping using the @MappedBy   annotation in the target object is important.

Employee.java is more or less same as the one already explained. The only change is in toString() method

Employee.java

import java.io.Serializable;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;

@Entity(name = "Employee")
@Table(name = "EMPLOYEE_TABLE")
public class Employee implements Serializable {

/**
*
*/
private static final long serialVersionUID = 1L;

private int employeeId;
private String name;
private Passport passportDetails;

public Employee() {

}

@Id
@Column(name = "EMP_ID")
@GeneratedValue(strategy = GenerationType.AUTO)
public int getEmployeeId() {
return employeeId;
}

public void setEmployeeId(int employeeId) {
this.employeeId = employeeId;
}

@Column(name = "EMP_NAME")
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "PASSPORT_NUMBER")
public Passport getPassportDetails() {
return passportDetails;
}

public void setPassportDetails(Passport passportDetails) {
this.passportDetails = passportDetails;
}

public String toString() {
return "[ ID = " + getEmployeeId() + " ; Name = " + getName()
+ " ; Passport Details : [" + " Passport Number = "
+ getPassportDetails().getPassportNumber()
+ " ; Address Line 1 = "
+ getPassportDetails().getAddressLine1()
+ " ; Address Line 2 = "
+ getPassportDetails().getAddressLine2() + " ; State = "
+ getPassportDetails().getState() + "; Country = "
+ getPassportDetails().getCountry() + "]]";

}

}

The Passport.java contains an additional attribute of  Employee.java.It is mapped using @OneToOne. The @MappedBy annotation is using for  indicating the inverse relation.

Passport.java

import java.io.Serializable;

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

@Entity(name = "Passport")
@Table(name = "PASSPORT_TABLE")
public class Passport implements Serializable {

/**
*
*/
private static final long serialVersionUID = 1L;
private int passportNumber;
private String addressLine1;
private String addressLine2;
private String state;
private String country;
private Employee employee;

public Passport() {

}

@Id
@Column(name = "PASSPORT_NUMBER")
@GeneratedValue(strategy = GenerationType.AUTO)
public int getPassportNumber() {
return passportNumber;
}

public void setPassportNumber(int passportNumber) {
this.passportNumber = passportNumber;
}

@Column(name = "ADDRESS_LINE1")
public String getAddressLine1() {
return addressLine1;
}

public void setAddressLine1(String addressLine1) {
this.addressLine1 = addressLine1;
}

@Column(name = "ADDRESS_LINE2")
public String getAddressLine2() {
return addressLine2;
}

public void setAddressLine2(String addressLine2) {
this.addressLine2 = addressLine2;
}

@Column(name = "STATE_NAME")
public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}

@Column(name = "COUNTRY_NAME")
public String getCountry() {
return country;
}

public void setCountry(String country) {
this.country = country;
}

@OneToOne(mappedBy = "passportDetails", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Employee getEmployee() {
return employee;
}

public void setEmployee(Employee employee) {
this.employee = employee;
}

public String toString() {
return "[ Passport Number = " + getPassportNumber()
+ " ; Address LIne 1 = " + getAddressLine1()
+ " ; Address Line 2 = " + getAddressLine2()
+ " ; State Name = " + getState() + " ; Country Name = "
+ getCountry() + " ; Employee Details : [" + "Id = "
+ getEmployee().getEmployeeId() + " ; Name = "
+ getEmployee().getName() + "]]";
}

}

Now lets see the class with main. We are using the same set of tables created in the above example.We are trying to fetch the same data inserted there .There are two methods : the  first method fetches the Employee details from the table. The second method fetches the Passport details from the database.  when we query for Employee , it should contain the mapped Passport details within it. Also  when we query for  Passport details , it should contain the mapped Employee object.

OnetoOneReadSample.java

import java.util.Iterator;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;

import com.jpa.entity.Employee;
import com.jpa.entity.Passport;

public class OnetoOneReadSample {

public OnetoOneReadSample() {

}

public void readPassportDetails() {
EntityManagerFactory entityManagerFactory = Persistence
.createEntityManagerFactory("OpenJPASample");
EntityManager entitymanager = entityManagerFactory
.createEntityManager();
if (null != entitymanager) {
EntityTransaction readTransaction = entitymanager.getTransaction();
readTransaction.begin();
Query query = entitymanager
.createQuery("select passport FROM Passport passport");
List list = query.getResultList();
Iterator iterator = list.iterator();
System.out.println("Passport Details fetched from database : ");
while (iterator.hasNext()) {
Passport passport = (Passport) iterator.next();
System.out.println(passport);
}
readTransaction.commit();
System.out.println("Passport Details over..");
}
}

public void readEmployeeDetails() {
EntityManagerFactory entityManagerFactory = Persistence
.createEntityManagerFactory("OpenJPASample");
EntityManager entitymanager = entityManagerFactory
.createEntityManager();
if (null != entitymanager) {
EntityTransaction readTransaction = entitymanager.getTransaction();
readTransaction.begin();
Query query = entitymanager
.createQuery("select employee FROM Employee employee");
List list = query.getResultList();
Iterator iterator = list.iterator();
System.out.println("Employee Details fetched from database : ");
while (iterator.hasNext()) {
Employee employee = (Employee) iterator.next();
System.out.println(employee);
}
readTransaction.commit();
System.out.println("Employee Details over");
}
}

public static void main(String[] args) {
OnetoOneReadSample sample = new OnetoOneReadSample();
sample.readEmployeeDetails();
sample.readPassportDetails();
}

}

output

Employee Details fetched from database :

[ ID = 501 ; Name = Bijoy ; Passport Details : [ Passport Number =  551 ; Address Line 1 = SKM ; Address Line 2 = Trivandrum ; State = Kerala; Country = India]]

Employee Details over

Passport Details fetched from database :

[ Passport Number = 551 ; Address LIne 1 = SKM ; Address Line 2 = Trivandrum ; State Name = Kerala ; Country Name = India ; Employee Details : [Id =  501 ; Name = Bijoy]]

Passport Details over..

The Employee object  retrieved contains Passport details. The Passport object retrieved contains the mapped Employee object also.In this way bidirectional one to one relation ship can be implemented.

See Related Discussions

JPA Overview

JPA Example – Insert/Read

find method in JPA example

JPA Update Example

JPA Delete EXample

JPQL

JPQL Update Query Example

JPQL Delete Query

Caching

Overview to caching in JPA

Locking in JPA

Data  Locking in JPA

JPA Mapping Schemes

One To Many mapping in JPA

Many To Many Mapping

Many To One Mapping

 

Leave a Reply

Your email address will not be published. Required fields are marked *