Entity Relationship On Hibernate JPA

This article is tested against Java EE 6, Hibernate 4.2.2.

One To Many Unidirectional Relationship

Unidirectional here means you can only obtain related entities from one side. For example supposed we have one to many Customer --> Account relationship, here’s what Customer entity look like

@Entity
public class Customer {

  @Id
  @GeneratedValue
  private long id;

  private String name;

  @OneToMany
  @JoinColumn(name = "customer_id")
  private Set<Account> accounts;

  // getters & setters..
}

Instead of Set, you can use List, however if you have more than one relationship in your entity class, you will get MultipleBagFetchException.

And This is what Account looks like. Notice there’s no reference to Customer object from Account.

@Entity
public class Account {

  @Id
  @GeneratedValue
  private long id;
  
  private double balance;

  // Getters & Setters ..
}

One To Many Bidirectional Relationship

Similar to unidirectional except you can obtain relation from both direction. This is what Customer and Account look like:

@Entity
public class Customer {

  @Id
  @GeneratedValue
  private long id;

  private String name;

  @OneToMany(mappedBy = "customer")
  private Set<Account> accounts;

  // getters & setters..
}

The “customer” on @OneToMany(mappedBy = "customer") refers to customer java property on Account entity.

@Entity
public class Account {

  @Id
  @GeneratedValue
  private long id;
  
  private double balance;

  @ManyToOne
  @JoinColumn(name = "customer_id")
  private Customer customer;

  // Getters & Setters ..
}

The @JoinColumn(name = "customer_id") annotation sets the database foreign key column name to be “customer_id”.

Lazy / Eager Loading

Lazy / Eager refers to when does entity relationship get populated. Eager means it will get populated as soon as the entity is obtained from database, where in Lazy it’s only populated when you request the relationship for the first time.

The default value for @OneToMany is LAZY, but for @ManyToOne it’s EAGER. It’s a good practice to explicitly configure it so your code is easier to understand

@Entity
public class Customer {
  ...
  @OneToMany(mappedBy = "customer", fetch = FetchType.LAZY)
  private Set<Account> accounts;
  ...
}
@Entity
public class Account {
  ...
  @ManyToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "customer_id")
  private Customer customer;
  ...
}

LazyInitializationException: could not initialize proxy – no Session

Almost every Hibernate/JPA noobie struggle with this exception when they started learning (including myself). This is basically saying you’re tring to access unfetched data after transaction has been closed.

If you use Spring declarative transaction management, transaction start and finish mark are declared by simply annotating DAO method with @Transactional. Let’s see a common failure where LazyInitializationException can happen.

Here’s your CustomerDAO class:

@Repository
public class CustomerDAO {
  
  @PersistenceContext private EntityManager em;

  @Transactional
  public List<Customer> getAllCustomers() {
    List<Customer> customers = em.createQuery("select c from Customer", 
      Customer.class).getResultList();
    return customers;
  }

Then on your controller you populate your Model object with list of Customers, and on your view your JSP tried to access Accounts associated with each customer:

<h1>Customer Info</h1>
Id: ${customer.id}
Name: ${customer.name}
Accounts: <ul>
  <c:forEach var="acc" items="${customer.accounts}">
    <li>Acc Id: ${acc.id}, Balance: ${acc.balance}</li>
  </c:forEach>
</ul>

Since the transaction is already closed when CustomerDAO.getAllCustomers() method returns, this will throw LazyInitializationException.

My preferred strategy to overcome this problem is to prefetch the related entities on the DAO, so all the information we need is available on view level in detached state. Other strategy being opening a transaction / session while view is being generated (see: OpenSessionInViewFilter). To achieve this, change the getAllCustomers() DAO method to use JPQL left join fetch syntax like this:

@Repository
public class CustomerDAO {
  ...
  @Transactional
  public List<Customer> getAllCustomers() {
    List<Customer> customers = em.createQuery(
      "select distinct c from Customer " +
      "left join fetch c.accounts", 
      Customer.class).getResultList();
    return customers;
  }
  ...
}

Now you can iterate and access all related accounts entities without exceptions.

Advertisements

One thought on “Entity Relationship On Hibernate JPA

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s