Error getting information from related entities - JAVA, SPRINGBOOT, JPA

433 views Asked by At

I hope you are well and can help me with a problem.
I leave my initial code parts here:

Product Entity:

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "products")

public class ProductEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String brand;
private String description;
private String price;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category_id")
private ProductCategoryEntity category;
private String stock;
@OneToMany(mappedBy = "product", cascade = CascadeType.ALL)
private List<ProductCommentsEntity> productComments;

}

Category Product Entity:

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "categories")
public class ProductCategoryEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "category", cascade = CascadeType.ALL)
private List<ProductEntity> products;

public ProductCategoryEntity(Long id, String name) {
    this.id = id;
    this.name = name;
    }
}

Comments Product Entity:

@Entity
@Data
@Table(name = "comments")
public class ProductCommentsEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String tittle;
private String comment;
private Integer rating;
private Date date;
@ManyToOne
private UserEntity user;
@ManyToOne
@JoinColumn(name = "product_id")
@JsonIgnore
private ProductEntity product;

}

So using JPA, Java and Spring Boot Repositories, I want to implement this method:

@Override
public List<Product> getAllProducts() {
    List<ProductEntity> productsEntity = this.productRepositoryJpa.findAll();
    return this.objectConverter.converterToTypeList(productsEntity, Product.class);
}

But then I get the following error:

In Postman this appears: Error: Transferred a partial file

and in the Intellij console the following appears:

2023-10-23 17:50:02.975  INFO 10556 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 0 ms
Hibernate: select productent0_.id as id1_5_, productent0_.brand as brand2_5_, productent0_.category_id as category7_5_, productent0_.description as descript3_5_, productent0_.name as name4_5_, productent0_.price as price5_5_, productent0_.stock as stock6_5_ from products productent0_
Hibernate: select productcat0_.id as id1_2_0_, productcat0_.name as name2_2_0_ from categories productcat0_ where productcat0_.id=?
Hibernate: select products0_.category_id as category7_5_0_, products0_.id as id1_5_0_, products0_.id as id1_5_1_, products0_.brand as brand2_5_1_, products0_.category_id as category7_5_1_, products0_.description as descript3_5_1_, products0_.name as name4_5_1_, products0_.price as price5_5_1_, products0_.stock as stock6_5_1_ from products products0_ where products0_.category_id=?
Hibernate: select productcom0_.product_id as product_6_3_0_, productcom0_.id as id1_3_0_, productcom0_.id as id1_3_1_, productcom0_.comment as comment2_3_1_, productcom0_.date as date3_3_1_, productcom0_.product_id as product_6_3_1_, productcom0_.rating as rating4_3_1_, productcom0_.tittle as tittle5_3_1_, productcom0_.user_id as user_id7_3_1_, userentity1_.id as id1_7_2_, userentity1_.city as city2_7_2_, userentity1_.country as country3_7_2_, userentity1_.extra_information as extra_in4_7_2_, userentity1_.number as number5_7_2_, userentity1_.postal_code as postal_c6_7_2_, userentity1_.street as street7_7_2_, userentity1_.creation_date as creation8_7_2_, userentity1_.email as email9_7_2_, userentity1_.identification_card as identif10_7_2_, userentity1_.last_name as last_na11_7_2_, userentity1_.name as name12_7_2_, userentity1_.password as passwor13_7_2_, userentity1_.phone as phone14_7_2_, userentity1_.role_id as role_id16_7_2_, userentity1_.state as state15_7_2_, roleentity2_.id as id1_6_3_, roleentity2_.name as name2_6_3_ from comments productcom0_ left outer join users userentity1_ on productcom0_.user_id=userentity1_.id left outer join roles roleentity2_ on userentity1_.role_id=roleentity2_.id where productcom0_.product_id=?
2023-10-23 17:50:03.212  WARN 10556 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Failure while trying to resolve exception [org.springframework.http.converter.HttpMessageNotWritableException]

java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
at org.apache.catalina.connector.ResponseFacade.checkCommitted(ResponseFacade.java:529) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:362) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver.sendServerError(DefaultHandlerExceptionResolver.java:552) ~[spring-webmvc-5.3.30.jar:5.3.30]
at org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver.handleHttpMessageNotWritable(DefaultHandlerExceptionResolver.java:442) ~[spring-webmvc-5.3.30.jar:5.3.30]
at org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver.doResolveException(DefaultHandlerExceptionResolver.java:209) ~[spring-webmvc-5.3.30.jar:5.3.30]
at org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:142) ~[spring-webmvc-5.3.30.jar:5.3.30]
at org.springframework.web.servlet.handler.HandlerExceptionResolverComposite.resolveException(HandlerExceptionResolverComposite.java:80) ~[spring-webmvc-5.3.30.jar:5.3.30]
at org.springframework.web.servlet.DispatcherServlet.processHandlerException(DispatcherServlet.java:1332) ~[spring-webmvc-5.3.30.jar:5.3.30]
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1143) ~[spring-webmvc-5.3.30.jar:5.3.30]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[spring-webmvc-5.3.30.jar:5.3.30]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:965) ~[spring-webmvc-5.3.30.jar:5.3.30]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.30.jar:5.3.30]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.30.jar:5.3.30]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:529) ~[tomcat-embed-core-9.0.80.jar:4.0.FR]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.30.jar:5.3.30]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:623) ~[tomcat-embed-core-9.0.80.jar:4.0.FR]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:209) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-9.0.80.jar:9.0.80]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.30.jar:5.3.30]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.30.jar:5.3.30]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.30.jar:5.3.30]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.30.jar:5.3.30]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.30.jar:5.3.30]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.30.jar:5.3.30]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:168) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:481) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:390) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:926) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1790) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.80.jar:9.0.80]
at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

2023-10-23 17:50:03.220 ERROR 10556 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: com.co.latienda.domain.model.Product["productCategory"]->com.co.latienda.domain.model.ProductCategory["products"]->java.util.ArrayList[0]->com.co.latienda.domain.model.Product["productCategory"]->com.co.latienda.domain.model.Product["productCategory"]->com.co.latienda.domain.model.ProductCategory["products"]->java.util.ArrayList[0]->com.co.latienda.domain.model.Product["productCategory"]->com.co.latienda.domain.model.ProductCategory["products"]->java.util.ArrayList[0]->com.co.latienda.domain.model.Product["productCategory"]->com.co.latienda.domain.model.ProductCategory["products"]->java.util.ArrayList[0]->com.co.latienda.domain.model.Product["productCategory"]->com.co.latienda.domain.model.ProductCategory["products"]->java.util.ArrayList[0]->com.co.latienda.domain.model.Product["productCategory"]->com.co.latienda.domain.model.ProductCategory["products"]->java.util.ArrayList[0]->com.co.latienda.domain.model.Product["productCategory"]->com.co.latienda.domain.model.ProductCategory["products"]->java.util.ArrayList[0]->com.co.latienda.domain.model.Product["productCategory"]->com.co.latienda.domain.model.ProductCategory["products"]->java.util.ArrayList[0]->com.co.latienda.domain.model.Product["productCategory"]->com.co.latienda.domain.model.Product["productCategory"])] with root cause

java.lang.StackOverflowError: null
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:789) ~[jackson-databind-2.13.5.jar:2.13.5]
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) ~[jackson-databind-2.13.5.jar:2.13.5]
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728) ~[jackson-databind-2.13.5.jar:2.13.5]
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:774) ~[jackson-databind-2.13.5.jar:2.13.5]
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178) ~[jackson-databind-2.13.5.jar:2.13.5]

NOTE: I tried to solve it with several solutions that I saw on the internet, I already used the one from @JsonIgnore and the other @Json annotations that they put on the identities.

I also leave some possibly relevant information here.

plugins {
id 'java'
id 'org.springframework.boot' version '2.7.16'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}

group = 'com.co'
version = '0.0.1-SNAPSHOT'

java {
sourceCompatibility = '1.8'
}

configurations {
compileOnly {
    extendsFrom annotationProcessor
}
}

repositories {
mavenCentral()
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.modelmapper:modelmapper:3.2.0'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'org.postgresql:postgresql'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
useJUnitPlatform()
}

I already tried solutions with Spring annotations, such as @JsonIgnore or the others suggested by @Json....
I implemented DTOS, but it was not good enough to solve the problem.

I'm hoping to find a way to fix this bidirectionally related entities issue.

1

There are 1 answers

2
lane.maxwell On

I believe this issue is because you have a circular reference in your mappings that are not being excluded.

That would be, ProductCategoryEntity has a list of ProductEntity, which has a ProductCategoryEntity which has a list of ProductEntity.....

The best way to handle these is to use @JsonBackReference and @JsonManagedReference.

On your ProductCategoryEntity

@JsonManagedReference
@OneToMany(mappedBy = "category", cascade = CascadeType.ALL)
private List<ProductEntity> products;

On your ProductEntity

@JsonBackReference
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category_id")
private ProductCategoryEntity category;

See the documentation for these annotations here