-1

I'm trying to make a database based API I have a huge load of data but every time I try to bring the data even with pageable property I get this error:

{
    "timestamp": "2022-01-12T01:34:01.851+00:00",
    "status": 500,
    "error": "Internal Server Error",
    "trace": "org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: br.com.leomanzini.space.flight.news.model.Article.launches, could not initialize proxy - no Session\r\n\tat org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:612)\r\n\tat org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218)\r\n\tat org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:591)\r\n\tat org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149)\r\n\tat org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:387)\r\n\tat java.base/java.lang.Iterable.forEach(Iterable.java:74)\r\n\tat br.com.leomanzini.space.flight.news.dto.ArticlesDTO.<init>(ArticlesDTO.java:65)\r\n\tat br.com.leomanzini.space.flight.news.service.ArticleService.lambda$findAll$0(ArticleService.java:30)\r\n\tat java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)\r\n\tat java.base/java.util.ArrayList$Itr.forEachRemaining(ArrayList.java:1032)\r\n\tat java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)\r\n\tat java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)\r\n\tat java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)\r\n\tat java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)\r\n\tat java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)\r\n\tat java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)\r\n\tat org.springframework.data.domain.Chunk.getConvertedContent(Chunk.java:173)\r\n\tat org.springframework.data.domain.PageImpl.map(PageImpl.java:106)\r\n\tat br.com.leomanzini.space.flight.news.service.ArticleService.findAll(ArticleService.java:30)\r\n\tat br.com.leomanzini.space.flight.news.controller.SpaceFlightsApiController.findAll(SpaceFlightsApiController.java:28)\r\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\r\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\r\n\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\r\n\tat java.base/java.lang.reflect.Method.invoke(Method.java:566)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:655)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:764)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1732)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\r\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)\r\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\r\n\tat java.base/java.lang.Thread.run(Thread.java:834)\r\n",
    "message": "failed to lazily initialize a collection of role: br.com.leomanzini.space.flight.news.model.Article.launches, could not initialize proxy - no Session",
    "path": "/articles"
}

At the database I have an amount of 11795 articles and their respectives relationship with others tables, there is a way to optimize this load or just make it works?

Article entity:

import lombok.*;

import javax.persistence.*;
import java.util.List;

@Entity
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "article")
public class Article {

    @Id
    @EqualsAndHashCode.Include
    private Long id;

    @Column(nullable = false)
    private Boolean featured;

    @Column(nullable = false)
    private String title;

    @Column(nullable = false)
    private String url;

    @Column(name = "image_url", nullable = false)
    private String imageUrl;

    @Column(name = "news_site", nullable = false)
    private String newsSite;

    @Column(nullable = false)
    private String summary;

    @Column(name = "published_at", nullable = false)
    private String publishedAt;

    @ManyToMany(fetch = FetchType.LAZY)
    private List<Launches> launches;

    @ManyToMany(fetch = FetchType.LAZY)
    private List<Events> events;

    @Column(name = "inserted_by_human")
    private Boolean insertedByHuman = false;
}

Events and Launches entities:

import lombok.*;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Events {

    @Id
    @EqualsAndHashCode.Include
    private Long id;

    private String provider;
}

import lombok.*;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Launches {

    @Id
    @EqualsAndHashCode.Include
    private String id;

    private String provider;
}

The repository is just a interface with JpaRepository and the Article as entity.

The service class:

import br.com.leomanzini.space.flight.news.dto.ArticlesDTO;
import br.com.leomanzini.space.flight.news.model.Article;
import br.com.leomanzini.space.flight.news.repository.ArticleRepository;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

@Service
@AllArgsConstructor
public class ArticleService {

    @Autowired
    private final ArticleRepository articleRepository;

    public Page<ArticlesDTO> findAll(Pageable pageable) {
        Page<Article> articleList = articleRepository.findAll(pageable);
        return articleList.map(article -> new ArticlesDTO(article));
    }
}

If I just use my entity as a return, the methods work, but when I convert the same to dto the application breaks. The dto class is below:

import br.com.leomanzini.space.flight.news.model.Article;
import lombok.*;

import javax.management.ConstructorParameters;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ArticlesDTO implements Serializable {

    @NotEmpty
    @EqualsAndHashCode.Include
    private Long id;

    @NotEmpty
    private String title;

    @NotEmpty
    private String url;

    @NotEmpty
    private String imageUrl;

    @NotEmpty
    private String newsSite;

    @NotEmpty
    private String summary;

    @NotEmpty
    private String publishedAt;

    @NotEmpty
    private String updatedAt;

    @NotEmpty
    private Boolean featured;

    @Valid
    private List<LaunchesDTO> launches;

    @Valid
    private List<EventsDTO> events;

    public ArticlesDTO (Article article) {
        id = article.getId();
        title = article.getTitle();
        url = article.getUrl();
        imageUrl = article.getImageUrl();
        newsSite = article.getNewsSite();
        summary = article.getSummary();
        publishedAt = article.getPublishedAt();
        updatedAt = article.getPublishedAt();
        featured = article.getFeatured();
        launches = new ArrayList<>();
        article.getLaunches().forEach(launch -> {
            LaunchesDTO launchesDTO = new LaunchesDTO(launch.getId(), launch.getProvider());
            launches.add(launchesDTO);
        });
        events = new ArrayList<>();
        article.getEvents().forEach(event -> {
            EventsDTO eventsDTO = new EventsDTO(event.getId(), event.getProvider());
            events.add(eventsDTO);
        });
    }
}

There is anyway to fix the error and make it works? Something to accelerate the database load or something else?

2 Answers2

0

The proposed answer was that in the service class, the @Transactional(readOnly = true) attribute has been enabled, it works fine to resolve the error

failed to lazyly initialize a collection of role: br.com . leomanzini.space.flight.news.model.Article.launches, could not initialize proxy.

The method at service class looked like this:

import br.com.leomanzini.space.flight.news.dto.ArticlesDTO;
import br.com.leomanzini.space.flight.news.model.Article;
import br.com.leomanzini.space.flight.news.repository.ArticleRepository;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@AllArgsConstructor
public class ArticleService {

    @Autowired
    private final ArticleRepository articleRepository;

    @Transactional(readOnly = true)
    public Page<ArticlesDTO> findAll(Pageable pageable) {
        Page<Article> articleList = articleRepository.findAll(pageable);
        return articleList.map(article -> new ArticlesDTO(article));
    }
}

And the requisition working fine!

requisition working

Yaqoob Bhatti
  • 1,271
  • 3
  • 14
  • 30
0

With FetchType.LAZY, the query that is used for retrieving those fields will be executed only when it is accessed for the first time. In order to do this, Hibernate creates and configures proxy classes for our entity classes (that's why we also should not declare our entity classes to be final if we want to use the lazy load feature).

When Hibernate initializes proxies, it requires a Session in order to do the task. That's why when you don't add the @Transactional annotation to the service method, it will throw out an LazyInitializationException...no Session.

Beside the solution above, if you don't want to add the @Transactional annotation, you can set hibernate.enable_lazy_load_no_trans property to true.

<property
name="hibernate.enable_lazy_load_no_trans"
value="true"/>

But this solution is considered to be an anti-pattern. It will create a new Session everytime you try to lazy-load. You can read it more here.
The hibernate.enable_lazy_load_no_trans Anti-Pattern

Long Nguyen
  • 179
  • 1
  • 6