There are several ways to map One To One relationship in JPA and Hibernate by using @OneToOne annotation including
Foreign key unidirectional and bidirectional mapping with @JoinColumn
Shared primary key unidirectional and bidirectional mapping with @MapsId and @PrimaryKeyJoinColumn
This guide will show you how to map along with the pros and cons of each approach
Consider the relationship between a person and an id card. One person has only one id card, and one id card only belongs to one person
Foreign key bidirectional mapping with @JoinColumn and @OneToOne
@OneToOne would be placed on both entities of the relationship, the mappedBy attribute value points to the relationship owner which has a foreign key column in the underlying table
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
@OneToOne(cascade = CascadeType.ALL, optional = false)
@JoinColumn(name = "id_card_id")
private IDCard idCard;
...
}
@Entity
public class IDCard {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(unique = true, nullable = false)
private String code = UUID.randomUUID().toString();
@OneToOne(mappedBy = "idCard")
private Person person;
...
}
@JoinColumn specifies the foreign key column. It has the default name to the underscore string join of the association field name and its primary key column name
The mappedBy
attribute on @OneToOne is required to specify for bidirectional mapping. If mappedBy
is absent, JPA and Hibernate will create a redundant foreign key column pointed back to the relationship owner
mysql> describe idcard;
+-----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| code | varchar(255) | NO | UNI | NULL | |
| person_id | int(11) | NO | UNI | NULL | |
+-----------+--------------+------+-----+---------+----------------+
Pros and cons
Foreign key mapping is more native and easy to understand than shared primary key mapping
The parent entity can quickly access and cascade CRUD operations to the child association
Hands-on tutorials
Foreign key unidirectional mapping with @JoinColumn and @OneToOne
@OneToOne would be placed on only 1 entity of the one-to-one relationship. We would not need to specify the mappedBy attribute
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
@OneToOne(cascade = CascadeType.ALL, optional = false)
@JoinColumn(name = "id_card_id")
private IDCard idCard;
...
}
@Entity
public class IDCard {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(unique = true, nullable = false)
private String code = UUID.randomUUID().toString();
...
}
@JoinColumn specifies the foreign key column. It has the default name to the underscore string join of the association field name and its primary key column name
Pros and cons
Foreign key mapping is more native and easy to understand than shared primary key mapping
Foreign key unidirectional mapping is less verbose than the bidirectional mapping
Shared primary key bidirectional mapping with @MapsId and @OneToOne
The same primary key would be used by both entities of the relationship. In the child entity, the shared primary key is also the foreign key
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@OneToOne(cascade = CascadeType.ALL, mappedBy = "person")
private IDCard idCard;
...
}
@Entity
public class IDCard implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@OneToOne(cascade = CascadeType.ALL, optional = false)
@JoinColumn(name = "person_id")
@MapsId
private Person person;
...
}
@OneToOne is marked on both entities. On the child entity, @MapsId is marked on the same association with @OneToOne
@JoinColumn specifies the foreign key column. It has the default name to the underscore string join of the association field name and its primary key column name
Pros and cons
The parent entity can quickly access and cascade CRUD operations to the child association
Foreign key mapping would be more native and easy to understand than shared primary key mapping
Hands-on tutorials
Shared primary key unidirectional mapping with @PrimaryKeyJoinColumn and @OneToOne
The same primary key would be used by both entities of the relationship. In the child entity, the shared primary key is also the foreign key
@Getter @Setter
@Entity
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
...
}
@Entity
public class IDCard {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int personId;
@OneToOne(cascade = CascadeType.ALL, optional = false)
@PrimaryKeyJoinColumn(name = "person_id", referencedColumnName = "id")
private Person person;
@Column(unique = true, nullable = false)
private String code = UUID.randomUUID().toString();
...
}
@OneToOne is only marked on child entity along with @PrimaryKeyJoinColumn
Pros and cons
- The parent entity cannot access or cascade CRUD operations to the child association
Hands-on tutorials
In this article, we had a quick look at various ways to map One to One relationship in JPA and Hibernate along with the notes on their pros and cons