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.:
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.:
@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
:
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
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.
Example with link table¶
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.
.
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;
}
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;
}
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;
}
@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;
}
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;
}