# Hibernate get vs load: Differences, Performance, and Use Cases

> Understand Hibernate get vs load. Use get for null-safe access and load for lazy proxies. Optimize entity retrieval in your Spring Boot apps.

- Repository: [Spring/spring-framework](https://github.com/spring-projects/spring-framework)
- Tags: deep-dive
- Published: 2026-02-16

---

**Use `Session.get()` for immediate data access with null-safety, and `Session.load()` for lazy proxies when you only need a reference without database round-trips.**

When working with the Spring Framework and Hibernate, understanding the distinction between entity retrieval methods is critical for optimizing database interactions. Both `Session.get()` and `Session.load()` are defined on Hibernate's `org.hibernate.Session` interface and retrieve entities by primary key, but they differ fundamentally in fetch strategy and exception handling. This guide examines the Hibernate get vs load methods using actual implementation details from the spring-projects/spring-framework repository.

## Core Differences Between get() and load()

### Fetch Strategy and Database Access

The most significant distinction lies in when the database query executes.

**`Session.get()`** performs an immediate SQL `SELECT`. When you invoke `session.get(Entity.class, id)`, Hibernate checks the first-level cache (session cache), then the second-level cache if configured. If the entity is not cached, it hits the database immediately and returns the fully populated instance or `null`.

**`Session.load()`** returns a proxy object without touching the database. The actual SQL `SELECT` is deferred until you access a non-identifier property of the proxy. This lazy initialization pattern avoids unnecessary database round-trips when you only need a reference to the entity.

### Null Handling vs Exception Handling

Error handling behavior diverges sharply between the two methods:

- **`get()`** returns `null` if no row exists with the given primary key. This makes it ideal for existence checks where `null` is a valid business case.
- **`load()`** never returns `null` for the initial call. Instead, it throws `org.hibernate.ObjectNotFoundException` when the proxy is first accessed if the underlying row does not exist. This makes `load()` risky for speculative lookups but efficient for associations where you assume the entity exists.

### Return Types and Proxy Behavior

`Session.get()` returns the actual entity class instance or `null`. `Session.load()` returns a Hibernate-generated proxy subclass that implements the entity interface or extends the entity class. This proxy contains only the identifier value until initialization occurs.

## Performance Implications

### When get() Adds Unnecessary Overhead

Using `get()` when you only need a foreign-key reference forces Hibernate to fetch all column data immediately. If you are merely setting an association on a new entity, this eager fetch wastes I/O and memory by retrieving data you never use.

### The Cost of Proxy Creation

`Session.load()` incurs minimal overhead for proxy generation but requires an open `Session` when the proxy is eventually accessed. According to the Spring Framework source code, `SessionHolder` in [`spring-orm/src/main/java/org/springframework/orm/jpa/hibernate/SessionHolder.java`](https://github.com/spring-projects/spring-framework/blob/main/spring-orm/src/main/java/org/springframework/orm/jpa/hibernate/SessionHolder.java) manages this session binding. If the session closes before proxy initialization, Hibernate throws a `LazyInitializationException`.

## Practical Use Cases and Code Examples

### Immediate Data Access with get()

Use `get()` when you need to verify existence or display data immediately:

```java
// Example: Eager fetch for immediate data access
Session session = entityManager.unwrap(Session.class);
Long personId = 42L;
Person person = session.get(Person.class, personId);

if (person != null) {
    System.out.println("Found: " + person.getFirstName());
} else {
    System.out.println("Person not found");
}

```

### Lazy Association References with load()

Use `load()` to establish foreign-key relationships without a database round-trip:

```java
// Example: Lazy reference for association setting
Session session = entityManager.unwrap(Session.class);
Long customerId = 10L;

Order order = new Order();
// No SELECT executed here; returns a proxy
order.setCustomer(session.load(Customer.class, customerId));
order.setTotal(BigDecimal.valueOf(99.99));

orderRepository.save(order);
// Hibernate issues INSERT with CUSTOMER_ID, never loading Customer data

```

### Handling Missing Entities

When using `load()`, prepare for `ObjectNotFoundException` if the entity might not exist:

```java
// Example: Handling missing entities with load()
try {
    Customer proxy = session.load(Customer.class, 999L);
    // Database hit occurs here when accessing non-id property
    System.out.println(proxy.getName());
} catch (ObjectNotFoundException ex) {
    // Entity does not exist in database
    logger.error("Customer not found", ex);
}

```

## Spring Framework Integration

### Session Management in Spring

Spring Framework does not reimplement Hibernate's `get()` or `load()` methods, but it provides infrastructure to manage the underlying `Session` lifecycle. The `SessionHolder` class in [`spring-orm/src/main/java/org/springframework/orm/jpa/hibernate/SessionHolder.java`](https://github.com/spring-projects/spring-framework/blob/main/spring-orm/src/main/java/org/springframework/orm/jpa/hibernate/SessionHolder.java) wraps the Hibernate `Session` and binds it to the current thread, ensuring that `load()` proxies remain valid throughout the transaction.

When you obtain a `Session` via `entityManager.unwrap(Session.class)` or through `SessionFactoryUtils.getSession(sessionFactory)`, you are working with the native Hibernate API that Spring manages.

### Transaction Synchronization

The `SpringSessionSynchronization` class in [`spring-orm/src/main/java/org/springframework/orm/jpa/hibernate/SpringSessionSynchronization.java`](https://github.com/spring-projects/spring-framework/blob/main/spring-orm/src/main/java/org/springframework/orm/jpa/hibernate/SpringSessionSynchronization.java) coordinates session flushing with Spring's transaction lifecycle. This is critical for `load()` because the deferred SQL execution must occur within the same transaction boundary. `HibernateTransactionManager` in [`spring-orm/src/main/java/org/springframework/orm/hibernate5/HibernateTransactionManager.java`](https://github.com/spring-projects/spring-framework/blob/main/spring-orm/src/main/java/org/springframework/orm/hibernate5/HibernateTransactionManager.java) orchestrates this process, ensuring that `getCurrentSession()` returns a session bound to the current Spring-managed transaction.

## Summary

- **`Session.get()`** executes an immediate SQL `SELECT`, returns the fully initialized entity or `null` if not found, and is ideal for existence checks and immediate data access.
- **`Session.load()`** returns a lightweight proxy without database access, throws `ObjectNotFoundException` when accessed if the row is missing, and is optimized for setting foreign-key references without loading unnecessary data.
- **Spring Framework** manages the underlying `Session` lifecycle through `SessionHolder` and `HibernateTransactionManager`, ensuring that both eager `get()` calls and lazy `load()` proxies operate correctly within transactional boundaries.

## Frequently Asked Questions

### What happens if I call Session.load() with an invalid ID?

`Session.load()` immediately returns a proxy object containing only the identifier you provided. No database validation occurs at this point. However, when you first access a non-identifier property (such as `getName()`), Hibernate executes the SQL `SELECT`. If no row exists with that ID, Hibernate throws `org.hibernate.ObjectNotFoundException`.

### Can I use Session.get() and Session.load() interchangeably in Spring applications?

No, they serve different architectural purposes. Use `Session.get()` when you need to verify existence or manipulate data immediately, as it returns `null` for missing entities. Use `Session.load()` when establishing associations (foreign keys) where you assume the target exists, avoiding unnecessary database round-trips. In Spring-managed transactions, both methods work correctly, but choosing the wrong one can lead to `LazyInitializationException` (if the session closes before a `load()` proxy is accessed) or unnecessary I/O (if `get()` fetches unused data).

### How does Spring Framework handle the Hibernate Session when using load()?

Spring binds the Hibernate `Session` to the current thread using `SessionHolder` (located in [`spring-orm/src/main/java/org/springframework/orm/jpa/hibernate/SessionHolder.java`](https://github.com/spring-projects/spring-framework/blob/main/spring-orm/src/main/java/org/springframework/orm/jpa/hibernate/SessionHolder.java)). When you call `Session.load()`, Spring ensures the session remains open for the duration of the transaction through `SpringSessionSynchronization`. This prevents `LazyInitializationException` when the proxy is later accessed, as the deferred SQL execution occurs within the same transactional boundary managed by `HibernateTransactionManager`.

### Is Session.load() faster than Session.get()?

`Session.load()` is faster when you only need a reference to an entity without accessing its data, because it avoids the immediate SQL `SELECT`. However, if you immediately access the loaded entity's properties, the performance difference disappears (and `load()` may be slightly slower due to proxy overhead). Use `load()` for performance optimization only when setting foreign keys or passing references without data access; otherwise, `get()` provides more predictable behavior with immediate null-checking.