Home > Java > Hibernate hard facts part 2

Hibernate hard facts part 2

Hibernate LogoHibernate 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.

email
Send to Kindle
Categories: Java Tags: , ,
  1. Null
    February 18th, 2010 at 15:00 | #1

    Nice writeup. I would love to see more detail explaining when to use bidirectional associations and when to stick to unidirectional. Thanks for your efforts!

  2. Alex
    March 3rd, 2010 at 18:41 | #2

    To avoid those errors you can use cascade on your associations.

  3. March 3rd, 2010 at 20:21 | #3

    Cascade doesn’t spare you the toil to associate your objects with each other. However, it let you save (or whatever) your master object without previously creating the dependent object.

  4. Abdellah
    May 27th, 2011 at 19:14 | #4

    Thanks for this good article.
    Should I understand that you recommand always using bidirectional associations ?

  5. May 27th, 2011 at 20:25 | #5

    Not at all! Use bidirectional associations only when you need it; just know there are some requirements.

  1. No trackbacks yet.