Yes, we're now running our Black Friday Sale. All Access and Pro are 33% off until 2nd December, 2025:
Preventing Jackson From Fetching Lazy Entity Fields
Last updated: October 3, 2025
1. Introduction
In this quick tutorial, we’ll explore ways to avoid Jackson serialization on non-fetched lazy objects using the Baeldung University domain model. We’ll set up a simple Spring-based application to demonstrate the principles, but no prior Spring experience is needed to follow along. All examples rely only on JPA concepts.
2. Setting Up the Example
One common challenge when working with JPA and Jackson is serializing entities containing lazy associations. We’ll start with a representation of a university department, which contains many courses:
@Entity
class Department {
@Id
Long id;
String name;
@OneToMany(mappedBy = "department")
List<Course> courses;
// getters and setters
}
The relationship to courses is mapped by the department field in Course. We also have to explicitly set the fetch type to LAZY, since the default for many-to-one relationships is EAGER:
@Entity
class Course {
@Id
Long id;
String name;
@ManyToOne(fetch = FetchType.LAZY)
Department department;
// getters and setters
}
When Jackson serializes an entity, it calls all of its getters. If a field is still lazily loaded, this triggers extra database queries. And if the persistence context is already closed, serialization fails with a LazyInitializationException.
3. Demonstrating the Problem
To visualize the issue, we’ll create endpoints to save and retrieve Department and Course entities.
3.1. Testing the DepartmentController
Let’s start with a controller with simple POST and GET endpoints:
@RestController
@RequestMapping("/departments")
public class DepartmentController {
@Autowired
DepartmentRepository repository;
@PostMapping
Department post(@RequestBody Department department) {
return repository.save(department);
}
@GetMapping("/{id}")
Optional<Department> get(@PathVariable("id") Long id) {
return repository.findById(id);
}
}
Even if we don’t include any courses, attempting to retrieve a newly created department results in an exception:
curl http://localhost:8080/departments/1
That’s because Jackson fails to initialize the proxied field:
failed to lazily initialize a collection of role:
com.baeldung.jacksonlazyfields.model.Department.courses:
could not initialize proxy - no Session
3.2. Testing the CourseController
We’ll follow the same pattern for a course controller, with simple POST and GET endpoints. But this time, we’ll only get an exception for courses that contain a department. We can check that by inserting a new department:
curl -X POST http://localhost:8080/departments\
-H "Content-Type: application/json" \
-d '{"name":"Computer-Science"}'
Then, reference it in a new course by ID:
curl -X POST http://localhost:8080/courses \
-H "Content-Type: application/json" \
-d '{"name":"Machine-Learning", "department":{"id":1} }'
If we try to retrieve that course, we’ll get an exception similar to the first one:
could not initialize proxy [com.baeldung.jacksonlazyfields.model.Department#1]
- no Session
Let’s see some options we have to avoid this.
4. Using @JsonIgnore
A simple option if we don’t need to serialize a specific association is to include a @JsonIgnore annotation on the field:
@JsonIgnore
@OneToMany(mappedBy = "department")
List<Course> courses;
Now, the field is excluded from serialization:
{
"id": 1,
"name": "Computer-Science"
}
This is useful for hiding a field in the payload.
5. Using Jackson’s Hibernate Module
A global solution is registering the Hibernate module for Jackson, which depends on jackson-datatype-hibernate6:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate6</artifactId>
</dependency>
After including the dependency in our POM, we have to make sure the ObjectMapper instance we’re using registers the Hibernate6Module:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Hibernate6Module());
This avoids lazy loading; instead, Jackson now writes null for unfetched associations:
{
"id": 1,
"name": "Computer-Science",
"courses": null
}
This solution is nice because it works for any object serialized with that ObjectMapper instance.
6. Using DTO Projections
A tedious but robust approach is creating DTOs. This way, we have complete control over what is serialized while decoupling the persistence layer, avoiding the need to include external libraries.
6.1. Creating the DTO
First, let’s create a record containing only the fields we want to serialize:
public record DepartmentDto(Long id, String name) {}
6.2. Updating the GET Endpoint
Second, we’ll update our GET endpoint to use the DTO:
@GetMapping("/{id}")
Optional get(@PathVariable("id") Long id) {
return repository.findById(id)
.map(d -> new DepartmentDto(id, d.getName()));
}
6.3. Fetching the Results
Now, we’ll have no issues with Jackson stumbling on non-fetched fields:
{
"id": 1,
"name": "Computer-Science"
}
7. Conclusion
In this article, we learned how to prevent Jackson from accessing unfetched proxies when serializing lazy relationships. The best approach depends on our design; DTOs are the cleanest, while registering the Hibernate module prevents unexpected exceptions. Also, simply adding @JsonIgnore ensures a field is never serialized.
The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.















