首页 > 解决方案 > Spring Boot ModelMapper - 无会话

问题描述

我正在尝试编写一个 RestAPI。我的数据库充满了数据(所考虑的表大约有 90000 行)。我想选择一些数据(大约 500 个)。为了性能,我想做这个多线程。没有线程一切正常但是如果我使用 ThreadpoolExecutor 来使用线程并且在所有线程完成后我想将获取的行列表作为 DTo 返回给控制器,我会得到异常:

org.hibernate.LazyInitializationException: could not initialize proxy [com.ghlabs.betpredictorapi.entity.Team#3] - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:170) ~[hibernate-core-5.4.23.Final.jar:5.4.23.Final]
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:310) ~[hibernate-core-5.4.23.Final.jar:5.4.23.Final]
at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:45) ~[hibernate-core-5.4.23.Final.jar:5.4.23.Final]
at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:95) ~[hibernate-core-5.4.23.Final.jar:5.4.23.Final]
at com.ghlabs.betpredictorapi.entity.Team$HibernateProxy$Be4OggaP.getAwayTeamMatches(Unknown Source) ~[classes/:na]
at jdk.internal.reflect.GeneratedMethodAccessor96.invoke(Unknown Source) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.modelmapper.internal.PropertyInfoImpl$MethodAccessor.getValue(PropertyInfoImpl.java:101) ~[modelmapper-2.3.8.jar:na]
at org.modelmapper.internal.MappingEngineImpl.resolveSourceValue(MappingEngineImpl.java:197) ~[modelmapper-2.3.8.jar:na]
at org.modelmapper.internal.MappingEngineImpl.propertyMap(MappingEngineImpl.java:171) ~[modelmapper-2.3.8.jar:na]
at org.modelmapper.internal.MappingEngineImpl.typeMap(MappingEngineImpl.java:152) ~[modelmapper-2.3.8.jar:na]
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:106) ~[modelmapper-2.3.8.jar:na]
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:72) ~[modelmapper-2.3.8.jar:na]
at org.modelmapper.ModelMapper.mapInternal(ModelMapper.java:573) ~[modelmapper-2.3.8.jar:na]
at org.modelmapper.ModelMapper.map(ModelMapper.java:406) ~[modelmapper-2.3.8.jar:na]
at com.ghlabs.betpredictorapi.service.MatchService.convertToMatchDTO(MatchService.java:110) ~[classes/:na]
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195) ~[na:na]
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1654) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) ~[na:na]
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913) ~[na:na]
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578) ~[na:na]
at com.ghlabs.betpredictorapi.service.MatchService.findByLeagueAndSeasonRange(MatchService.java:89) ~[classes/:na]
at com.ghlabs.betpredictorapi.controller.CalculationController.calculateMatch(CalculationController.java:55) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197) ~[spring-web-5.3.1.jar:5.3.1]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141) ~[spring-web-5.3.1.jar:5.3.1]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.3.1.jar:5.3.1]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:893) ~[spring-webmvc-5.3.1.jar:5.3.1]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:807) ~[spring-webmvc-5.3.1.jar:5.3.1]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.1.jar:5.3.1]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1061) ~[spring-webmvc-5.3.1.jar:5.3.1]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:961) ~[spring-webmvc-5.3.1.jar:5.3.1]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.1.jar:5.3.1]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) ~[spring-webmvc-5.3.1.jar:5.3.1]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:652) ~[tomcat-embed-core-9.0.39.jar:4.0.FR]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.1.jar:5.3.1]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) ~[tomcat-embed-core-9.0.39.jar:4.0.FR]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.39.jar:9.0.39]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.1.jar:5.3.1]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.1.jar:5.3.1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.1.jar:5.3.1]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.1.jar:5.3.1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.1.jar:5.3.1]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.1.jar:5.3.1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]

这些是我正在使用的函数(我正在调用 findByLeagueAndSeasonRange-function):

        public List<MatchDTO> findByLeagueAndSeasonRange(Long leagueId, int minSeasonYearEnd, int maxSeasonYearEnd) {
    ExecutorService es = Executors.newFixedThreadPool(10);

    Pageable pageable = PageRequest.of(0,50, Sort.by("matchDate").descending());
    Page<Match> page = matchRepository.findByLeagueAndSeasonRange(leagueId, minSeasonYearEnd, maxSeasonYearEnd, pageable);
    List<Match> matches = new ArrayList<>(page.getContent());

    logger.info("---- Total Pages: " + page.getTotalPages());
    logger.info("Page " + 1 + " - Elements: " + page.getNumberOfElements());

    for (int i = 1; i < page.getTotalPages(); i++) {
        int finalI = i;
        Callable<List<Match>> callable = () -> {
            Pageable pageable2 = PageRequest.of(finalI,50);
            Page<Match> p = matchRepository.findByLeagueAndSeasonRange(leagueId, minSeasonYearEnd, maxSeasonYearEnd, pageable2);

            logger.info("Page " + (finalI + 1)+ " - Elements: " + p.getNumberOfElements());
            return p.getContent();
        };
        Future<List<Match>> future = es.submit(callable);
        try {
            List<Match> futureMatches = future.get();
            matches.addAll(futureMatches);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

    shutdownAndAwaitTermination(es);
    logger.info(matches.size() + " matches fetched from database.");

    return matches.stream()
            .map(this::convertToMatchDTO)
            .collect(Collectors.toList());
}

private void shutdownAndAwaitTermination(ExecutorService pool) {
    pool.shutdown();
    try {
        if (!pool.awaitTermination(30, TimeUnit.SECONDS)) {
            pool.shutdownNow();
            if (!pool.awaitTermination(30, TimeUnit.SECONDS))
                logger.error("Pool did not terminate");
        }
    } catch (InterruptedException ie) {
        pool.shutdownNow();
        Thread.currentThread().interrupt();
    }
}

private MatchDTO convertToMatchDTO(Match match) {
    modelMapper.getConfiguration()
            .setMatchingStrategy(MatchingStrategies.LOOSE);
    return modelMapper
            .map(match, MatchDTO.class);
}

例外中考虑的实体如下:

@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Team implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String teamName;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "fk_league", referencedColumnName = "id", nullable = false)
    private League league;

    @OneToMany(mappedBy="homeTeam")
    List<Match> homeTeamMatches;

    @OneToMany(mappedBy="awayTeam")
    List<Match> awayTeamMatches;

    @Column(name = "created_at", nullable = false, updatable = false)
    private Date createdAt;

    @Column(name = "updated_at", nullable = false)
    private Date updatedAt;

    public Team(String teamName, League league) {
        this.teamName = teamName;
        this.league = league;
    }

    @PrePersist
    private void createdAt() {
        this.createdAt = new Date();
        this.updatedAt = new Date();
    }

    @PreUpdate
    private void updatedAt() {
        this.updatedAt = new Date();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Team team = (Team) o;
        return id.equals(team.id) &&
                teamName.equals(team.teamName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, teamName);
    }

    @Override
    public String toString() {
        return "Team{" +
                "id=" + id +
                ", teamName='" + teamName + '\'' +
                ", league=" + league +
                ", homeTeamMatches=" + homeTeamMatches +
                ", awayTeamMatches=" + awayTeamMatches +
                ", createdAt=" + createdAt +
                ", updatedAt=" + updatedAt +
                '}';
    }
}

这是我从数据库中获取的实体:

@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "matches")
public class Match {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private Date matchDate;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "fk_homeTeam", referencedColumnName = "id", nullable = false)
    private Team homeTeam;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "fk_awayTeam", referencedColumnName = "id", nullable = false)
    private Team awayTeam;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "fk_league", referencedColumnName = "id", nullable = false)
    private League league;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "fk_season", referencedColumnName = "id", nullable = false)
    private Season season;

    private int homeTeamFTGoals;
    private int awayTeamFTGoals;
    private int homeTeamHTGoals;
    private int awayTeamHTGoals;
    private boolean homeTeamFTWins;
    private boolean awayTeamFTWins;
    private boolean homeTeamHTWins;
    private boolean awayTeamHTWins;
    private boolean drawFT;
    private boolean drawHT;
    private int homeTeamShots;
    private int awayTeamShots;
    private int homeTeamShotsOnTarget;
    private int awayTeamShotsOnTarget;
    private int homeTeamHitWoodWork;
    private int awayTeamHitWoordWork;
    private int homeTeamCorners;
    private int awayTeamCorners;
    private int homeTeamFoulsCommitted;
    private int awayTeamFouldCommitted;
    private int homeTeamFreekicksConceeded;
    private int awayTeamFreekicksConceeded;
    private int homeTeamOffsides;
    private int awayTeamOffsides;
    private int homeTeamYellowCards;
    private int awayTeamYellowCards;
    private int homeTeamRedCards;
    private int awayTeamRedCards;

    @Column(name = "created_at", nullable = false, updatable = false)
    private Date createdAt;

    @Column(name = "updated_at", nullable = false)
    private Date updatedAt;

    public Match(League league, Season season, Date matchDate, Team homeTeam, Team awayTeam, int fthg, int ftag, int hthg, int htag, boolean b, boolean b1, boolean b2, boolean b3, boolean b4, boolean b5, int hs, int as, int hst, int ast, int hhw, int ahw, int hc, int ac, int hf, int af, int hfkc, int afkc, int ho, int ao, int hy, int ay, int hr, int ar) {
        this.league = league;
        this.season = season;
        this.matchDate = matchDate;
        this.homeTeam = homeTeam;
        this.awayTeam = awayTeam;
        this.homeTeamFTGoals = fthg;
        this.awayTeamFTGoals = ftag;
        this.homeTeamHTGoals = hthg;
        this.awayTeamHTGoals = htag;
        this.homeTeamFTWins = b;
        this.awayTeamFTWins = b1;
        this.homeTeamHTWins = b2;
        this.awayTeamHTWins = b3;
        this.drawFT = b4;
        this.drawHT = b5;
        this.homeTeamShots = hs;
        this.awayTeamShots = as;
        this.homeTeamShotsOnTarget = hst;
        this.awayTeamShotsOnTarget = ast;
        this.homeTeamHitWoodWork = hhw;
        this.awayTeamHitWoordWork = ahw;
        this.homeTeamCorners = hc;
        this.awayTeamCorners = ac;
        this.homeTeamFoulsCommitted = hf;
        this.awayTeamFouldCommitted = af;
        this.homeTeamFreekicksConceeded = hfkc;
        this.awayTeamFreekicksConceeded = afkc;
        this.homeTeamOffsides = ho;
        this.awayTeamOffsides = ao;
        this.homeTeamYellowCards = hy;
        this.awayTeamYellowCards = ay;
        this.homeTeamRedCards = hr;
        this.awayTeamRedCards = ar;
    }

    @PrePersist
    private void createdAt() {
        this.createdAt = new Date();
        this.updatedAt = new Date();
    }

    @PreUpdate
    private void updatedAt() {
        this.updatedAt = new Date();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Match match = (Match) o;
        return id.equals(match.id) &&
                matchDate.equals(match.matchDate) &&
                homeTeam.equals(match.homeTeam) &&
                awayTeam.equals(match.awayTeam) &&
                Objects.equals(league, match.league) &&
                Objects.equals(season, match.season);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, matchDate, homeTeam, awayTeam, league, season);
    }

    @Override
    public String toString() {
        return "Match{" +
                "id=" + id +
                ", matchDate=" + matchDate +
                ", homeTeam=" + homeTeam +
                ", awayTeam=" + awayTeam +
                ", league=" + league +
                ", season=" + season +
                '}';
    }
}

任何人都可以帮助我吗?

标签: javaspring-bootrestjpamodelmapper

解决方案


推荐阅读