JPA 2.0 - Coding entity relationships


JPA Mappings


Assumptions

  1. You have an understanding of JPA
  2. You know what are entities and how to write entity classes.
  3. You understand annotations like @Table, @Column, @Id etc.
  4. You understand the database terminology like one-to-one, one-to-many, many-to-one, many-to-many.
  5. You understand the database terminology of unidirectional and bidirectional mappings.

Agenda

  1. Define a problem statement
  2. Understand the data model solution
  3. Explanations
    1. One to One
    2. One to Many
    3. Many to One
    4. Many to Many

Problem Statement

We have a vehicle lease application for the employees of a company. Transport department gives these vehicles for use to its employees. An employee can have any number of vehicles with him at a given time. He may also share vehicles with other employees at same time. Transport department tracks each vehicle based on its registration number. It also needs to maintain some information about the vehicle's manufacturer and model. (employees may wish to take specific vehicles on lease).
So the database must be able to maintain following information
  • Information about vehicle registration
  • Information about vehicle make and model
  • Information about employees
  • Linkages between vehicle-employee, vehicle - owner - registration, vehicle-model and make.

Data model Solution

The solution that I propose is as below.

I know that the registration and vehicle table should be merged into 1 table, but then for 1:1 mapping example, let us assume the above diagram as correct.

So our database tables develop as below

Many to One

Table Vehicle and table Make are in many to one relationship. This means many vehicles can have the same make i.e. same manufacturer, same model and same date of manufacture (hypothetical).
In database, many side will keep an id of the one side. So Vehicle table keeps a foreign key to table Make. In our Vehicle table, this key is 'make'.
In JPA, in the Vehicle entity, we need to create an instance of Make entity. This is because Make entity is a foreign key in Table entity. We need to annotate Make entity reference in Vehicle entity as @ManyToOne i.e. many vehicles are related to one make. We also need to inform Vehicle entity that the Make entity is linked through the 'make' column in 'Vehicle' table and 'id' column in 'Make' table.
So the Vehicle entity now looks like

@Entity
@Table( name = "Vehicle" )
public class Vehicle
{
    @Id
    @Column( name = "id" )
    @GeneratedValue( strategy = GenerationType.AUTO )
    private int id;

    @ManyToOne
    @JoinColumn ( name = "make", referencedColumnName = "id" )
    private Make make;
}

The JoinColumn annotation's name refers to the name of column in Vehicle table - which is 'make'. And the referencedColumn refers to the column in Make table - which is 'id'.
But the Make entity gets no special annotation if this relationship is unidirectional.
If it were a bi-directional relationship, then we would create a list of Vehicle entities in Make entity. And in the annotation, we would say that the Make entity is mapped by 'make' column in Vehicle entity. i.e. 'make' is the foreign key column for Make table in Vehicle table. This would be done as

@Entity
@Table( name = "Make" )
public class Make
{
    @Id
    @Column( name = "id" )
    @GeneratedValue( strategy = GenerationType.AUTO )
    private int id;

    @OneToMany( mappedBy = "make" ) 
    // 'make' is the variable name for Make entity in Vehicle entity.
    private List vehicles;
}

Note: This is for bi-directional Many-to-One mapping.

One to Many

If you think carefully, a one-to-many is reverse of many-to-one.
So why not use it that way?

One to One

Table Vehicle and table Registration are in One-to-One mapping. This means one vehicle has only one registration and vice versa.
In database, the owing side of relationship will have a foreign key for the owned side. In our case, vehicle owns a registration. So registration table's id is available in vehicle table as a foreign key.
In JPA, the entity Vehicle will have an instance of Registration entity. And just like in Many-to-One, we will use a JoinColumn annotation to refer to the foreign keys. And the Registration entity will not have any linkage to Vehicle. It is a uni-directional relationship for us.

@Entity
@Table( name = "Vehicle" )
public class Vehicle
{
    @Id
    @Column( name = "id" )
    @GeneratedValue( strategy = GenerationType.AUTO )
    private int id;

    @ManyToOne
    @JoinColumn ( name = "make", referencedColumnName = "id" )
    private Make make;

    @OneToOne
    @JoinColumn ( name = "registration", referencedColumnName = "id")
    private Registration registration;
}

Many to Many

Table vehicle and table user exist in many-to-many relationship. This means a user can lease many vehicles and a vehicle can be leased out to many users at once.
In database, a many-to-many relationship is implemented using a join table. So we have a table called User_vehicle. This join table has only foreign keys to both user table and vehicle table.
Let us assume that User is the owning side of the relationship.
In JPA, User entity has a list of Vehicle entities. But in the mapping description, it will have to tell that User_vehicle is the join table. I will also need to tell that it is represented by 'user' column in User_vehicle table. I will also tell that 'vehicle' column in User_vehicle table denotes Vehicle entity.

@Table(name = "user")
@Entity
public class User
{

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    @Column(name = "name")
    private String name;

    @Column(name = "address")
    private String address;

    @ManyToMany
    @JoinTable(name = "user_vehicle",
            joinColumns = @JoinColumn(name = "user", referencedColumnName = "id"),
            inverseJoinColumns = 
                    @JoinColumn(name = "vehicle", referencedColumnName = "id"))
    private List vehicles;
}


But this is a uni-directional relationship. We can have the relationship from Vehicle side too. And this is simple. Since User has already mentioned all the information about the join, about the tables and columns, the vehicle table does not need to give all this information. It just will need to inform that in User entity, it is available by name vehicles. So now the Vehicle table is modified as

@Entity
@Table(name = "vehicle")
public class Vehicle
{
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    @ManyToOne
    @JoinColumn(name = "make", referencedColumnName = "id")
    private Make make;

    @OneToOne
    @JoinColumn(name = "registration", referencedColumnName = "id")
    Registration registration;

    @ManyToMany(mappedBy = "vehicles")
    List users;
}


This completes our explanation on entity relationships.
 

If you have any doubt or you see any mistake, please feel free to let me know at harsh.curse@gmail.com

Comments