1. Introduction
In this quick tutorial, we’ll discuss many-to-one association in Hibernate. Associations are one of the most frequently asked questions in interview. These could be very confusing and could lead to design issues. We’ll understand many-to-one association by writing a program.
The many-to-one association is one of the most common and simple associations. This association has a direct equivalent to database design where a foreign key is used to create a relationship between a child table and a parent table. A many-to-one association is by definition is a child association.
2. Important points about many-to-one association
- This association is not bidirectional and is owned by the child entity.
- The
@ManyToOne
annotation is used to denote many-to-one association. - The many-to-one association is owned by the child table. That is, the child table will have the foreign key.
- The
@ManyToOne
annotation is used on the child entity. - Using
@ManyToOne
in child entity will create a Foreign Key (a column) in the child table. You can use@JoinColumn
and the Foreign Key name in addition to@ManyToOne
annotation. - If you don’t specify the join column using
@JoinColumn
, the Foreign Key column name is formed by joining the@ManyToOne
property and the parent entity identifier via the_
character.
3. Example
Consider the example of a person having many cars. From the side of car the relationship is many-to-one. That is many cars map to one person.
Let us now run an example.
pom.xml
<dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.27</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>5.6.3.Final</version> </dependency> </dependencies>
HibernateUtil.java
package com.learnitweb.util; import org.hibernate.SessionFactory; import org.hibernate.boot.Metadata; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; public class HibernateUtil { private static StandardServiceRegistry standardServiceRegistry; private static SessionFactory sessionFactory; static { try { if (sessionFactory == null) { standardServiceRegistry = new StandardServiceRegistryBuilder().configure().build(); MetadataSources metadataSources = new MetadataSources(standardServiceRegistry); Metadata metadata = metadataSources.getMetadataBuilder().build(); sessionFactory = metadata.getSessionFactoryBuilder().build(); } } catch (Exception e) { e.printStackTrace(); if (standardServiceRegistry != null) { StandardServiceRegistryBuilder.destroy(standardServiceRegistry); } } } public static SessionFactory getSessionFactory() { return sessionFactory; } }
hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- Database connection settings --> <property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property> <property name="connection.url">jdbc:mysql://localhost:3306/mydb</property> <property name="connection.username">root</property> <property name="connection.password">admin</property> <!-- SQL dialect --> <property name="dialect">org.hibernate.dialect.MySQL8Dialect</property> <!-- Echo all executed SQL to stdout --> <property name="show_sql">true</property> <!-- Drop and re-create the database schema on startup --> <property name="hbm2ddl.auto">create</property> <mapping class="com.learnitweb.entity.User"></mapping> <mapping class="com.learnitweb.entity.Car"></mapping> </session-factory> </hibernate-configuration>
Car.java
package com.learnitweb.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.ForeignKey; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; @Entity @Table(name = "Car") public class Car { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Long id; @Column(name = "model") String model; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", foreignKey = @ForeignKey(name = "USER_ID_FK")) private User user; // getter, setter and toString() omitted for brevity }
User.java
package com.learnitweb.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "User") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") private Long id; @Column(name = "name") private String name; // getter, setter and toString() omitted for brevity }
Client.java
package com.learnitweb.client; import org.hibernate.Session; import com.learnitweb.entity.Car; import com.learnitweb.entity.User; import com.learnitweb.util.HibernateUtil; public class Client { public static void main(String[] args) { try (Session session = HibernateUtil.getSessionFactory().openSession()) { session.beginTransaction(); User user = new User(); user.setName("Dummy Name"); session.save(user); Car car1 = new Car(); car1.setModel("model-1"); car1.setUser(user); session.save(car1); session.getTransaction().commit(); } catch (Exception e) { e.printStackTrace(); } } }
Output – SQL statements in console
Hibernate: create table Car (id bigint not null auto_increment, model varchar(255), user_id bigint, primary key (id)) Hibernate: create table User (id bigint not null auto_increment, name varchar(255), primary key (id)) Hibernate: alter table Car add constraint USER_ID_FK foreign key (user_id) references User (id) Hibernate: insert into User (name) values (?) Hibernate: insert into Car (model, user_id) values (?, ?)
Tables created
CREATE TABLE `car` ( `id` bigint NOT NULL AUTO_INCREMENT, `model` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, `user_id` bigint DEFAULT NULL, PRIMARY KEY (`id`), KEY `USER_ID_FK` (`user_id`), CONSTRAINT `USER_ID_FK` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ) CREATE TABLE `user` ( `id` bigint NOT NULL AUTO_INCREMENT, `name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`) )
Data in tables
User
id | name |
1 | Dummy Name |
Car
id | model | user_id |
1 | model-1 | 1 |
4. Performance tips
- For saving child entities after saving the parent, the usual approach is to get the Parent and set in the Child. For example:
User user = session.find(User.class, 1L); Car car = new Car(); car.setModel("dummy model"); car.setUser(user);
The Hibernate will execute the select
query, but we don’t need that as we need only to set the Foreign Key column. To avoid this extra query, you should use getReferece()
method or if you are using Spring Data JPA use getOne()
.
- You should use
FetchType.LAZY
in many-to-one associations. Many-to-one associations are be default eager. Using LAZY strategy will save queries fired by Hibernate. - While using the LAZY strategy, if you use the following approach, Hibernate uses two queries.
Car car = session.find(Car.class, 1L);
This approach will use two queries. You should use join fetch to increase performance. This will use only query.
Car car = session.createQuery("select c " + "from Car c " + "join fetch c.user " + "where c.id = :id", Car.class) .setParameter("id", 1L).getSingleResult();