Posts Tagged ‘orm’

Bidirectional relationships – owning and inverse sides

I used to have a hard time remembering which side was which in a bidirectional relationship using ORM. I was confused because the owning side was not the side I thought it was when thinking about a relationship. Let’s say we have a Customer that has many Orders. The English language semantics suggest to me that the Customer side of the relationship is the owning side. It “has many” Orders, right?

But every ORM framework will tell you that the “many” side of the relationship should be the owning side and the “one” side is the inverse. Why? Let’s look at some Java class definitions using JPA.

@Entity
public class Customer implements Serializable {
	@Id
	public String id;

	public String name;
	public String emailAddress;

	@OneToMany
	List<Order> order = new ArrayList<Order>();

	@Version
	public int version;

}

A Customer has a one-to-many relationship with Order.

@Entity
@Table(name="orders")
public class Order implements Serializable {
	@Id
	public String id;

	@ManyToOne
	Customer customer;

}

And the Order references the customer to which it belongs. Here is our bi-directional relationship. But we don’t know why the Order is the owning-side of the relationship yet. If we were to create a database schema based on these classes we would get three tables:

mysql> describe Customer;
+--------------+--------------+------+-----+---------+-------+
| Field        | Type         | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+-------+
| id           | varchar(255) | NO   | PRI | NULL    |       |
| emailAddress | varchar(255) | YES  |     | NULL    |       |
| name         | varchar(255) | YES  |     | NULL    |       |
| version      | int(11)      | YES  |     | NULL    |       |
+--------------+--------------+------+-----+---------+-------+

mysql> describe customer_orders;
+-------------+--------------+------+-----+---------+-------+
| Field       | Type         | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+-------+
| CUSTOMER_ID | varchar(255) | YES  | MUL | NULL    |       |
| ORDER_ID    | varchar(255) | YES  | MUL | NULL    |       |
+-------------+--------------+------+-----+---------+-------+

mysql> describe orders;
+-------------+--------------+------+-----+---------+-------+
| Field       | Type         | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+-------+
| id          | varchar(255) | NO   | PRI | NULL    |       |
| CUSTOMER_ID | varchar(255) | YES  | MUL | NULL    |       |
+-------------+--------------+------+-----+---------+-------+

The tables that map to our entities look fine. We also get a join table to map from Customer to Order. Even though the Order class can refer directly to its Customer the Customer must look at the join table to get to its Orders.

The SQL query generated by the ORM to get the Orders associated with a customer would join the customer_orders table to get a list of order ids associated with that customer, then join the orders table on those order ids. There is a more direct way we can get there, especially since the orders table already references its customer id. We need to indicate to the JPA runtime that the Customer can reference a column on the orders table. We do that with the mappedBy attribute in @OneToMany.

@Entity
public class Customer implements Serializable {
	@Id
	public String id;

	public String name;
	public String emailAddress;

	@OneToMany(mappedBy="customer")
	List<Order> order = new ArrayList<Order>();

	@Version
	public int version;

}

When we add the mappedBy attribute on the @OneToMany annotation the JPA runtime knows that there is a field in the related object named customer. The column this field maps to is the appropriate foreign key to use to find associated Orders. Creating the database schema with this change to the Customer class @OneToMany annotation we get only two tables.

mysql> describe Customer;
+--------------+--------------+------+-----+---------+-------+
| Field        | Type         | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+-------+
| id           | varchar(255) | NO   | PRI | NULL    |       |
| emailAddress | varchar(255) | YES  |     | NULL    |       |
| name         | varchar(255) | YES  |     | NULL    |       |
| version      | int(11)      | YES  |     | NULL    |       |
+--------------+--------------+------+-----+---------+-------+
4 rows in set (0.01 sec)

mysql> describe orders;
+-------------+--------------+------+-----+---------+-------+
| Field       | Type         | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+-------+
| id          | varchar(255) | NO   | PRI | NULL    |       |
| CUSTOMER_ID | varchar(255) | YES  | MUL | NULL    |       |
+-------------+--------------+------+-----+---------+-------+
2 rows in set (0.01 sec)

Now the JPA runtime knows that the customer_id column on the orders table can map between an Order and a Customer. The owning-side of a bidirectional relationship is the one with the foreign key column on the table – the orders table. The orders table owns the relationship because it is responsible for the column that maps between Customer and Order. The “owning-side” is not a statement about a Customer has many Orders, the logical relationship between entities. It is a statement about which underlying table is responsible for the mapping data between two tables and the entities mapped by those tables.

I was just reminded of my initial questions about why we use these terms when I wrote a @OneToMany(mappedBy=”…”) a few days ago. As always I’m open to comments.