About Me

My Photo
A Sun certified Java professional with time proven experience in architecture and designing of mid/large scale Java/JEE based applications. Creator of the EasyTest Framework.A lot of experience with technologies such as Spring framework, Hibernate , JPA, Java4-6, REST, SOA , YUI , JUnit , Cloud Foundry PaaS and other technologies.

Tuesday, May 28, 2013

JPA 2 | Understanding relationships between entities

Entities in Object Relational Model are the objects that contain/maintain state for the application. An application can have relationships between its objects. For example, an author can have one or many books to his name. Or an employee can work for one or more projects. We find relationships everywhere. For a relationship, we need two entities that can be related to each other, just like in a marriage. Entities need to have a relationship with other entities to become meaningful.

In today's blog, we will look at how entity relationships are defined in JPA 2. The ideas and most of the contents on this blog are inspired from Pro JPA 2 : Mastering the Java Persistence API .

A relationship normally has the following attributes :

  • Role : In every relationship there are two entities that are related to one another, and each entity is said to play a role in the relationship.
  • Direction : Relationships can be unidirectional or bidirectional. For e.g.. a Person has an address is normally unidirectional whereas Employee working on a project is normally bidirectional. We will look at how to identify and define directionality while coming up with a Data Model.
  • Cardinality : Cardinality defines the number of entities that exists on each side of the relationship. For e.g. a Person could have written many books. In such a case the cardinality on the source side of the relationship (the side that is used to traverse the relationship) is one where as the cardinality on the destination side of the relationship is "many". We will see later in the post that cardinality is what drives the relationship to a great extent.
  • Ordinality : Ordinality determines whether the target entity needs to be specified when a source entity is created. Ordinality thus boils down to Yes or No, True or False, 0 or 1.

Mappings in JPA 2

Mappings or associations in JPA 2 are divided into two broader groups :

  • Single Valued Mappings -> A mapping from source entity to target entity where the cardinality of the target entity is "one". OneToOne and ManyToOne  are examples of Single Valued mappings.
  • Collection valued Mappings -> A mapping from source entity to destination entity where the cardinality of the target entity is more than one. OneToMany and ManyToMany are examples of Collection Valued mappings.

Single Valued Mappings

In a single valued entity, the source entity refers to at most one target entity. JPA uses two annotations to define the Single Valued Mappings : @ManyToOne and @OneToOne. We will understand each one of them in turn.

@ManyToOne Mapping

A Many to one mapping occurs when many entities on the source side of the relationship( the side from which the relationship is traversed ) are related to a single entity on the target side of the relationship. For e.g. Many Employees can be associated with a single department. Thus Employee is the "many" from ManyToOne annotation and Department is the "one" from the same annotation. Here is the example figure straight from the Pro JPA 2 Book:







The important thing to notice in the above figure is that the arrow head points from Employee to Department and not the other way round. This type of relationship is called Unidirectional Relationship. 
A Unidirectional many to one relationships in JPA2 is defined by simply annotating the attribute in the Source Entity that refers to the target entity with @ManyToOne annotation.   The example below shows the Employee Entity that has a Unidirectional ManyToOne association with the Department Entity.


@Entity
public class Employee {
    
    @ManyToOne
    private Department department;
    // other attributes defined.....
}

@JoinColumn


A Join Column in JPA is a column in the owner entity that refers to a key (usually a primary key) in the non-owner or inverse entity. The first thing that comes to mind after reading the above line is that JoinColumns are nothing but a Foreign Key Columns. And it is indeed the case. JPA calls them Join Columns, possibly because they are more verbose in what their actual role is, to join the two entities using a common column.

Another thing to notice above is that we have used the terms owner and non-owner entities. For easy rememberability, the entity that has a join column is always the owning entity.



In the above example, the owner Entity Employee, has a Join Column named DEPT_ID that has a foreign key to the non-owner Department entity.


ManyToOne and JoinColumns

ManyToOne mappings are always defined on the owning entity. Thus for the above figure the Entity Class would look like this :

@Entity
public class Employee {
    @Id private int id;
    @ManyToOne
    @JoinColumn(name="DEPT_ID")
    private Department department;
    // ... 
}  
Note that if no @JoinColumn is defined along with the @ManyToOne mapping, then a default name is assumed. The default name is composed of the name of the relationship attribute in the source entity (which is department in our case) + an underscore + the name of the primary key on the target entity (which in our case is id) . Thus the default name would be department_id (assuming case insensitiveness).

OneToOne Mappings

A person can have only one full time job (in normal scenarios). In such a case the relationship between a person and his job is a OneToOne relationship. Thus we will define the one to one mapping between Person and his Job as follows :
@Entity
public class Person {
    @Id private int id;
    @OneToOne
    @JoinColumn(name="JOB_ID")
    private Job job;
    // ... 
} 
Notice how similar the mapping is to the previous ManyToOne mapping. The attribute job has a OneToOne mapping annotation as well as the JoinColumn annotation that identifies the name of the column that maps to the key in the target column.
In OneToOne mapping one instance of source entity can refer to only one instance of target entity.

Bidirectional OneToOne Mapping
The above example depicts a unidirectional OneToOne mapping. There are cases when the relationship is bidirectional. For e.g. in the above case the target entity Job also can have an association to the Person whom this job belongs to. In such a case, it is a bidirectional OntToOne relationship. We know that the owner always have a JoinColumn annotation. In the case of OneToOne mapping, any entity can be the owner. The other non-owner entity then has to provide the mappedBy attribute value to indicate that it is not the owner of the relationship. 

@Entity public class Job { @Id private int id; private String designation; private String location; @OneToOne(mappedBy="job") private Person person; // ... }
In the above example, the mappedBy attribute refers to the attribute name job, defined in the source entity Person.

Collection Valued Mappings

When a source entity(or a collection of source entities) refers to a collection of target entities, we use collection valued mappings. The two collection valued mapping annotations in JPA are OneToMany and ManyToMany.

OneToMany mapping

When a source entity is associated with a collection of target entities, the relationship defined is a one to many relationship. For example, while reading an ebook you can define multiple bookmarks. The relationship between a Book and its associated Bookmarks is a One To Many relationship.



@Entity
public class Book {
    @Id private int id;
    @OneToMany
    @JoinColumn(name="BOOK_ID") // BOOK_ID exists in the BookMark table
    private List bookMarks;
    // ... 
}

The above example depicts a Unidirectional OneToMany relationship between Book and its bookmarks.

Bidirectional OneToMany Mapping
When the relationship is bidirectional, there are normally two associations defined : one from source to target entity(having the JoinColumn annotation) , and the other from target to source entity(having the mapped by attribute ). OneToMany bidirectional mapping always implies a ManyToOne mapping back to the source. Another important thing to remember from the previous discussion about ManyToOne mapping is that if theres is a ManyToOne mapping, then the JoinColumn annotation will always be defined on the ManyToOne mapping attribute. Thus in case of Bidirectional OneToMany mapping, the JoinColumn will exists on the entity having ManyToOne annotation. 


@Entity
public class Book {
    @Id private int id;
    @OneToMany(mappedBy="book")
    private List jobs;
    // ... 
}


@Entity
public class Job {
    @Id private int jobId;
    @ManyToOne
    @JoinColumn(name="book_id")
    private Book book;
    // ... 
}

ManyToMany Mapping
When a source entity is associated with multiple target entities and the same target entity is also associated with multiple source entities, then there exists a ManyToMany mapping between source and target entity. For example, a Person can have multiple club memberships and each club can also have multiple members. In this case the association between a Person and Club entity is ManyToMany.

Although a Unidirectional ManyToMany does not really have any big takers (if at all), it is still possible to define it. Lets see an example.


@Entity
public class Person {
    @Id private int id;
    @ManyToMany
    private List memberOf;
    // ... 
}


@Entity
public class Club {
    @Id private int id;
    
    private String name;
    // ... 
}

As we can see, the Club entity does not have any ManyToMany mapping to the Person Entity.


Bidirectional ManyToMany Mapping


In the real world, there would exist a bidirectional ManyToMany mapping. And as we know in a bidirectional mapping, there should be a way to identify the source and target entity.
We do that by defining the mappedBy attribute of the relationship on the target entity. Note that since the multiplicity of both the sides of the relationship is plural, it is not possible(or feasible/correct) to store an iunlimited set of foreign key values in a single entity row. We should use the third table to associate the two entities. This third table is called the JoinTable. A Join Table is a requirement for a ManyToMany relationship. A JoinTable consists of the Primary Keys from both the entities and nothing else. Here is an example of ManyToMany relationship from Pro JPA2 book.



In the above example, the Employee and Project table are two entities related with ManyToMany relationship. EMP_PROJ table is the JoinTable that is used to store the entity reference for each of the table.
In JPA, we use the @JoinTable annotation to define a Join Table for the Many to Many relationship. Here is an example


@Entity
public class Employee {
    @Id private int id;
    private String name;
    @ManyToMany
    @JoinTable(name="EMP_PROJ",
          joinColumns=@JoinColumn(name="EMP_ID"),
          inverseJoinColumns=@JoinColumn(name="PROJ_ID"))
    private Collection projects;
    // ...
}

The JoinTable annotation has a name attribute which corresponds to the name of the Join Table. It also has two other attributes :

  • joinColumns which is the column name of the owning Entity
  • inverseJoinColumns which is the column name of the inverse or non owning Entity. 

Summary
The blog post above talks about Entity relationships and the JPA specifications to handle relationships in a consistent manner. Almost the entire stuff was inspired from chapter 4 section 6 of the Pro JPA 2: Mastering the Java Persistence API. I hope after reading this, you should be able to correctly define and manage JPA entities in your project.


3 comments:

Anonymous said...

There are some exception. Please fix them.

1. Picture ManyToMany.png has got
relation project<->project instead
Project<->Employee
2. >> The relationship between a Book >> and its associated Bookmarks is a >> One To Many relationship.
But sample code is not about Bookmarks.

Anuj said...

Thanks for pointing out the mismatch. I have fixed the 2nd point in your comment and will fix the 1st one soon.

j00ff said...

I believe there shouldn't be @JoinColumn(name="BOOK_ID") annotation above the
List bookMarks under OneToMany section (unidirectional relation). From what I've read before the @JoinColumn "means" foreign key but the Book entity doesn't have one