Hibernate – How to Define Association Mappings between Entities

In previous tutorial, we learned about four persistent states of hibernate entities and there we noted that hibernate can identify java objects as persistent only when they are annotated with certain annotations; otherwise they are treated as simple plain java objects with no direct relation to database. Moving on, when we annotate the java classes with JPA annotations and make them persistent entities, we can face situations where two entities can be related and must be referenced from each other, in either uni-direction or in bi-direction. Let’s understand few basic things before actually creating references between hibernate entities.

Understanding Entities and Their Associations

Entities can contain references to other entities, either directly as an embedded property or field, or indirectly via a collection of some sort (arrays, sets, lists, etc.). These associations are represented using foreign key relationships in the underlying tables. These foreign keys will rely on the identifiers used by participating tables.

When only one of the pair of entities contains a reference to the other, the association is unidirectional. If the association is mutual, then it is referred to as bidirectional.

A common mistake by beginners, when designing entity models, is to try to make all associations bidirectional. Remember that associations that are not a natural part of the object model should not be forced into it. Hibernate Query Language (HQL) often proves a more natural way to access the required information when needed.

Ideally, we would like to dictate that only changes to one end of the relationship will result in any updates to the foreign key; and indeed, Hibernate allows us to do this by marking one end of the association as being managed by the other.

In hibernate mapping associations, one (and only one) of the participating classes is referred to as “managing the relationship” and other one is called “managed by” using ‘mappedBy’ property. We should not make both ends of association “managing the relationship”. Never do it.

Note that “mappedBy” is purely about how the foreign key relationships between entities are saved. It has nothing to do with saving the entities themselves using cascade functionality.

While Hibernate lets us specify that changes to one association will result in changes to the database, it does not allow us to cause changes to one end of the association to be automatically reflected in the other end in the Java POJOs. We must use cascading capabilities for doing so.

Understanding Associations with Example

Let’s quickly build and example to understand what we have read above about associations about entities and how this should be done. I am using two simple entities (AccountEntity and EmployeeEntity) for this example and then I will create one-to-one association between them. Then we will see how various association options change the runtime behavior and complexity.

AccountEntity.java

@Entity
@Table(name = "Account")
public class AccountEntity implements Serializable
{
   private static final long serialVersionUID = 1L;

   @Id
   @Column(name = "ID", unique = true, nullable = false)
   @GeneratedValue(strategy = GenerationType.SEQUENCE)
   private Integer           accountId;

   @Column(name = "ACC_NO", unique = false, nullable = false, length = 100)
   private String            accountNumber;

   //We will define the association here
   EmployeeEntity            employee;

   //Getters and Setters are not shown for brevity
}

EmployeeEntity.java

@Entity
@Table(name = "Employee")
public class EmployeeEntity implements Serializable
{
   private static final long serialVersionUID = -1798070786993154676L;
   @Id
   @Column(name = "ID", unique = true, nullable = false)
   @GeneratedValue(strategy = GenerationType.SEQUENCE)
   private Integer           employeeId;
   @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;

   //We will define the association here
   AccountEntity             account;

   //Getters and Setters are not shown for brevity
}

Scenario 1) Association managed by both entities

In this type of association, we will define the association as below using @OneToOne annotation.

//Inside EmployeeEntity.java
@OneToOne
AccountEntity		account;

//Inside AccountEntity.java
@OneToOne
EmployeeEntity		employee;

With above association, both ends are managing the association so both must be updated with information of each other using setter methods defined in entity java files. If you fail to do so, you will not be able to fetch the associated entity information and it will be returned as null.

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

      // Create new Employee object
      EmployeeEntity emp = new EmployeeEntity();
      emp.setFirstName("Lokesh");
      emp.setLastName("Gupta");

      // Create new Employee object
      AccountEntity acc = new AccountEntity();
      acc.setAccountNumber("DUMMY_ACCOUNT");
      emp.setAccount(acc);
      //acc.setEmployee(emp);

      sessionOne.save(acc);
      sessionOne.save(emp);
      sessionOne.getTransaction().commit();

      Integer genEmpId = emp.getEmployeeId();
      Integer genAccId  = acc.getAccountId();

      Session sessionTwo = HibernateUtil.getSessionFactory().openSession();
      sessionTwo.beginTransaction();
      EmployeeEntity employee = (EmployeeEntity) sessionTwo.get(EmployeeEntity.class, genEmpId);
      AccountEntity account = (AccountEntity) sessionTwo.get(AccountEntity.class, genAccId);

      System.out.println(employee.getEmployeeId());
      System.out.println(employee.getAccount().getAccountNumber());
      System.out.println(account.getAccountId());
      System.out.println(account.getEmployee().getEmployeeId());

      HibernateUtil.shutdown();
   }
}

Output:

Hibernate: insert into Account (ACC_NO, employee_ID, ID) values (?, ?, ?)
Hibernate: insert into Employee (account_ID, FIRST_NAME, LAST_NAME, ID) values (?, ?, ?, ?)
Hibernate: select employeeen0_.ID as ID1_1_0_, employeeen0_.account_ID as account_4_1_0_, employeeen0_.FIRST_NAME as FIRST_NA2_1_0_,
			employeeen0_.LAST_NAME as LAST_NAM3_1_0_, accountent1_.ID as ID1_0_1_, accountent1_.ACC_NO as ACC_NO2_0_1_,
			accountent1_.employee_ID as employee3_0_1_, employeeen2_.ID as ID1_1_2_, employeeen2_.account_ID as account_4_1_2_,
			employeeen2_.FIRST_NAME as FIRST_NA2_1_2_, employeeen2_.LAST_NAME as LAST_NAM3_1_2_ from Employee
			employeeen0_ left outer join Account accountent1_ on employeeen0_.account_ID=accountent1_.ID
			left outer join Employee employeeen2_ on accountent1_.employee_ID=employeeen2_.ID where employeeen0_.ID=?

20
DUMMY_ACCOUNT
10
Exception in thread "main" java.lang.NullPointerException
	at com.howtodoinjava.test.TestHibernate.main(TestHibernate.java:43)

You see that I had set the account entity in employee entity, so I am able to get it. BUT I commented the line “acc.setEmployee(emp);” and thus not set the employee entity inside account entity, so I am not able to get it.

Also observe the insert queries above. Both tables have foreign key association with column names employee_ID and account_ID respectively. It’s example of circular dependency in data and can easily bring down your application any time.

To correctly store above relationship, you must un-comment the line “acc.setEmployee(emp);“. After uncommenting the line, you will be able to store and retrieve the association as desired but still there is circular dependency.

After uncommenting the line, output will be like below:

Hibernate: insert into Account (ACC_NO, employee_ID, ID) values (?, ?, ?)
Hibernate: insert into Employee (account_ID, FIRST_NAME, LAST_NAME, ID) values (?, ?, ?, ?)
Hibernate: update Account set ACC_NO=?, employee_ID=? where ID=?
Hibernate: select employeeen0_.ID as ID1_1_0_, employeeen0_.account_ID as account_4_1_0_, employeeen0_.FIRST_NAME as FIRST_NA2_1_0_,
			employeeen0_.LAST_NAME as LAST_NAM3_1_0_, accountent1_.ID as ID1_0_1_, accountent1_.ACC_NO as ACC_NO2_0_1_,
			accountent1_.employee_ID as employee3_0_1_, employeeen2_.ID as ID1_1_2_, employeeen2_.account_ID as account_4_1_2_,
			employeeen2_.FIRST_NAME as FIRST_NA2_1_2_, employeeen2_.LAST_NAME as LAST_NAM3_1_2_ from Employee
			employeeen0_ left outer join Account accountent1_ on employeeen0_.account_ID=accountent1_.ID
			left outer join Employee employeeen2_ on accountent1_.employee_ID=employeeen2_.ID where employeeen0_.ID=?

20
DUMMY_ACCOUNT
10
20

Scenario 2) Association managed by single entity

Let’s say association is managed by EmployeeEntity then the annotations in both entity will look like below.

//Inside EmployeeEntity.java
@OneToOne
AccountEntity		account;

//Inside AccountEntity.java
@OneToOne (mappedBy = "account")
EmployeeEntity		employee;

Now to tell hibernate the association is managed by EmployeeEntity, we will add ‘mappedBy‘ attribute inside AccountEntity to make it manageable. Now to test above code we will have to set the association only once using “emp.setAccount(acc);” and employee entity is which is managing the relationship. AccountEntity dos not need to know anything explicitly.

Let’s look at below test run.

Session sessionOne = HibernateUtil.getSessionFactory().openSession();
sessionOne.beginTransaction();

// Create new Employee object
EmployeeEntity emp = new EmployeeEntity();
emp.setFirstName("Lokesh");
emp.setLastName("Gupta");

// Create new Employee object
AccountEntity acc = new AccountEntity();
acc.setAccountNumber("DUMMY_ACCOUNT");
emp.setAccount(acc);
//acc.setEmployee(emp);

sessionOne.save(acc);
sessionOne.save(emp);
sessionOne.getTransaction().commit();

Integer genEmpId = emp.getEmployeeId();
Integer genAccId  = acc.getAccountId();

Session sessionTwo = HibernateUtil.getSessionFactory().openSession();
sessionTwo.beginTransaction();
EmployeeEntity employee = (EmployeeEntity) sessionTwo.get(EmployeeEntity.class, genEmpId);
AccountEntity account = (AccountEntity) sessionTwo.get(AccountEntity.class, genAccId);

System.out.println(employee.getEmployeeId());
System.out.println(employee.getAccount().getAccountNumber());
System.out.println(account.getAccountId());
System.out.println(account.getEmployee().getEmployeeId());

HibernateUtil.shutdown();

Output:

Hibernate: insert into Account (ACC_NO, ID) values (?, ?)
Hibernate: insert into Employee (account_ID, FIRST_NAME, LAST_NAME, ID) values (?, ?, ?, ?)
Hibernate: select employeeen0_.ID as ID1_1_0_, employeeen0_.account_ID as account_4_1_0_, employeeen0_.FIRST_NAME as FIRST_NA2_1_0_,
employeeen0_.LAST_NAME as LAST_NAM3_1_0_, accountent1_.ID as ID1_0_1_, accountent1_.ACC_NO as ACC_NO2_0_1_ from Employee employeeen0_
left outer join Account accountent1_ on employeeen0_.account_ID=accountent1_.ID where employeeen0_.ID=?
Hibernate: select employeeen0_.ID as ID1_1_1_, employeeen0_.account_ID as account_4_1_1_, employeeen0_.FIRST_NAME as FIRST_NA2_1_1_,
employeeen0_.LAST_NAME as LAST_NAM3_1_1_, accountent1_.ID as ID1_0_0_, accountent1_.ACC_NO as ACC_NO2_0_0_ from Employee employeeen0_
left outer join Account accountent1_ on employeeen0_.account_ID=accountent1_.ID where employeeen0_.account_ID=?

20
DUMMY_ACCOUNT
10
20

You see that you do not need to tell anything to account entity (‘acc.setEmployee(emp)‘ is commented). Employee entity is managing the association both ways.

Another observation is regarding foreign key columns which we have only one now i.e. account_ID is Employee table. So no circular dependency as well. All good.

Guide to Define Associations for Various Mappings

Above example shows how to manage association between entities in one-to-one mapping. In above example, we could have chosen the association managed by AccountEntity as well and things would have worked out pretty well with minor code changes. But in case of other mappings (e.g. One-to-many or Many-to-one), you will not have the liberty to define associations at your will. You need rules.

Below table shows how you can select the side of the relationship that should be made the owner of a bi-directional association. Remember that to make an association the owner, you must mark the other end as being mapped by the other.

[su_table]

Type of Association
Options/ Usage
One-to-one
Either end can be made the owner, but one (and only one) of them should be; if you don’t specify this, you will end up with a circular dependency.
One-to-many
The many end must be made the owner of the association.
Many-to-one
This is the same as the one-to-many relationship viewed from the opposite perspective, so the same rule applies: the many end must be made the owner of the association.
Many-to-many
Either end of the association can be made the owner.

[/su_table]

If this all seems rather confusing, just remember that association ownership is concerned exclusively with the management of the foreign keys in the database and that’s it. I will advise you to go through my previous tutorials discussing “one-to-one mapping“, “one-to-many mapping” and “many-to-many mapping” in details. They will help you in building the concept stronger.

If you have any question then drop me a comment below.

Happy Learning !!

Was this post helpful?

Join 7000+ Fellow Programmers

Subscribe to get new post notifications, industry updates, best practices, and much more. Directly into your inbox, for free.

16 thoughts on “Hibernate – How to Define Association Mappings between Entities”

  1. Nice tutorials Lokesh. Thanks for clarifying the concepts. I was looking for an example where I can do the mapping between three entities. Details about tables can be found here https://www.w3resource.com/sql/sql-table.php. Can you please help me in writing Java Entities (POJOs) and mapping of the tables mentioned in the link. My main goal is to write a java jpa/hibernate program that can work with join queries like
    SELECT o.ord_num,c.cust_name,o.cust_code,
    a.agent_code,c.cust_city,o.ord_description
    FROM agents a,customer c,orders o
    WHERE c.cust_city=a.working_area
    AND o.cust_code=c.cust_code
    AND o.agent_code=a.agent_code
    and c.cust_code=’C00001′;

    Reply
  2. Thanks, Lokesh sir, You cleared my confusion about owner and owned entity with respect to mappedBy attribute. I googled, read the book and visited jboss official site and tired my eyes but did not get satisfactory answer. These sites just say formal jargon and typical stereotype definition that left me bewildered. Thanks man you explain it. You gave one example consist of EmployeEntity and AccountEntity and said that EmployeEntity is owner and AccountEntity is managedBy(mappedBy). You made the point clear further by explicitly show as follows

    Let’s say association is managed by EmployeeEntity then the annotations in both entity will look like below.
    //Inside EmployeeEntity.java
    @OneToOne
    AccountEntity account;

    //Inside AccountEntity.java
    @OneToOne (mappedBy = “account”)
    EmployeeEntity employee;

    Thanks again. Keep it up. We need your help

    Reply
  3. Lokesh,

    First thanks to your great work. I am new to hibernate, but I can understand your article quite easily. I understand that both are bidirectional and scenario 1 has circular dependency which is bad, and second scenario does the right thing.

    However I am a little confused by your statement at the following lines,

    “Also observe the insert queries above. Both tables have foreign key association with column names employee_ID and account_ID respectively. It’s example of circular dependency in data and can easily bring down your application any time.”

    Are these two foreign keys specified in database schema during table creations or they are just the artifact of circular dependency?

    Reply
  4. In second scenario, account table is not having any column to connect with employee ?
    How is it getting account.getEmployee() ?

    Secondly if one way mapping is able to get and insert data in both table as shown above ?
    Why do we need bi-directional ?

    In my opinion as i tried the same with JPA, we can not do account.getEmployee() in unidirectional
    I am getting a null pointer.

    Reply
    • Second scenario is not example of unidirectional mapping. It’s also bidirectional. It differs from scenario 1 only from point – who owns the association. In first case, both sides can manage the relationship; in second case, EmployeeEntity manages the association.

      Reply
  5. The example code for scenario 2) above, is not in sync with the explanation given.
    Currrent:—————————————————–
    //Inside EmployeeEntity.java
    @OneToOne
    AccountEntity account;

    //Inside AccountEntity.java
    @OneToOne (mappedBy = “account”)
    EmployeeEntity employee;

    Corrected as per explanation:——————————
    //Inside EmployeeEntity.java
    @OneToOne(mappedBy = “employee”)
    AccountEntity account;

    //Inside AccountEntity.java
    @OneToOne
    EmployeeEntity employee;

    Reply

Leave a Comment

HowToDoInJava

A blog about Java and its related technologies, the best practices, algorithms, interview questions, scripting languages, and Python.