Defining Hibernate Association Mappings

When we annotate the Java POJOs with JPA annotations and make them persistent entities, we can face situations where two entities can be related, and we must reference these entities from each other. This reference between the entities can be either uni-direction or in bi-direction.

Creating these references is called mapping the associations. Before beginning the mapping between the hibernate entities, let’s understand a few basic things.

1. Entities and 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 primary ids used by participating tables.

When only one out of the two entities contains a reference to the other, the association is uni-directional. If the association is mutual and both entities refer to one another, it is bi-directional.

Not all relationships are bi-directional

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, one (and only one) of the participating entities is referred to as “managing the relationship” and the other entity is called “managed by” using ‘mappedBy‘ property. We should not make both ends of the association “managing the relationship”. Never do it.

While Hibernate lets us specify that changes to one side of the 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. We must use cascading functionality for doing so.

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

2. Understanding Entity Association with an Example

Let’s quickly build an example to understand what we have read about associations and entities, and how we should do this. We are using two entities (AccountEntity and EmployeeEntity) for this example, and we will create a one-to-one bi-directional association between them.

@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
}
@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
}

2.1. Mapping Association Managed by Both Entities

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

@OneToOne
AccountEntity       account;
@OneToOne
EmployeeEntity      employee;

With the above association, both ends are managing the association, so both must be updated with information about each other using setter methods defined in entity java files.

If we fail to do so, we 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 Account object
      AccountEntity acc = new AccountEntity();
      acc.setAccountNumber("DUMMY_ACCOUNT");
      emp.setAccount(acc);

      //***** We have intentionally commented this line*******
      //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();
   }
}

Let us check the output.

  • We can see that we had set the account entity in employee entity, so we are able to get it.
  • BUT as we have commented the line “acc.setEmployee(emp);” and thus not set the employee entity inside account entity, so we are not able to get it.
  • Also, notice the insert queries above. Both tables have foreign key associations with column names employee_ID and account_ID respectively. It’s an example of a circular dependency in data and can easily bring down your application at any time.
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_, ........
 
20
DUMMY_ACCOUNT
10
Exception in thread "main" java.lang.NullPointerException
    at com.howtodoinjava.test.TestHibernate.main(TestHibernate.java:43)

To correctly store the above relationship, you must uncomment the line “acc.setEmployee(emp);“. After uncommenting the line, we will be able to store and retrieve the association as desired, but still there is a circular dependency. To fix this, we must make the mapping managed by only one entity.

After uncommenting the said line, the 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_, ......

20
DUMMY_ACCOUNT
10
20

2.2. Association Mapping Managed by One Entity

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

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

Now to tell hibernate that the association is managed by EmployeeEntity, we will add ‘mappedBy‘ attribute inside AccountEntity to make it manageable.

Now to test the above code and we will have to set the association only once using”emp.setAccount(acc);“and employee entity is which is managing the relationship. AccountEntity does 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);

//*****This line is commented intentinally************
//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();
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_, ....

Hibernate: select employeeen0_.ID as ID1_1_1_, ....
 
20
DUMMY_ACCOUNT
10
20

We can see that we do not need to tell anything to the 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 in the Employee table. So no circular dependency as well. All good.

3. Guide to Different Types of Association Mappings

The above example shows how to manage the association between entities in a one-to-one mapping. In our example, we could have chosen the association managed by AccountEntity as well, and the things would have worked out pretty well with minor code changes because we have assumed one employee strictly has only one account.

But in the case of other mappings (e.g., One-to-many or Many-to-one), we will not have the liberty to define associations at our will. We need rules.

The below table shows how we can select the side of the relationship that should be made the owner of a bi-directional association. Remember that to make one entity as owner of the association, we must mark the other entity as being mapped by the owner entity.

Association Types
Options/Usage
Either end can be made by the owner, but one (and only one) of them should be; if we don’t specify this, we will end up with a circular dependency.
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.
Either end of the association can be made the owner.

If this all seems somewhat confusing, just remember that association ownership is exclusively concerned with managing 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 detail. They will help you in building the concept stronger.

If you have any questions, then drop me a comment below.

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 !!

9 thoughts on “Defining Hibernate Association Mappings”

  1. 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
  2. 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
  3. 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
  4. 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 related technologies, the best practices, algorithms, and interview questions.