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 TypesOptions/Usage
One-to-oneEither 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.
One-to-manyThe many end must be made the owner of the association.
Many-to-oneThis 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-manyEither 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 !!

Leave a Reply

9 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions, and frequently asked interview questions.