Learnitweb

many-to-one association in Hibernate

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

idname
1Dummy Name

Car

idmodeluser_id
1model-11

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();