Feb 17, 2012
Hibernate One-to-One Bi-Directional Mapping
In the following article, I would like to show an approach how to map one-to-one bi-directionall two entities in Hibernate.
First of all, let’s take a look at the relation between two tables in a database:

As you can see, there is the one-to-one relation between user and user_profile tables and the user_profile table has a primary key that in fact, it is a foreign key to the id property of the user table. In other words, there is a simple use-case: one user record can have only one related user_profile record.
What the mapping looks like?
There are two classes. The first, parent, class is User:
package com.scriptico.domain.entities;
import java.io.Serializable;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;
@Entity
@Table(name = "user")
public final class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
@Column(name = "id", unique = true)
private String id;
@OneToOne(mappedBy = "user")
private UserProfile profile;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public UserProfile getProfile() {
return profile;
}
public void setProfile(UserProfile profile) {
this.profile = profile;
}
}
The second, child, class is UserProfile:
package com.scriptico.domain.entities;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;
@Entity
@Table(name = "user_profile")
public final class UserProfile implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(generator = "foreign")
@GenericGenerator(
name = "foreign",
strategy = "foreign",
parameters = {@org.hibernate.annotations.Parameter(name = "property", value = "user")})
@Column(name = "user_id")
private String userId;
@Column(name = "first_name", nullable = true, length = 50)
private String firstName;
@Column(name = "last_name", nullable = true, length = 50)
private String lastName;
@Column(name = "display_name", nullable = true, length = 50)
private String displayName;
@OneToOne
@PrimaryKeyJoinColumn(name = "user_id", referencedColumnName = "id")
private User user;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Now few words about the code above. The parent class has a mapped UserProfile child class and as you can see, I use the following annotation:
@OneToOne(mappedBy = "user")
the mappedBy property defines the field in the child class that owns the relationship and in our case, it is the user field in the UserProfile class.
Now, a few cents about the UserProfile class. First of all, I did a trick to define the primary key. The UserProfile class must have an identifier and as a result, the userId property is marked by @Id annotation. If the class has no identifier, Hibernate will throw the following exception:
org.hibernate.AnnotationException: No identifier specified for entity: com.scriptico.domain.entities.UserProfile
Now, the identifier property should to be marked by the @GeneratedValue annotation. The @GeneratedValue annotation provides for the specification of generation strategies for the value of the primary key. Although you may not specify the @GeneratedValue annotation for the identifier, the following attempt to save the User entity:
private void create() {
User user = new User();
UserProfile profile = new UserProfile();
user.setProfile(profile);
profile.setUser(user);
userDao.createUser(user);
}
will throw an exception:
org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save(): com.scriptico.domain.entities.UserProfile
Now, if you just mark your identifier by the @GeneratedValue annotation and try to run the following method
private void create() {
User user = new User();
UserProfile profile = new UserProfile();
profile.setUserId(user.getId()); // an attempt to initialize the identifier
user.setProfile(profile);
profile.setUser(user);
userDao.createUser(user);
}
Hibernate will throw the following exception:
org.hibernate.AssertionFailure: null id in com.scriptico.domain.entities.UserProfile entry (don’t flush the Session after an exception occurs)
So, I defined my own generator and specified the strategy for the @GeneratedValue annotation as shown below:
@GeneratedValue(generator = "foreign")
@GenericGenerator(
name = "foreign",
strategy = "foreign",
parameters = {@org.hibernate.annotations.Parameter(name = "property", value = "user")})
The user property is marked by @OneToOne and @PrimaryKeyJoinColumn annotations. The @OneToOne annotation defines a single-valued association to another entity that has one-to-one multiplicity. The @PrimaryKeyJoinColumn annotation specifies a primary key column that is used as a foreign key to join to another table.
That is it. Now, tables are mapped and Hibernate saves records perfectly!
I do really appreciate your comments and if you see any problems with the approach, please let me know.
Resources
Thanks for sharing this tutorial.
I managed to do my mapping as directed, but I wonder if it is possible to do the same using a Long as primary key instead a String.
Hi Foster,
yes, there is no problem to use Long as a type for the primary key.
[...] few months ago, I wrote an article how to map the one-to-one bi-directional relations between two entities and now is the time for a simple example how to map many-to-many relation. You [...]