Java – “the inverse side of the association” in a bidirectional JPA OneToMany/ManyToOne association

hibernatejavajpamappedbyone-to-many

In the example section of the @OneToMany JPA annotation reference:

Example 1-59 @OneToMany – Customer Class With Generics

@Entity
public class Customer implements Serializable {
    ...
    @OneToMany(cascade=ALL, mappedBy="customer")
    public Set<Order> getOrders() { 
        return orders; 
    }
    ...
}

Example 1-60 @ManyToOne – Order Class With Generics

@Entity
public class Order implements Serializable {
    ...
    @ManyToOne
    @JoinColumn(name="CUST_ID", nullable=false)
    public Customer getCustomer() { 
        return customer; 
    }
    ...
}

It seems to me that the Customer entity is the owner of the association. However, in the explanation for the mappedBy attribute in the same document, it is written that:

if the relationship is bidirectional,
then set the mappedBy element on the
inverse (non-owning) side of the
association to the name of the field
or property that owns the relationship
as Example 1-60 shows.

However, if I am not wrong, it looks like in the example, the mappedBy is actually specified on the owning side of the association, rather than the non-owning side.

So my question is basically:

  1. In a bidirectional (one-to-many/many-to-one) association, which of the entities is the owner? How can we designate the One side as the owner? How can we designate the Many side as the owner?

  2. What is meant by "the inverse side of the association"? How can we designate the One side as the inverse? How can we designate the Many side as the inverse?

Best Answer

To understand this, you must take a step back. In OO, the customer owns the orders (orders are a list in the customer object). There can't be an order without a customer. So the customer seems to be the owner of the orders.

But in the SQL world, one item will actually contain a pointer to the other. Since there is 1 customer for N orders, each order contains a foreign key to the customer it belongs to. This is the "connection" and this means the order "owns" (or literally contains) the connection (information). This is exactly the opposite from the OO/model world.

This may help to understand:

public class Customer {
     // This field doesn't exist in the database
     // It is simulated with a SQL query
     // "OO speak": Customer owns the orders
     private List<Order> orders;
}

public class Order {
     // This field actually exists in the DB
     // In a purely OO model, we could omit it
     // "DB speak": Order contains a foreign key to customer
     private Customer customer;
}

The inverse side is the OO "owner" of the object, in this case the customer. The customer has no columns in the table to store the orders, so you must tell it where in the order table it can save this data (which happens via mappedBy).

Another common example are trees with nodes which can be both parents and children. In this case, the two fields are used in one class:

public class Node {
    // Again, this is managed by Hibernate.
    // There is no matching column in the database.
    @OneToMany(cascade = CascadeType.ALL) // mappedBy is only necessary when there are two fields with the type "Node"
    private List<Node> children;

    // This field exists in the database.
    // For the OO model, it's not really necessary and in fact
    // some XML implementations omit it to save memory.
    // Of course, that limits your options to navigate the tree.
    @ManyToOne
    private Node parent;
}

This explains for the "foreign key" many-to-one design works. There is a second approach which uses another table to maintain the relations. That means, for our first example, you have three tables: The one with customers, the one with orders and a two-column table with pairs of primary keys (customerPK, orderPK).

This approach is more flexible than the one above (it can easily handle one-to-one, many-to-one, one-to-many and even many-to-many). The price is that

  • it's a bit slower (having to maintain another table and joins uses three tables instead of just two),
  • the join syntax is more complex (which can be tedious if you have to manually write many queries, for example when you try to debug something)
  • it's more error prone because you can suddenly get too many or too few results when something goes wrong in the code which manages the connection table.

That's why I rarely recommend this approach.