Skip to content

Relation modeling

Using the Hibernate framework, we can indicate what the relations between tables in a relational database look like. In the standard relational model, tables are joined using foreign keys. In the object-oriented approach, relations between whole objects should be established. Hibernate allows you to define relations:

  • one-to-one using the @OneToOne annotation
  • one-to-many using the @OneToMany and@ManyToOne annotations
  • many-to-many with the @ManyToMany annotation

Related relations can be:

  • bidirectional - when both tied tables at class level are referenced to each other (i.e. class A points to class B and B points to A)
  • unidirectional - only one side of the relation references the table represented by another class (ie class A points to class B, but class B does not point to class A).

@OneToOne

Annotation @OneToOne indicates a relation of one-to-one tpye.

Unidirectional relation

The following example shows a relationship where a Student object can have at most oneStudentBook object:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity(name = "student_books")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentBook {
  @Id
  private Integer id;

  private String number;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import java.util.Date;

@Entity(name = "students")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {

  @Id
  private Integer id;

  private String firstName;

  private String lastName;

  private Date birthDate;

  @OneToOne
  private StudentBook studentBook;
}

At the database level, the students table additionally expects a student_book_id column, which is a reference to the relation value, i.e.:

one-to-one-uni

NOTE: For the purposes of the examples, the H2 database was used, the configuration of which means that table names are always defined with uppercase letters, although the entity names in the@ Entity annotations are lowercase.

Bidirectional relation

If we want to modify the relation from the previous example and add two-sidedness, we need to use the @ OneToOne annotation also on theStudentBook class, i.e.:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import java.util.Date;

@Entity(name = "students")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {

  @Id
  private Integer id;

  private String firstName;

  private String lastName;

  private Date birthDate;

  @OneToOne
  private StudentBook studentBook;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToOne;

@Entity(name = "student_books")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class StudentBook {
  @Id
  private Integer id;

  private String number;

  @OneToOne
  private Student student;
}

In this case also the student_books table will contain a reference to the value of theStudent object, i.e.:

one-to-one-bi

@JoinColumn

By default, Hibernate expects the column pointing to the "other side" of the relationship to be named name_identifier_field. This name can be changed with the annotation @ JoinColumn, e.g.:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import java.util.Date;

@Entity(name = "students")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {

  @Id
  private Integer id;

  private String firstName;

  private String lastName;

  private Date birthDate;

  @OneToOne
  @JoinColumn(name = "book_id")
  private StudentBook studentBook;
}

This allows us to use any column name at the database level, i.e. in this case book_id:

join_column

mappedBy

Each of the associations in question allows you to specify the mappedBy property. This parameter points to the field name in the class that is the owner of the relation (e.g. has a reference to the second table using foreign key).

The next example modifies the relationship between the entities defined in the previous examples - Student andStudentBook. This time the Student entity is the owner of the relationship. Note that:

  • the StudentBook class indicates the owner of the relationship with a field name
  • the owner is specified in the mappedBy attribute in the@ OneToOne annotation
  • at the database level, the StudentBook table should not contain theStudent entity ID in this case

mapped_by


Relations one-to-many/many-to-one

A one-to-many relationship can be defined with the @ OneToMany annotation. We use this annotation on the class field representing the collection of the dependent entity.

We can create a one-to-many relationship in two ways:

  • similar to many-to-many, with a link table
  • by adding an additional column indicating the owner of the relationship.

In practice, we rarely use a linking table to define such a relationship.

By default, Hibernate expects a link table when using the @ OneToMany annotation. The following example defines two entities - one represents a customer who can have multiple orders:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity(name = "orders")
public class Order {

  @Id
  private Integer id;

  private float price;
  private String name;
  private Date orderDate;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity(name = "clients")
public class Client {
  @Id
  private Integer id;

  private String firstName;
  private String lastName;

  @OneToMany
  private List<Order> orders;
}

In the joining table clients_orders, theclients_id column points to the id column of he entity Client and the orders_id column points to the id column of the order entity.

one_to_many_tl.

Example without the joining table

Most often, the more convenient way to define a one-to-many relationship is to use an extra column in the table that represents part of the "many" relationship. This effect can be achieved by adding the @ JoinColumn annotation next to the@ OneToMany annotation, e.g.:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity(name = "clients")
public class Client {
  @Id
  private Integer id;

  private String firstName;
  private String lastName;

  @OneToMany
  @JoinColumn(name = "client_id") // an indication of the name of the joining column in the orders table
  private List<Order> orders;
}

one_to_many_cl

Example - two-way relationship

The described relationship from the previous examples can also be two-way. This effect is achieved by adding the annotation @ManyToOne.

Using the @ OneToMany and@ ManyToOne annotations, without the @JoinColumn, Hibernate expects a table linking these two entities and an additional column pointing to the father of the relationship, i.e .:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity(name = "clients")
public class Client {
  @Id
  private Integer id;

  private String firstName;
  private String lastName;

  @OneToMany
  private List<Order> orders;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity(name = "orders")
public class Order {

  @Id
  private Integer id;

  private float price;
  private String name;
  private Date orderDate;

  @ManyToOne
  private Client client;
}

one_to_many_bi


As with the one-to-one relationship, it is also possible to specify the owner of the relationship with the mappedBy field. The value of the attribute points to the field name on the other side of the relationship, e.g .:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity(name = "clients")
public class Client {
  @Id
  private Integer id;

  private String firstName;
  private String lastName;

  @OneToMany(mappedBy = "client")
  private List<Order> orders;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity(name = "orders")
public class Order {

  @Id
  private Integer id;

  private float price;
  private String name;
  private Date orderDate;

  @ManyToOne
  private Client client;
}

one_to_many_mapped_by

@ManyToMany

Relational databases also provide the ability to create many-to-many relationships. With Hibernate, we can achieve this effect using the @ManyToMany annotation. Such relation:

  • always must have a linking table
  • can be one-sided or two-sided
  • can indicate the owner of a relationship with the mappedBy attribute

The following example uses the entities Orders and Client, but this time one order can be assigned to multiple clients. In addition, one customer may have multiple orders. The example uses the one-sided relation:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity(name = "orders")
public class Order {

  @Id
  private Integer id;

  private float price;
  private String name;
  private Date orderDate;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity(name = "clients")
public class Client {
  @Id
  private Integer id;

  private String firstName;
  private String lastName;

  @ManyToMany
  private List<Order> orders;
}

many_to_many_uni

To get information about customers in the Order entity, we must also use the@ManyToMany annotation:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity(name = "orders")
public class Order {

  @Id
  private Integer id;

  private float price;
  private String name;
  private Date orderDate;

  @ManyToMany(mappedBy = "orders")
  private List<Client> clients;
}