Preventing Jackson from Fetching Lazy Entity Fields Example
1. Introduction
The Spring Boot JPA project uses Jackson for JSON serialization. When Jackson serializes a JPA entity, it can trigger lazy-loading of associated entities even outside of transactions. If the Hibernates proxies are closed, then accessing a lazy field throws HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role. In this example, I will demonstrate how it happened and three strategies to prevent Jackson fetching lazy fields.
- Annotate with
@JsonIgnorefor the lazy-loading fields - Use DTO Projection in the Rest controller
- Use DTO POJO in the Rest controller
2. Setup
2.1 Build.gradle
In this step, I will create a Gradle Java 21 project via Spring Initializer with: MapStruct, Spring Data JPA, and H2 libraries.
build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.5.6'
id 'io.spring.dependency-management' version '1.1.7'
}
group = 'org.jcg.zheng.demo'
version = '0.0.1-SNAPSHOT'
description = 'Demo project for Spring Boot'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
// https://mvnrepository.com/artifact/org.mapstruct/mapstruct
implementation 'org.mapstruct:mapstruct:1.6.3'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.6.3'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform()
}
2.2 Application.properties
In this step, I will update the generated application.properties to enable the Hibernate logging and disable the Open Session In View(OSIV). Note: Spring Boot default OSIV to true – it forces persistence context to stay open so that the view layer can trigger the proxy initialization.
application.properteis
spring.application.name=demolazyfetch logging.level.org.springframework.web=DEBUG logging.level.org.springframework.transaction.interceptor.TransactionInterceptor=DEBUG logging.level.org.hibernate.SQL=DEBUG spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=update spring.h2.console.enabled=true #default is true spring.jpa.open-in-view=false
2.3 DemolazyfetchApplication
In this step, I will show the generated DemolazyfetchApplication.java. No change is needed.
DemolazyfetchApplication.java
package org.jcg.zheng.demo.demolazyfetch;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemolazyfetchApplication {
public static void main(String[] args) {
SpringApplication.run(DemolazyfetchApplication.class, args);
}
}
2.4 Insert Test Data SQL
In this step, I will insert test data in the H2 database via H2 console.
TestData.sql
insert into parent(id) values(1); insert into child(id, price, quantity,parent_id) values(1,'10.5',2, 1); insert into child(id, price, quantity,parent_id) values(2,'20.6',1, 1);
3. Parent & Child Entity Classes
3.1 Parent Entity
In this step, I will create a Parent.java entity class that annotates with @OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST, fetch = FetchType.LAZY) at the List of Child.
Parent.java
package org.jcg.zheng.demo.demolazyfetch.entity;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
@Entity
public class Parent {
@Id
private Long id;
// @JsonIgnore
@OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
private List<Child> children = new ArrayList<>();
public List<Child> getChildren() {
return children;
}
public Long getId() {
return id;
}
public void setChildren(List<Child> children) {
this.children = children;
}
public void setId(Long id) {
this.id = id;
}
}
- Line 20: commented
@JsonIgnoreinitially till the step 8.5. - Line 21:
@OneToMany(FetchType.LAZY)for thechildren.
3.2 Child Entity
In this step, I will create a Child.java entity class that annotates with @ManyToOne.
Child.java
package org.jcg.zheng.demo.demolazyfetch.entity;
import java.math.BigDecimal;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
@Entity
public class Child {
@Id
private Long id;
private BigDecimal price;
private int quantity;
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name = "parent_id")
private Parent parent;
public Long getId() {
return id;
}
public Parent getParent() {
return parent;
}
public BigDecimal getPrice() {
return price;
}
public int getQuantity() {
return quantity;
}
public void setId(Long id) {
this.id = id;
}
public void setParent(Parent parent) {
parent.getChildren().add(this);
this.parent = parent;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
}
- Line 20: annotated
@ManyToOne(fetch=FetchType.LAZY)for theparent.
4. Parent & Child DTO POJOs
4.1 ParentDto
In this step, I will create a ParentDto.java POJO class that implements Serializable.
ParentDto.java
package org.jcg.zheng.demo.demolazyfetch.dto;
import java.io.Serializable;
import java.util.List;
public class ParentDto implements Serializable{
private static final long serialVersionUID = -5451873400700416755L;
private Long id;
private List<ChildDto> orderItems;
public ParentDto() {
super();
}
public Long getId() {
return id;
}
public List<ChildDto> getOrderItems() {
return orderItems;
}
public void setId(Long id) {
this.id = id;
}
public void setOrderItems(List<ChildDto> orderItems) {
this.orderItems = orderItems;
}
}
4.2 ChildDto
In this step, I will create a ChildDto.java POJO class that implements Serializable.
ChildDto.java
package org.jcg.zheng.demo.demolazyfetch.dto;
import java.io.Serializable;
import java.math.BigDecimal;
public class ChildDto implements Serializable {
private static final long serialVersionUID = -3024223952933674948L;
private Long id;
private ParentDto order;
private BigDecimal price;
private int quantity;
public ChildDto() {
super();
}
public Long getId() {
return id;
}
public ParentDto getOrder() {
return order;
}
public BigDecimal getPrice() {
return price;
}
public int getQuantity() {
return quantity;
}
public void setId(Long id) {
this.id = id;
}
public void setOrder(ParentDto order) {
this.order = order;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
}
5. Parent & Child Repository Interfaces
5.1 Parent Repository
In this step, I will create a ParentRepo.java entity class that extends from JpaRepository.
ParentRepo.java
package org.jcg.zheng.demo.demolazyfetch.repo;
import java.util.List;
import org.jcg.zheng.demo.demolazyfetch.entity.Parent;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ParentRepo extends JpaRepository<Parent, Long> {
List<ParentView> findParentViewById(Long id);
}
5.2 Child Repository
In this step, I will create a ChildRepo.java entity class that extends with JpaRepository.
Child.java
package org.jcg.zheng.demo.demolazyfetch.repo;
import org.jcg.zheng.demo.demolazyfetch.entity.Child;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ChildRepo extends JpaRepository<Child, Long> {
}
6. Parent & Child Mapper Interfaces
6.1 Parent Mapper
In this step, I will create a ParentMapper.java interface that converts the Parent entity to ParentDto POJO.
ParentMapper.java
package org.jcg.zheng.demo.demolazyfetch.mapper;
import org.jcg.zheng.demo.demolazyfetch.dto.ParentDto;
import org.jcg.zheng.demo.demolazyfetch.entity.Parent;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper(componentModel = "spring")
public interface ParentMapper {
ParentMapper INSTANCE = Mappers.getMapper(ParentMapper.class);
ParentDto toDto(Parent order);
}
6.2 Child Mapper
In this step, I will create a ChildMapper.java interface that converts the Child entity to ChildDto POJO..
ChildMapper.java
package org.jcg.zheng.demo.demolazyfetch.mapper;
import org.jcg.zheng.demo.demolazyfetch.dto.ChildDto;
import org.jcg.zheng.demo.demolazyfetch.entity.Child;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper(componentModel = "spring")
public interface ChildMapper {
ChildMapper INSTANCE = Mappers.getMapper(ChildMapper.class);
ChildDto toDto(Child item);
}
7. Rest Controller and Service
7.1 OrderService
In this step, I will create a OrderService.java entity class that has two methods: ParentDto getDto(Long orderId) and ParentView getView(Long orderId).
OrderService.java
package org.jcg.zheng.demo.demolazyfetch.service;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.jcg.zheng.demo.demolazyfetch.dto.ChildDto;
import org.jcg.zheng.demo.demolazyfetch.dto.ParentDto;
import org.jcg.zheng.demo.demolazyfetch.entity.Child;
import org.jcg.zheng.demo.demolazyfetch.entity.Parent;
import org.jcg.zheng.demo.demolazyfetch.mapper.ChildMapper;
import org.jcg.zheng.demo.demolazyfetch.mapper.ParentMapper;
import org.jcg.zheng.demo.demolazyfetch.repo.ParentRepo;
import org.jcg.zheng.demo.demolazyfetch.repo.ParentView;
import org.springframework.stereotype.Service;
import jakarta.transaction.Transactional;
@Service
@Transactional
public class OrderService {
public OrderService(ParentRepo orderRepo, ParentMapper orderMapper, ChildMapper orderItemMapper) {
super();
this.orderRepo = orderRepo;
this.orderMapper = orderMapper;
this.orderItemMapper = orderItemMapper;
}
private final ParentRepo orderRepo;
private final ParentMapper orderMapper;
private final ChildMapper orderItemMapper;
public ParentDto getDto(Long orderId) {
ParentDto oDto = null;
Optional<Parent> found = orderRepo.findById(orderId);
if (found.isPresent()) {
List<Child> items = found.get().getChildren();
oDto = orderMapper.toDto(found.get());
List<ChildDto> iDs = new ArrayList<>();
oDto.setOrderItems(iDs);
for (Child oi : items) {
iDs.add(orderItemMapper.toDto(oi));
}
}
return oDto;
}
public ParentView getView(Long orderId) {
return orderRepo.findParentViewById(orderId).get(0);
}
}
- Line 37: accessing lazy-loading fields within the transaction is ok.
7.2 Parent View
In this step, I will create a ParentView.java interface that does not contain any lazy-loading fields.
ParentView.java
package org.jcg.zheng.demo.demolazyfetch.repo;
public interface ParentView {
Long getId();
}
7.3 DemoController
In this step, I will create a DemoControlller.java class that demonstrates xxx is thrown when calling ResponseEntity getOrder(@PathVariable("id") Long id) .
Child.java
package org.jcg.zheng.demo.demolazyfetch.rest;
import java.util.Optional;
import org.jcg.zheng.demo.demolazyfetch.entity.Parent;
import org.jcg.zheng.demo.demolazyfetch.repo.ParentRepo;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/demo")
public class DemoController {
public DemoController(ParentRepo parentRepo) {
super();
this.parentRepo = parentRepo;
}
private final ParentRepo parentRepo;
@GetMapping("{id}")
public ResponseEntity<Parent> getOrder(@PathVariable("id") Long id) {
Optional<Parent> found = parentRepo.findById(id);
if (found.isPresent()) {
return ResponseEntity.ok(found.get());
} else {
return ResponseEntity.notFound().build();
}
}
}
- Line 25: The Rest response is the
ParentEntity class.
7.4 OrderController
In this step, I will create an OrderControlller.java class that demonstrates both DTO and view Interface addressed accessing the lazy-loading fields.
title here
package org.jcg.zheng.demo.demolazyfetch.rest;
import org.jcg.zheng.demo.demolazyfetch.dto.ParentDto;
import org.jcg.zheng.demo.demolazyfetch.repo.ParentView;
import org.jcg.zheng.demo.demolazyfetch.service.OrderService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/order")
public class OrderController {
public OrderController(OrderService orderService) {
super();
this.orderService = orderService;
}
private final OrderService orderService;
@GetMapping("/dto/{orderId}")
public ResponseEntity<ParentDto> getDto(@PathVariable("orderId") Long orderId) {
ParentDto found = orderService.getDto(orderId);
if (found == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(found);
}
@GetMapping("/view/{orderId}")
public ResponseEntity<ParentView> getOrderP(@PathVariable("orderId") Long orderId) {
ParentView found = orderService.getView(orderId);
return ResponseEntity.ok(found);
}
}
- Line 24: the Rest response is
ParentDtoPOJO. - Line 33: the Rest Response is
ParentViewInterface.
8. Demonstrate
8.1 Start the Spring Boot Application
In this step, I will start the spring boot application and capture the server log.
Server Started Log
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.5.6) 2025-10-12T14:17:58.348-05:00 INFO 22396 --- [demolazyfetch] [ main] o.j.z.d.d.DemolazyfetchApplication : Starting DemolazyfetchApplication using Java 21.0.8 with PID 22396 (C:\MaryZheng\workspace\demolazyfetch\bin\main started by zzhen in C:\MaryZheng\workspace\demolazyfetch) 2025-10-12T14:17:58.353-05:00 INFO 22396 --- [demolazyfetch] [ main] o.j.z.d.d.DemolazyfetchApplication : No active profile set, falling back to 1 default profile: "default" 2025-10-12T14:17:58.943-05:00 INFO 22396 --- [demolazyfetch] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. 2025-10-12T14:17:58.995-05:00 INFO 22396 --- [demolazyfetch] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 38 ms. Found 2 JPA repository interfaces. 2025-10-12T14:17:59.384-05:00 INFO 22396 --- [demolazyfetch] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http) 2025-10-12T14:17:59.393-05:00 INFO 22396 --- [demolazyfetch] [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2025-10-12T14:17:59.394-05:00 INFO 22396 --- [demolazyfetch] [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.46] 2025-10-12T14:17:59.434-05:00 INFO 22396 --- [demolazyfetch] [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2025-10-12T14:17:59.436-05:00 INFO 22396 --- [demolazyfetch] [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1039 ms 2025-10-12T14:17:59.633-05:00 INFO 22396 --- [demolazyfetch] [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default] 2025-10-12T14:17:59.692-05:00 INFO 22396 --- [demolazyfetch] [ main] org.hibernate.Version : HHH000412: Hibernate ORM core version 6.6.29.Final 2025-10-12T14:17:59.724-05:00 INFO 22396 --- [demolazyfetch] [ main] o.h.c.internal.RegionFactoryInitiator : HHH000026: Second-level cache disabled 2025-10-12T14:17:59.950-05:00 INFO 22396 --- [demolazyfetch] [ main] o.s.o.j.p.SpringPersistenceUnitInfo : No LoadTimeWeaver setup: ignoring JPA class transformer 2025-10-12T14:17:59.973-05:00 INFO 22396 --- [demolazyfetch] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting... 2025-10-12T14:18:00.114-05:00 INFO 22396 --- [demolazyfetch] [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:9dfd436a-e6f6-47b3-8f35-ee35bcb3571b user=SA 2025-10-12T14:18:00.115-05:00 INFO 22396 --- [demolazyfetch] [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed. 2025-10-12T14:18:00.161-05:00 INFO 22396 --- [demolazyfetch] [ main] org.hibernate.orm.connections.pooling : HHH10001005: Database info: Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)'] Database driver: undefined/unknown Database version: 2.3.232 Autocommit mode: undefined/unknown Isolation level: undefined/unknown Minimum pool size: undefined/unknown Maximum pool size: undefined/unknown 2025-10-12T14:18:00.654-05:00 INFO 22396 --- [demolazyfetch] [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration) 2025-10-12T14:18:00.681-05:00 DEBUG 22396 --- [demolazyfetch] [ main] org.hibernate.SQL : create table child (id bigint not null, price numeric(38,2), quantity integer not null, parent_id bigint, primary key (id)) Hibernate: create table child (id bigint not null, price numeric(38,2), quantity integer not null, parent_id bigint, primary key (id)) 2025-10-12T14:18:00.685-05:00 DEBUG 22396 --- [demolazyfetch] [ main] org.hibernate.SQL : create table parent (id bigint not null, primary key (id)) Hibernate: create table parent (id bigint not null, primary key (id)) 2025-10-12T14:18:00.686-05:00 DEBUG 22396 --- [demolazyfetch] [ main] org.hibernate.SQL : alter table if exists child add constraint FK7dag1cncltpyhoc2mbwka356h foreign key (parent_id) references parent Hibernate: alter table if exists child add constraint FK7dag1cncltpyhoc2mbwka356h foreign key (parent_id) references parent 2025-10-12T14:18:00.692-05:00 INFO 22396 --- [demolazyfetch] [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default' 2025-10-12T14:18:01.213-05:00 DEBUG 22396 --- [demolazyfetch] [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : 5 mappings in 'requestMappingHandlerMapping' 2025-10-12T14:18:01.287-05:00 DEBUG 22396 --- [demolazyfetch] [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Patterns [/webjars/**, /**] in 'resourceHandlerMapping' 2025-10-12T14:18:01.311-05:00 DEBUG 22396 --- [demolazyfetch] [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : ControllerAdvice beans: 0 @ModelAttribute, 0 @InitBinder, 1 RequestBodyAdvice, 1 ResponseBodyAdvice 2025-10-12T14:18:01.356-05:00 DEBUG 22396 --- [demolazyfetch] [ main] .m.m.a.ExceptionHandlerExceptionResolver : ControllerAdvice beans: 0 @ExceptionHandler, 1 ResponseBodyAdvice 2025-10-12T14:18:01.391-05:00 INFO 22396 --- [demolazyfetch] [ main] o.s.b.a.h2.H2ConsoleAutoConfiguration : H2 console available at '/h2-console'. Database available at 'jdbc:h2:mem:9dfd436a-e6f6-47b3-8f35-ee35bcb3571b' 2025-10-12T14:18:01.471-05:00 INFO 22396 --- [demolazyfetch] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/' 2025-10-12T14:18:01.476-05:00 INFO 22396 --- [demolazyfetch] [ main] o.j.z.d.d.DemolazyfetchApplication : Started DemolazyfetchApplication in 3.484 seconds (process running for 3.758)
Once the server is up, run the TestData.sql outlined at step 2.4 to prepare the test data.
8.2 Test DemoController for LazyInititionException
In this step, I will send Get http://localhost:8080/demo/1 to capture the server log.
Server.log
2025-10-12T13:24:49.852-05:00 DEBUG 9384 --- [demolazyfetch] [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet : GET "/demo/1", parameters={}
2025-10-12T13:24:49.853-05:00 DEBUG 9384 --- [demolazyfetch] [nio-8080-exec-4] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.jcg.zheng.demo.demolazyfetch.rest.DemoController#getOrder(Long)
2025-10-12T13:24:49.854-05:00 DEBUG 9384 --- [demolazyfetch] [nio-8080-exec-4] org.hibernate.SQL : select p1_0.id from parent p1_0 where p1_0.id=?
Hibernate: select p1_0.id from parent p1_0 where p1_0.id=?
2025-10-12T13:24:49.856-05:00 DEBUG 9384 --- [demolazyfetch] [nio-8080-exec-4] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Using 'application/json', given [application/json] and supported [application/json, application/*+json]
2025-10-12T13:24:49.857-05:00 DEBUG 9384 --- [demolazyfetch] [nio-8080-exec-4] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Writing [org.jcg.zheng.demo.demolazyfetch.entity.Parent@3ff5b054]
2025-10-12T13:24:49.859-05:00 WARN 9384 --- [demolazyfetch] [nio-8080-exec-4] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: failed to lazily initialize a collection of role: org.jcg.zheng.demo.demolazyfetch.entity.Parent.children: could not initialize proxy - no Session]
2025-10-12T13:24:49.860-05:00 DEBUG 9384 --- [demolazyfetch] [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet : Completed 500 INTERNAL_SERVER_ERROR
2025-10-12T13:24:49.860-05:00 DEBUG 9384 --- [demolazyfetch] [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet : "ERROR" dispatch for GET "/error", parameters={}
2025-10-12T13:24:49.860-05:00 DEBUG 9384 --- [demolazyfetch] [nio-8080-exec-4] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2025-10-12T13:24:49.860-05:00 DEBUG 9384 --- [demolazyfetch] [nio-8080-exec-4] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Found 'Content-Type:application/json' in response
2025-10-12T13:24:49.861-05:00 DEBUG 9384 --- [demolazyfetch] [nio-8080-exec-4] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Writing [{timestamp=Sun Oct 12 13:24:49 CDT 2025, status=500, error=Internal Server Error, path=/demo/1}]
2025-10-12T13:24:49.861-05:00 DEBUG 9384 --- [demolazyfetch] [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet : Exiting from "ERROR" dispatch, status 500
- Line 6: trying to write to
ParentEntity class as the Rest response. - Line 7: failed to write to JSON when accessing the
getChildrenas it is a lazy-loading field.
8.3 Test OrderController for DTO
In this step, I will send GET http://localhost:8080/order/dto/1 to capture the Rest response.
http://localhost:8080/order/dto/1 Response
{
"id": 1,
"orderItems": [
{
"id": 1,
"order": null,
"price": 10.50,
"quantity": 2
},
{
"id": 2,
"order": null,
"price": 20.60,
"quantity": 1
}
]
}- DTO is returned in the Rest response – the lazy-loading field is accessed inside the
OrderServiceand mapped to DTO via mapper.
8.4 Test OrderController for ParentView Interface
In this step, I will send GET http://localhost:8080/order/view/1 and capture the Rest response.
http://localhost:8080/order/view/1 Response
{
"id": 1
}- The lazy-loaded fields are not included in the Rest response as the
ParentViewdoes not include it.
8.5 Update Parent Entity with @JsonIgnore
In this step, I will uncomment the @JsonIgnore annotation in the Parent.java class defined at step 3.1. Restart the Spring Boot application and send GET http://localhost:8080/demo/1 and capture the Rest response.
http://localhost:8080/demo/1 Response
{
"id": 1
}- The lazy-loaded fields are not included in the Rest response as
@JsonIgnorefiltered it.
9. Conclusion
In this example, I demonstrated three ways to prevent Jackson from accessing un-fetched entities when serializing lazy-loaded fields.
- DTO POJO mapper – this is the cleanest solution, but requires extra mapping logic.
@JsonIgnore– this is simplest if lazy-loading fields are not needed.- Hibernate projection interface – this is another simpler solution if lazy-loading fields are not needed.
10. Download
This was an example of a Gradle project which handled lazy entity field serialization.
You can download the full source code of this example here: Preventing Jackson from Fetching Lazy Entity Fields Example




