Hibernate / JPA One to One Mappings

Learn to create and manage one-to-one relationships between entities in a hibernate/JPA-based application using @OneToOne annotation. We will be learning to create the association in 4 different ways.

1. Overview

We are taking the example of an employee and his account having one to one relationship. We are assuming that

  • an employee can have only one Account
  • an account will be associated with one employee only
  • EmployeeEntity is the owner of the relationship in a bi-directional relationship.

In hibernate, there are primarily 3 ways to create one-to-one relationships between two entities. Either way, we have to use @OneToOne annotation.

  1. The first technique is widely used and uses a foreign key column in one of the tables.
  2. The second technique uses a rather known solution of having a join table to store the mapping between the first two tables.
  3. The third technique is something new that uses a common primary key in both tables.

2. Using a Foreign Key Association

In this kind of association, a foreign key column is created in the owner entity. For example, we have made EmployeeEntity owner, then an extra column "ACCOUNT_ID" will be created in Employee table. This column will store the foreign key for Account table.

The table structure will be like this:

foreign-key-association-one-to-one-2460824

To make such an association, refer the Account entity in EmployeeEntity class as follow:

@Entity
@Table(name = "EMPLOYEE")
public class EmployeeEntity implements Serializable { 

      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      @Column(name = "ID")
      private Integer employeeId;

      @OneToOne
      @JoinColumn(name="ACCOUNT_ID")
      private AccountEntity account;

      //Other fields, getters, setters are hidden for brevity
}

The join column is declared with the @JoinColumn annotation that looks like the @Column annotation. It has one more parameter named referencedColumnName. This parameter declares the column name in the targeted entity that will be used to join.

If no @JoinColumn is declared on the owner side, the defaults apply. A join column(s) will be created in the owner 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 (and only one) has to be the owner. The owner is responsible for the association column(s) update. To declare any side as not responsible for the relationship, the attribute mappedBy is used. The ‘mappedBy‘ refers to the property name of the association on the owner’s side.

@Entity
@Table(name = "ACCOUNT")
public class AccountEntity implements Serializable {

      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      @Column(name = "ID")
      private Integer accountId;

      @OneToOne(mappedBy = "account")
      private EmployeeEntity employee;

      //Other fields, getters, setters are hidden for brevity
}

Above "mappedBy” attribute declares that it is dependent on the owner entity for mapping.

Let’s test the above mappings:

AccountEntity account = new AccountEntity();
account.setAccountNumber("123-345-65454");

// Add new Employee object
EmployeeEntity emp = new EmployeeEntity();
emp.setEmail("demo-user@mail.com");
emp.setFirstName("demo");
emp.setLastName("user");

// Save Account
session.persist(account);

Assertions.assertNotNull(account.getAccountId());

// Save Employee
emp.setAccount(account);
session.persist(emp);
Assertions.assertNotNull(emp.getEmployeeId());

Assertions.assertNotNull(emp.getAccount().getAccountId());

Running the above code creates the desired schema in the database and runs these SQL queries.

Hibernate: insert into ACCOUNT (ACC_NUMBER) values (?)
Hibernate: insert into Employee (ACCOUNT_ID, EMAIL, FIRST_NAME, LAST_NAME) values (?, ?, ?, ?)

We can verify the data and mappings in both tables when we run the above program.

3. Using a Join Table

This approach is not new to all of us. Here, hibernate will create a new table that will store the primary key values from both entities. Let us start with the targeted DB structure in this technique.

join-table-one-to-one-mapping-7664579

In this technique, the main annotation to be used is @JoinTable. This annotation is used to define the new table name (mandatory) and foreign keys from both of the tables. Let’s see how it is used:

@Entity
@Table(name = "EMPLOYEE")
public class EmployeeEntity implements Serializable { 

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "ID")
  private Integer employeeId;

  @OneToOne(cascade = CascadeType.ALL)
  @JoinTable(name = "EMPLOYEE_ACCCOUNT",
      joinColumns = @JoinColumn(name = "EMPLOYEE_ID"),
      inverseJoinColumns = @JoinColumn(name = "ACCOUNT_ID"))
  private AccountEntity account;

  //Other fields, getters, setters are hidden for brevity
}

@JoinTable annotation is used in EmployeeEntity class. It declares that a new table EMPLOYEE_ACCOUNT will be created with two columns EMPLOYEE_ID (primary key of EMPLOYEE table) and ACCOUNT_ID (primary key of ACCOUNT table).

Testing the above entities generates the following SQL queries in log files:

Hibernate: insert into ACCOUNT (ACC_NUMBER) values (?)
Hibernate: insert into Employee (EMAIL, FIRST_NAME, LAST_NAME) values (?, ?, ?)
Hibernate: insert into EMPLOYEE_ACCCOUNT (ACCOUNT_ID, EMPLOYEE_ID) values (?, ?)

4. Using a Shared Primary Key

In this technique, hibernate will ensure that it will use a common primary key value in both tables. This way primary key of EmployeeEntity can safely be assumed the primary key of AccountEntity also.

The table structure will be like this:

shared-primary-key-one-to-one-8711420

In this approach, @PrimaryKeyJoinColumn is the main annotation to be used. Let us see how to use it.

@Entity
@Table(name = "EMPLOYEE")
public class EmployeeEntity implements Serializable { 

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "ID")
  private Integer employeeId;

  @OneToOne(cascade = CascadeType.ALL)
  @PrimaryKeyJoinColumn
  private AccountEntity account;

  //Other fields, getters, setters are hidden for brevity
}

In AccountEntity side, it will remain dependent on the owner entity for the mapping.

@Entity
@Table(name = "ACCOUNT")
public class AccountEntity implements Serializable {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "ID")
  private Integer accountId;

  @OneToOne(mappedBy="account", cascade=CascadeType.ALL)
  private EmployeeEntity employee; 

  //Other fields, getters, setters are hidden for brevity
}

Testing the above entities generates the following SQL queries in log files:

Hibernate: insert into ACCOUNT (ACC_NUMBER) values (?)
Hibernate: insert into Employee (ACCOUNT_ID, EMAIL, FIRST_NAME, LAST_NAME) values (?, ?, ?, ?)

5. Using a Shared Primary Key with @MapsId

In this technique, hibernate assumes both the source and target share the same primary key values. When using @MapsId, the parent-side association becomes redundant since the child-entity can be easily fetched using the parent entity identifier.

In this approach, @MapsId is the main annotation to be used. Let’s see how to use it.

@Entity
@Table(name = "EMPLOYEE")
public class EmployeeEntity implements Serializable { 

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "ID")
  private Integer employeeId;

  @OneToOne
  @MapsId
  private AccountEntity account;

  //Other fields, getters, setters are hidden for brevity
}

In AccountEntity side, it will remain dependent on the owner entity for the mapping. So no changes are required on AccountEntity side.

@Entity
@Table(name = "ACCOUNT")
public class AccountEntity implements Serializable {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "ID")
  private Integer accountId;

  //Other fields, getters, setters are hidden for brevity
}

Testing the above entities generates the following SQL queries in log files:

Hibernate: insert into ACCOUNT (ID, ACC_NUMBER) values (?, ?)
Hibernate: insert into Employee (EMAIL, FIRST_NAME, LAST_NAME, account_ID) values (?, ?, ?, ?)

So, we have seen all 4 different ways to create one-to-one mapping supported in hibernate. I will suggest you download the source code and play with it.

Happy Learning !!

Sourcecode on Github

Was this post helpful?

Join 7000+ Awesome Developers

Get the latest updates from industry, awesome resources, blog updates and much more.

* We do not spam !!

36 thoughts on “Hibernate / JPA One to One Mappings”

  1. Hi Lokesh,
    This article is very good, your way explanation also very good. I studied various articles on One to One and i found total 6 technical ways to define One to One mapping (all information is not defined any where, based on understanding on articles, I defined as below ways)
    1. Unidirectional with Foreign Key
    2. Unidirectional with Shared Primary Key.
    3. Bidirectional with Foreign Key
    4. Bidirectional with Shared Primary Key.
    5. Bidirectional with MapsId
    6. Join Table.

    Requesting could you please clarify my doubt, how many ways can we define One to One mapping ?

    Reply
  2. Thanks for this Article.. I’m having query related to lazy fetching.. how to achieve the same using the above @OneToOne mapping example. Please can you help on that..

    Reply
  3. Wonderful explanation!
    You wrote a great article. Earlier I spent a lot of time studying this topic but I did not understand it. Thank you!

    Reply
  4. User.java
    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy="user")
    private Set<Address> userAddresses = new HashSet<Address>();
    
    Address.java
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="UserID")
    private User user;
    

    In OneToMany relation, the many side is the owner of the relation right ? but by specifying mappedBy=user we are saying that User is the owning entity.why?
    Can you please explain the above concept ?

    for bi directional rules check oracle documentation:https://docs.oracle.com/cd/E19798-01/821-1841/bnbqj/index.html

    Reply
  5. Hi. I have a question about @OneToOne(cascade = CascadeType.ALL) when you implement “Using a common join table”. In this case, the cascade is on the side of EmployeeEntity. But I wonder, if the mappedby is in AccountEntity, then EmployeeEntity has a foreign key in AccountEntity, and I expect that removing, for example, a record from AccountEntity, the removal will be cascaded also in EmployeeEntity. So, shouldn’t the cascade be on the side of AccountEntity, where you also have the mapped by? Thanks for these great tutorials.

    Valentino

    Reply
  6. In your “mappedBy” example no foreign key relationship maintained by EmployeeEntity. Even there is no relationship between both table . You can cross verify your sql query above. i.e.

    Hibernate: insert into ACCOUNT (ACC_NUMBER) values (?)
    Hibernate: insert into Employee (ACCOUNT_ID, EMAIL, FIRST_NAME, LAST_NAME) values (?, ?, ?, ?)

    there is no update query for foreign key.

    Please clarify?

    I think @JoinColumn instead of @PrimaryKeyJoinColumn is better choice.

    Reply
  7. Hi,

    I tried foreign key column model, my question is why hibernate creates join table ’employee_acccount’ and have no use in one to one association. can any one clear my doubt?

    Reply
      • thanks but I got How to do it…
        I am pasting my example here…

        1) AccountEntity.java

        package com.hbm;
        
        import java.io.Serializable;
        import java.util.HashSet;
        import java.util.Set;
        
        import javax.persistence.Column;
        import javax.persistence.Entity;
        import javax.persistence.GeneratedValue;
        import javax.persistence.GenerationType;
        import javax.persistence.Id;
        import javax.persistence.OneToMany;
        import javax.persistence.Table;
        import javax.persistence.UniqueConstraint;
        
        
        @Entity(name = "JoinTableAccountEntity")
        @Table(name = "ACCOUNT", uniqueConstraints = {
        @UniqueConstraint(columnNames = "ID")})
        public class AccountEntity implements Serializable
        {
        
           private static final long serialVersionUID = -6790693372846798580L;
        
           @Id
           @GeneratedValue(strategy = GenerationType.IDENTITY)
           @Column(name = "ID", unique = true, nullable = false)
           private Integer accountId;
        
           @Column(name = "ACC_NUMBER", unique = true, nullable = false, length = 100)
           private String accountNumber;
           
           public AccountEntity( String accountNumber) {
        	super();
        	this.accountNumber = accountNumber;
        }
        
        @OneToMany(mappedBy = "accountentity")
           private Set empaccdetail= new HashSet();
        
           public Set getEmpaccdetail() {
        	return empaccdetail;
        }
        
        public void setEmpaccdetail(Set empaccdetail) {
        	this.empaccdetail = empaccdetail;
        }
        
        public Integer getAccountId() {
               return accountId;
           }
        
           public void setAccountId(Integer accountId) {
               this.accountId = accountId;
           }
        
           public String getAccountNumber() {
               return accountNumber;
           }
        
           public void setAccountNumber(String accountNumber) {
               this.accountNumber = accountNumber;
           }
        }
        

        2)EmployeeEntity.java

        package com.hbm;
        import java.io.Serializable;
        import java.util.Set;
        
        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.JoinColumn;
        import javax.persistence.JoinTable;
        import javax.persistence.OneToMany;
        import javax.persistence.Table;
        import javax.persistence.UniqueConstraint;
         
        @Entity
        @Table(name = "Employee", uniqueConstraints = {
        @UniqueConstraint(columnNames = "ID"),
        @UniqueConstraint(columnNames = "EMAIL") })
        public class EmployeeEntity implements Serializable
        {
            private static final long serialVersionUID = -1798070786993154676L;
         
            @Id
            @GeneratedValue(strategy = GenerationType.IDENTITY)
            @Column(name = "ID", unique = true, nullable = false)
            private Integer employeeId;
         
            public EmployeeEntity(String email, String firstName,
        			String lastName) {
        		super();
        		this.email = email;
        		this.firstName = firstName;
        		this.lastName = lastName;
        	}
        
        	@Column(name = "EMAIL", unique = true, nullable = false, length = 100)
            private String email;
         
            @Column(name = "FIRST_NAME", unique = false, nullable = false, length = 100)
            private String firstName;
         
            @Column(name = "LAST_NAME", unique = false, nullable = false, length = 100)
            private String lastName;
         
            @OneToMany( mappedBy="accountentity")
            private Set empaccdetail;
         
            public Integer getEmployeeId() {
                return employeeId;
            }
         
            public void setEmployeeId(Integer employeeId) {
                this.employeeId = employeeId;
            }
         
            public String getEmail() {
                return email;
            }
         
            public void setEmail(String email) {
                this.email = email;
            }
         
            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 Set getEmpaccdetail() {
        		return empaccdetail;
        	}
        
        	public void setEmpaccdetail(Set empaccdetail) {
        		this.empaccdetail = empaccdetail;
        	}
        }
        

        3)EmpAcc.java (bean for joined table)

        package com.hbm;
        
        import javax.persistence.CascadeType;
        import javax.persistence.Column;
        import javax.persistence.Entity;
        import javax.persistence.GeneratedValue;
        import javax.persistence.Id;
        import javax.persistence.JoinColumn;
        import javax.persistence.ManyToOne;
        import javax.persistence.Table;
        
        @Entity
        @Table(name = "Emp_Acc")
        public class EmpAcc 
        {
        	@Id
        	 @GeneratedValue
        	@Column(name="EMP_ACC_ID")
        		private long emp_acc_id;
        	
        	@ManyToOne(cascade = CascadeType.ALL)
        	    @JoinColumn(name = "EID")  
        	    private EmployeeEntity employeeentity;
        	 
        	  @ManyToOne(cascade = CascadeType.ALL)
        	    @JoinColumn(name = "AID")
        	    private AccountEntity accountentity;
        	    public long getEmp_acc_id() {
        		return emp_acc_id;
        	}
        	public void setEmp_acc_id(long emp_acc_id) {
        		this.emp_acc_id = emp_acc_id;
        	}
        	public EmployeeEntity getEmployeeentity() {
        		return employeeentity;
        	}
        	public void setEmployeeentity(EmployeeEntity employeeentity) {
        		this.employeeentity = employeeentity;
        	}
        	public AccountEntity getAccountentity() {
        		return accountentity;
        	}
        	public void setAccountentity(AccountEntity accountentity) {
        		this.accountentity = accountentity;
        	}
        	public int getBalance() {
        		return balance;
        	}
        	public void setBalance(int balance) {
        		this.balance = balance;
        	}
        		private int  balance;
        	
        
        }
        

        4)MainClass.java

        package com.hbm;
        
        import org.hibernate.Session;
        import org.hibernate.cfg.Configuration;
         
        public class TestJoinTable
        {
            public static void main(String[] args)
            {
                Session session = new Configuration().configure("com/hbm/hibernate.cfg.xml").buildSessionFactory().openSession();
                		session.beginTransaction();
         
                		EmployeeEntity emp=new EmployeeEntity("fdsgf@fgf"," jffksjf"," fdlskf");
                		
                		AccountEntity acc=new AccountEntity("245");
                		        		
                		EmpAcc empAcc=new EmpAcc();
                		empAcc.setEmployeeentity(emp);
                		empAcc.setAccountentity(acc);
                		empAcc.setBalance(3555);
                	
           session.save(empAcc);
            System.out.println("saved ");
                session.getTransaction().commit();
        
            }
        }
        

        In the above example balance is the new column.

        Reply
  8. your example is good one but i want one additional column in a newly created join table (employee_account) Can you please modify this example to add extra column

    Reply
  9. Hi ,
    Can you please elobarate on mappedBy attribute.
    Could not understand, even I searched so many sites.
    mappedby definition says ‘I am not the owner side and mapped by other entity’
    From the given example(Employee and Account) with below code.
    @OneToOne(mappedBy=”account”)
    private EmployeeEntity employee;
    Who is not the owner? and who is Owner and what is does actually.
    Please explains

    Reply
  10. Was learning Hibernate and went through so many tutorials online. Completely misunderstood the functionality of mappedBy till i came across this article. Good stuff guys, keep up the good work (Y)

    Reply
  11. i need a help,
    we have a list as field to store it in data base,
    for this we have to use hibernate annotations mapping class(no hibernate mapping file should be used), pojo class object contains some fields as List, those fileds will be inserted into db as string by mapping class. if possible for you, give an example

    Reply
  12. HI Lokesh,
    Good after noon,

    Currently i am working with hibernate 3. i have been trying to develop a simple hibernate project with one to many relationship.every thing is fine but i am getting error like. i have placed cfg.xml file under src folder.is correct. why i am getting error. please suggest me. as soon as…..

    log4j:WARN No appenders could be found for logger (org.hibernate.cfg.annotations.Version).
    log4j:WARN Please initialize the log4j system properly.
    Initial SessionFactory creation failed.org.hibernate.HibernateException: could not find file: hibernate.cgf.xml
    Exception in thread “main” java.lang.ExceptionInInitializerError
    at com.hib.pro.HibernateUtil.buildSessionFactory(HibernateUtil.java:23)
    at com.hib.pro.HibernateUtil.(HibernateUtil.java:11)
    at com.hib.pro.TestForeignKeyAssociation.main(TestForeignKeyAssociation.java:19)
    Caused by: org.hibernate.HibernateException: could not find file: hibernate.cgf.xml
    at org.hibernate.cfg.Configuration.configure(Configuration.java:1462)
    at com.hib.pro.HibernateUtil.buildSessionFactory(HibernateUtil.java:18)
    … 2 more
    Caused by: java.io.FileNotFoundException: hibernate.cgf.xml (The system cannot find the file specified)
    at java.io.FileInputStream.open(Native Method)
    at java.io.FileInputStream.(FileInputStream.java:106)
    at org.hibernate.cfg.Configuration.configure(Configuration.java:1459)
    … 3 more

    Reply
    • Usually src folders have filters which cause to generate only .class files in target folder or classes folder. Copy the file in resources folder. The important thing is that file should be present in target folder (war file).

      Reply
  13. Hi Lokesh,

    Good Work. But I see a flaw in the DB design of your “Using foreign key association”. In the Employee Table one can have one account shared by the multiple employees which does not make it one to one but many to one. For that you need to put Unique constraint on Account Id column in Employee Table. Please clarify.

    Reply
    • Abhi, you are right. It should be implemented this way. But, also that’s not core concept for this tutorial. Mixing concepts in single post, sometimes push the core concept in back seat. Which i did not wanted to do, otherwise in any code review session, I can myself blew this code. [:-)]

      Reply
  14. Thank you sir, My name is A Aravind ,i had completed my B.Tech with an aggregate of 70%,belongs to ECE stream. Now i need to learn JAVA very effectively,i need to get placed in Java only in reputed companies. So can you please upload Core Java tutorials,JSP,SERVLETS,Struts and Hibernates. Because i am having knowledge on only Core Java,now i want to learn Struts,Hiberbates,EJB from you.

    Thanking You, A.Aravind

    Reply

Leave a Comment

HowToDoInJava

A blog about Java and related technologies, the best practices, algorithms, and interview questions.