Hibernate one-to-one mapping tutorial
last modified July 16, 2020
Hibernate one-to-one mapping tutorial shows how to create a one-to-one mapping between two entities in Hibernate with annotations.
Hibernate is an object-relational mapping tool for the Java programming language. It provides a framework for mapping an object-oriented domain model to a relational database.
Entity is a Java object that is going to be persisted. Entity classes are decorated with
Java annotations such as @Id
, @Table
, or @Column
.
@OneToOne
@OneToOne
annotation marks the relationship between two entities
with one-to-one multiplicity. It is not normally necessary to specify the
associated target entity explicitly since it can usually be inferred from the
type of the object being referenced. If the relationship is bidirectional, the
non-owning side must use the mappedBy
element of the
@OneToOne
annotation to specify the relationship field or property
of the owning side.
Hibernate one-to-one mapping example
The following application creates a one-to-one mapping between two classes:
User
and UserProfile
. The one-to-one association is
bidirectional; i.e. we can refer to a UserProfile
from
User
and vice versa. We use MySQL.
$ tree . ├── pom.xml └── src ├── main │ ├── java │ │ └── com │ │ └── zetcode │ │ ├── HibernateOneToOne.java │ │ └── model │ │ ├── Gender.java │ │ ├── User.java │ │ └── UserProfile.java │ └── resources │ ├── log4j2.xml │ └── META-INF │ └── persistence.xml └── test └── java
This is the project structure.
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zetcode</groupId> <artifactId>HibernateOneToOne</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.45</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.2.8.Final</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.10.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.5.0</version> <configuration> <mainClass>com.zetcode.HibernateOneToOne</mainClass> </configuration> </plugin> </plugins> </build> </project>
This is the Maven build file. The mysql-connector-java
is a MySQL driver
and hibernate-core
brings the core Hibernate functionality.
The log4j
and slf4j-log4j12
are logging artifacts.
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> <persistence-unit name="my-pu" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <properties> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/testdb?useSSL=false"/> <property name="javax.persistence.jdbc.user" value="testuser"/> <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/> <property name="javax.persistence.jdbc.password" value="test623"/> <property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/> </properties> </persistence-unit> </persistence>
The persistence.xml
file is a standard configuration file in JPA. It has to be
included in the META-INF
directory inside the JAR file that contains the entity beans.
The persistence.xml
file must define a persistence-unit with a unique name in the
current scoped classloader. The provider attribute specifies the underlying implementation
of the JPA EntityManager.
We provide configuration options for the MySQL connection. The hibernate.dialect
is set to MySQL Hibernate dialect. The hibernate.hbm2ddl.auto
is set to update
,
which means that the database schema is updated. If it does not exist, it is created.
<?xml version="1.0" encoding="UTF-8"?> <Configuration> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d [%t] %-5level %logger - %m%n" /> </Console> </Appenders> <Loggers> <!-- Log everything in Hibernate --> <Logger name="org.hibernate" level="error" additivity="false"> <AppenderRef ref="Console" /> </Logger> <!-- Log SQL statements --> <Logger name="org.hibernate.SQL" level="error" additivity="false"> <AppenderRef ref="Console" /> </Logger> <!-- Log custom packages --> <Logger name="com.zetcode" level="error" additivity="false"> <AppenderRef ref="Console" /> </Logger> <Root level="error"> <AppenderRef ref="Console" /> </Root> </Loggers> </Configuration>
The log4j2.xml
file is a configuration file for Log4j2.
package com.zetcode.model; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToOne; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO, generator = "native") @GenericGenerator(name = "native", strategy = "native") @Column(name = "user_id") private Long id; private String name; private String email; @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) private UserProfile userProfile; public User() { } public User(String name, String email) { this.name = name; this.email = email; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public UserProfile getUserProfile() { return userProfile; } public void setUserProfile(UserProfile userProfile) { this.userProfile = userProfile; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("User{id=").append(id).append(", name=") .append(name).append(", email=") .append(email).append(", userProfile=") .append(userProfile).append("}"); return builder.toString(); } }
This is the User
entity.
@Entity @Table(name = "users") public class User {
The @Entity
annotation specifies that the class is an entity.
The @Table
annotation specifies the primary table for the annotated entity.
So in our code example, Hibernate creates a users
table in the MySQL testdb
database.
@Id @GeneratedValue(strategy = GenerationType.AUTO, generator = "native") @GenericGenerator(name = "native", strategy = "native") @Column(name = "user_id") private Long id;
The @Id
annotation specifies the primary key of an entity while the
@GeneratedValue
and @GenericGenerator
provide for the
specification of generation strategies for the values of primary keys.
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "up_id", nullable = false) private UserProfile userProfile;
A user can has only one profile. We use the @OneToOne
mapping which
creates a one-to-one mapping between User
and UserProfile
entities.
The @JoinColumn
annotation defines the foreign key; it is the column
that associates the two tables.
The CascadeType
specifies what operations are propagated to the
related entities. The FetchType.LAZY
strategy is a hint to the persistence provider
runtime that data should be fetched lazily when it is first accessed.
package com.zetcode.model; import java.time.LocalDate; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToOne; import javax.persistence.Table; import org.hibernate.annotations.GenericGenerator; @Entity @Table(name = "user_profiles") public class UserProfile { @Id @GeneratedValue(strategy = GenerationType.AUTO, generator = "native") @GenericGenerator(name = "native", strategy = "native") @Column(name="up_id") private Long id; @Column(name = "phone_number") private String phoneNumber; @Enumerated(EnumType.STRING) @Column(length = 10) private Gender gender; @Column(name = "date_of_birth") private LocalDate dateOfBirth; private String address; @OneToOne(fetch = FetchType.LAZY, cascade=CascadeType.ALL, mappedBy = "userProfile") private User user; public UserProfile() { } public UserProfile(String phoneNumber, Gender gender, LocalDate dateOfBirth, String address) { this.phoneNumber = phoneNumber; this.gender = gender; this.dateOfBirth = dateOfBirth; this.address = address; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getPhoneNumber() { return phoneNumber; } public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; } public Gender getGender() { return gender; } public void setGender(Gender gender) { this.gender = gender; } public LocalDate getDateOfBirth() { return dateOfBirth; } public void setDateOfBirth(LocalDate dateOfBirth) { this.dateOfBirth = dateOfBirth; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("UserProfile{id=").append(id).append(", phoneNumber=") .append(phoneNumber).append(", gender=").append(gender) .append(", dateOfBirth=").append(dateOfBirth) .append(", address=").append(address).append("}"); return builder.toString(); } }
This is the UserProfile
entity.
@OneToOne(fetch = FetchType.LAZY, cascade=CascadeType.ALL, mappedBy = "userProfile") private User user;
The mappedBy
option is set on the non-owning side; it tells Hibernate
that the owner of the relationship is User's
userProfile
field.
package com.zetcode.model; public enum Gender { MALE, FEMALE }
This is the Gender
enumeration.
package com.zetcode; import com.zetcode.model.Gender; import com.zetcode.model.User; import com.zetcode.model.UserProfile; import java.time.LocalDate; import java.time.Month; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.persistence.Query; public class HibernateOneToOne { private static final String PERSISTENCE_UNIT_NAME = "my-pu"; public static void main(String[] args) { EntityManagerFactory emf = Persistence.createEntityManagerFactory( PERSISTENCE_UNIT_NAME); EntityManager entityManager = emf.createEntityManager(); try { entityManager.getTransaction().begin(); User user1 = new User("Peter", "peter6@gmail.com"); UserProfile uprof1 = new UserProfile("0956909433", Gender.MALE, LocalDate.of(1987, Month.MARCH, 20), "Sturova 9, Bratislava"); user1.setUserProfile(uprof1); uprof1.setUser(user1); entityManager.persist(user1); User user2 = new User("Lucia", "lucia12@yahoo.com"); UserProfile uprof2 = new UserProfile("0854919671", Gender.FEMALE, LocalDate.of(1993, Month.JULY, 12), "Bazova 12, Brezno"); user2.setUserProfile(uprof2); uprof2.setUser(user2); entityManager.persist(user2); String qlQuery = "SELECT u FROM User u"; Query query = entityManager.createQuery(qlQuery); List<User> users = query.getResultList(); users.stream().forEach((user) -> { System.out.println(user.getUserProfile().getDateOfBirth()); }); String qlQuery2 = "SELECT up FROM UserProfile up"; Query query2 = entityManager.createQuery(qlQuery2); List<UserProfile> user_profiles = query2.getResultList(); user_profiles.stream().forEach((user_profile) -> { System.out.println(user_profile.getUser().getName()); }); entityManager.getTransaction().commit(); } finally { entityManager.close(); emf.close(); } } }
In the HibernateOneToOne
, we create some entity classes, save them, and perform
two queries.
User user1 = new User("Peter", "peter6@gmail.com"); UserProfile uprof1 = new UserProfile("0956909433", Gender.MALE, LocalDate.of(1987, Month.MARCH, 20), "Sturova 9, Bratislava"); user1.setUserProfile(uprof1); uprof1.setUser(user1);
A user and his profile is created. Note that we set a user to a profile and a profile to a user.
entityManager.persist(user1);
A user is saved with the persist()
method.
String qlQuery = "SELECT u FROM User u"; Query query = entityManager.createQuery(qlQuery); List<User> users = query.getResultList(); users.stream().forEach((user) -> { System.out.println(user.getUserProfile().getDateOfBirth()); });
In this query, we refer to the user profile via the user.
String qlQuery2 = "SELECT up FROM UserProfile up"; Query query2 = entityManager.createQuery(qlQuery2); List<UserProfile> user_profiles = query2.getResultList(); user_profiles.stream().forEach((user_profile) -> { System.out.println(user_profile.getUser().getName()); });
In the second query, we refer to a user via a user profile. This is the essence of the bidirectional mapping between the two entities.
1987-03-20 1993-07-12 Peter Lucia
This is the output.
mysql> select * from users; +---------+-------------------+-------+-------+ | user_id | email | name | up_id | +---------+-------------------+-------+-------+ | 1 | peter6@gmail.com | Peter | 1 | | 2 | lucia12@yahoo.com | Lucia | 2 | +---------+-------------------+-------+-------+ 2 rows in set (0.00 sec) mysql> select * from user_profiles; +-------+-----------------------+---------------+--------+--------------+ | up_id | address | date_of_birth | gender | phone_number | +-------+-----------------------+---------------+--------+--------------+ | 1 | Sturova 9, Bratislava | 1987-03-20 | MALE | 0956909433 | | 2 | Bazova 12, Brezno | 1993-07-12 | FEMALE | 0854919671 | +-------+-----------------------+---------------+--------+--------------+ 2 rows in set (0.00 sec)
This is how Hibernate created the tables. The up_id
is the foreign
key and is set in the relationship owner - the users table.
In this tutorial, we have presented a one-to-one association between entities in Hibernate.