Hibernate One to One Mapping Annotation Example

If we are working on any hibernate application or we are planning to work on any in the future, then you can easily understand the one-to-one relationships between several entities in the application. In this hibernate one to one mapping example, We will discuss 4 different variations.

Table of Contents

Various supported techniques for one to one mapping
1. Using foreign key association
2. Using join table
3. Using shared primary key
4. @MapsId

For this hibernate one to one mapping example, I am extending the example written for hibernate hello world example. We have two entities here: Employee and Account.

One Employee can have only one Account. Similarly, an account will be associated with one employee only. It’s one to one relationship for this example.

Various supported techniques

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

  1. First technique is most widely used and uses a foreign key column in one of the tables.
  2. Second technique uses a rather known solution of having a third table to store mapping between first two tables.
  3. Third technique is something new which uses a common primary key value in both the tables.

1. Hibernate one to one mapping with foreign key association

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

Table structure will be like this:

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

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


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

The join column is declared with the @JoinColumn annotation which looks like the @Column annotation. It has one more parameters named referencedColumnName. This parameter declares the column in the targeted entity that will be used to the 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 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.


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

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

Lets test above mappings in running code:


public class TestForeignKeyAssociation {

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

		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.saveOrUpdate(account);
		// Save Employee
		emp.setAccount(account);
		session.saveOrUpdate(emp);

		session.getTransaction().commit();
		HibernateUtil.shutdown();
	}
}

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


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

You can verify the data and mappings in both tables when you run above program.

2. Hibernate one to one mapping with common join table

This approach is not new to all of us. Let us start with targeted DB structure in this technique.

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

In this technique, 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. Lets see how it is used:

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

@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 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 (?, ?)

3. Hibernate one to one mapping with 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 EmployeeEntity can safely be assumed the primary key of AccountEntity also.

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 see how to use it.


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

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


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

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 (?, ?, ?, ?)

4. Hibernate one to one mapping with @MapsId

In this technique, hibernate assumes both the source and target share the same primary key values.

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

@Id
private Integer employeeId;

@OneToOne 
@MapsId
private AccountEntity account;

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

@Id
private Integer accountId;

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 to download the source code and play with it.

Happy Learning !!

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 One to One Mapping Annotation Example”

  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.