Hibernate Logo

Hibernate is a widely-used ORM framework. Many organizations use it across their projects in order to manage their data access tier. Yet, many developers working on Hibernate don’t fully understand the full measure of its features. In this serie of articles, I will try to dispel some common misunderstandings regarding Hibernate.

Part 1 : Updating a persistent object

Directional associations

Association is a relationship between two classes that denotes a connection between the two. Navigability is the way by which an instance of one class can access the instance of the second one. UML describes the following navigabilities:

  • undirectional navigability, from class A to class B
  • bidirectional navigability, from class A to class B and from the latter to the former
  • unknown navigability (early in the design)

When talking about persistence, associations are designed from (or to, depending on what comes first) foreign key constraints. If I want to describe that a customer can have multiple orders, chances are the Order table has a foreign key constraint on the Customer table. On the other hand, there’s no such thing as directionality in the relational world. This means that the designed directionality is a feature brought to you by the object world, and the mapping tool used.

In the relational world, we speak of a relationship’s ownership to identify the where the data used by the join resides. In the previous example, the Order table is the owner since it has the foreign key constraint on the Customer table. This ownership has no relation to navigability whatsoever.

In the case of a bidirectional association, one would think that creating the association from A to B would be enough. For example, in a one-to-many association, having a customer, if I create an account, I just have to add this account to my customer’s, haven’t I? Unfortunately, this is not the case. If I forget to associate both ends, I end up catching very nasty exceptions. Interestingly enough, they are different depending on which association you miss.

It is thus a good practice to create an addOrder() method on the Customer class to manage such subtleties:

/**
 * Adds an order to this customer's. Manage bidirectional associations.
 *
 * @param order
 */
public void addOrder(Order order) {
    getOrders().add(order);
    order.setCustomer(this);
}

Whether you use this tip or not, remember to document its use (or not) in the Javadoc, since if you don’t, client code will need to.

Now if one wants to remove an order from the database, one should normally call the session.delete() method. Like above, this will throw an exception if one didn’t previously remove this order from this customer’s. This will be slightly more hindering since it cannot be factorized in the domain class.

// We got the session somehow and the snippet is running in a transaction
Order order = (Order) session.load(Order.class, 1L);
Customer customer = order.getCustomer();
customer.getOrders().remove(order);
session.delete(order);
session.getTransaction().commit();

We’ve seen previously a simple many-to-one association creation. This gets even worse in the case of a many-to-many association update, since updating means removing and creating.

You can do it in a method, provided getters return the real collection and not an unmodifiable (a usually good practice).

// In the Customer class
public void removeOrder(Order order) {
    getOrders().remove(order);
    order.getCustomers().remove(this);
}

// In the Order class
public void removeCustomer(Customer customer) {
    getCustomers().remove(customer);
    customer.getOrders().remove(this);
}

You will find the test cases here</a>.