Post

Hibernate Many-to-One Relationship



Introduction

In Hibernate, a Many-to-One relationship represents an association where multiple instances of one entity (the “many” side) are related to a single instance of another entity (the “one” side). This is commonly used in database modeling, where many records in one table are associated with a single record in another table. In this article, we’ll cover how to map a Many-to-One relationship in Hibernate, using both unidirectional and bidirectional approaches, with clear examples.

Understanding Many-to-One Relationship in Hibernate

In database terms, a Many-to-One relationship occurs when many rows in one table are related to a single row in another table. For example, many Order records can be associated with a single Customer. In Hibernate, this relationship can be mapped using annotations, where one entity holds a reference to another entity.

Unidirectional Many-to-One Mapping

In a unidirectional Many-to-One relationship, the “many” side of the relationship holds a reference to the “one” side, but the reverse is not true. For example, multiple Order entities can reference the same Customer, but the Customer does not know about the Orders.

Entity Classes

Let’s define two entities: Order and Customer. Each order belongs to a specific customer, but the customer does not maintain a reference to their orders.

Order.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Entity
@Table(name = "orders")
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "order_date")
    private LocalDate orderDate;

    @Column(name = "total")
    private Double total;

    @ManyToOne
    @JoinColumn(name = "customer_id", referencedColumnName = "id")
    private Customer customer;

    // Getters, Setters, and Constructors omitted for brevity
}

Customer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
@Entity
@Table(name = "customers")
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;

    // Getters, Setters, and Constructors omitted for brevity
}

Explanation:

  • @ManyToOne: Declares that many Order entities can be linked to one Customer.
  • @JoinColumn: Specifies the foreign key (customer_id) in the orders table, which references the id in the customers table.
  • The customer field in the Order entity holds the reference to the Customer.

Objects Creation

In this example, we will use the EntityManager to persist the objects, though various persistence approaches are also possible.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Customer customer = new Customer();
customer.setName("John Doe");

Order order1 = new Order();
order1.setOrderDate(LocalDate.of(2024, 10, 20));
order1.setTotal(150.75);
order1.setCustomer(customer);

Order order2 = new Order();
order2.setOrderDate(LocalDate.of(2024, 10, 21));
order2.setTotal(200.50);
order2.setCustomer(customer);

entityManager.persist(customer);
entityManager.persist(order1);
entityManager.persist(order2);

Table Structure

Below is an example of how the customers and orders tables might look after data is inserted using the entities described above.

Customers Table:

1
2
3
| id  | name       |
|-----|------------|
| 1   | John Doe   |

Orders Table:

1
2
3
4
| id  | order_date | total  | customer_id |
|-----|------------|--------|-------------|
| 1   | 2024-10-20 | 150.75 | 1           |
| 2   | 2024-10-21 | 200.50 | 1           |

SQL Queries Generated by Hibernate

If you have hibernate.hbm2ddl.auto=update or spring.jpa.hibernate.ddl-auto=update enabled, you will see the corresponding insert statements reflecting the data added to the tables.

1
2
3
insert into customers (name) values (?);
insert into orders (order_date, total, customer_id) values (?, ?, ?);
insert into orders (order_date, total, customer_id) values (?, ?, ?);

When entityManager.persist(customer) is called, Hibernate first inserts the Customer entity into the customers table, generating an ID for the customer (e.g., id = 1). After the customer is persisted, Hibernate inserts the associated Order entities into the orders table, assigning the customer_id that corresponds to the persisted Customer.

Bidirectional Many-to-One (One-to-Many) Mapping

Bidirectional relationships are essentially equivalent to the @OneToMany annotation.

For more information, you can read about it here.

Fetching Strategy for @ManyToOne

In a @ManyToOne relationship, the fetching strategy determines how related entities are retrieved from the database when the parent entity is fetched. By default, @ManyToOne uses FetchType.EAGER, which means that the associated entity is loaded immediately along with the parent entity. This can lead to performance issues if the associated entity has a large number of records or if multiple parent entities are retrieved simultaneously.

If this immediate loading is not required, you can use FetchType.LAZY, which defers the loading of the associated entity until it is explicitly accessed. This approach is generally more efficient, as it reduces the initial amount of data fetched.

  • EAGER Fetching:

    1
    2
    3
    
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "person_id")
    private Person person;
    
  • LAZY Fetching:

    1
    2
    3
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "person_id")
    private Person person;
    

Choosing the right fetching strategy depends on the specific use case and the expected usage patterns of the application. Use EAGER fetching when you always need the associated entity, and prefer LAZY fetching when you want to minimize initial data retrieval and only load related entities as needed.

Summary

  • In a unidirectional Many-to-One relationship, only the “many” side knows about the “one” side.
  • In a bidirectional Many-to-One (also called One-to-Many) relationship, both sides of the relationship know about each other.
  • Use @ManyToOne to declare the “many” side of the relationship, and @OneToMany(mappedBy = "...") to define the “one” side in bidirectional mappings.
  • The fetching strategy can be lazy or eager depending on your performance needs, and the cascade types help manage the lifecycle of related entities.

Conclusion

Hibernate makes it easy to map Many-to-One relationships, allowing you to efficiently manage associations between entities. Depending on your use case, you can implement either unidirectional or bidirectional mappings. With the examples and explanations in this article, you should now be equipped to handle Many-to-One relationships in your Hibernate projects effectively.

© 2024 Java Tutorial Online. All rights reserved.