hibernate加载很长(我认为hibernate加载所有项目)

时间:2017-06-03 18:13:37

标签: java spring-mvc pagination

我想分页和整理电影。但装载很长。 如果他们同时显示5或100部电影加载。任何人都有想法更快地做到这一点? 我在DB中有超过100k条目。

@RequestMapping( value = "/movies/page/{pageNumber}", method = org.springframework.web.bind.annotation.RequestMethod.GET )
    public String showPagedMoviePage( HttpServletRequest request, @PathVariable Integer pageNumber, Model model,
                                      @RequestHeader( value = "X-Requested-With", required = false ) String requestedWith ) {
        PagedListHolder<?> pagedListHolder = (PagedListHolder<?>) request.getSession().getAttribute( "movie" );

        int MOVIE_LIST_PAGE_SIZE = 10;

        List<Movie> movies = movieDao.getMovies();

        if ( pagedListHolder == null ) {
            pagedListHolder = new PagedListHolder<>( movies );
            pagedListHolder.setPageSize( MOVIE_LIST_PAGE_SIZE );
        }
        else {
            final int goToPage = pageNumber - 1;
            if ( goToPage <= pagedListHolder.getPageCount() && goToPage >= 0 ) {
                pagedListHolder.setPage( goToPage );
            }
        }

        request.getSession().setAttribute( "movie", pagedListHolder );

        pagedListHolder.setSort( new MutableSortDefinition( "release_date", true, false ) );
        pagedListHolder.resort();

        int current = pagedListHolder.getPage() + 1;
        int begin = Math.max( 1, current - MOVIE_LIST_PAGE_SIZE );
        int end = Math.min( begin + 5, pagedListHolder.getPageCount() );
        int totalPageCount = pagedListHolder.getPageCount();
        String baseUrl = "/movies/page/";

        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        User currentUser = userDao.findOneByUsername( auth.getName() );

        List<UserMovie> UserWatched = currentUser.getUserMovies();

        List<Movie> movieUserWatched = new ArrayList<Movie>();

        for ( UserMovie u : UserWatched ) {
            movieUserWatched.add( u.getMovie() );
        }

        String genreName = "";
        for ( Movie s : movieDao.findAll() ) {
            for ( Genres g : s.getGenres() ) {
                genreName += "" + g.getName() + ", ";
            }
        }
        genreName = genreName.substring( 0, genreName.length() - 1 );

        model.addAttribute( "genre", genreName );
        model.addAttribute( "movieUserWatched", movieUserWatched );

        model.addAttribute( "beginIndex", begin );
        model.addAttribute( "endIndex", end );
        model.addAttribute( "currentIndex", current );
        model.addAttribute( "totalPageCount", totalPageCount );
        model.addAttribute( "baseUrl", baseUrl );
        model.addAttribute( "movies", pagedListHolder );

        return "/home/movies";
    }

MovieDao()类:  @服务     公共课MovieDao         延伸CrudDao {

    @Autowired
    public MovieDao( MovieRepository repository ) {
        super( repository );
    }

    @Override
    public boolean validateOnSaveOrUpdate( Optional<Movie> oldEntity, Movie entity, Errors errors ) {
        if ( entity == null ) {
            return false;
        }

        if ( Strings.isNullOrEmpty( entity.getTitle() ) ) {
            errors.rejectValue( "movie", "movie.messages.movie_empty" );
            return false;
        }

        // Movie are used?
        Movie tmpMovie = findOneByTitle( entity.getTitle() );
        if ( tmpMovie != null && !tmpMovie.getId().equals( entity.getId() ) ) {
            // errors.rejectValue( "movie", "movie.messages.id_already_exists", new Object[] { entity.getTitle() }, null
            // );
            return false;
        }

        return true;
    }

    @Override
    public boolean validateOnDelete( Movie entity, Errors errors ) {
        if ( entity != null ) {
            return true;
        }
        return false;
    }

    public Movie findOneByTitle( String title ) {
        return repository.findOneByTitle( title );
    }

    public Iterable<Movie> findAll() {
        return repository.findAll();
    }

    public ArrayList<Movie> getMovies() {
        Iterable<Movie> source = repository.findAll();
        ArrayList<Movie> target = new ArrayList<Movie>();
        source.forEach( target::add );
        return target;
    }

}

电影模特:
    @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property =“id”)     @实体     公共课电影         扩展AuditedEntity         实现com.miyava.common.Entity {

    @Column( name = "movie_id" )
    @Id
    @GeneratedValue( strategy = GenerationType.AUTO )
    @JsonView( DataTablesOutput.View.class )
    private Long id;

    @Column( name = "theMovieDbId" )
    private Long theMovieDbId;

    @NotEmpty( message = "movie.messages.title_empty" )
    @Column( nullable = false, unique = false, length = 255 )
    @Length( max = 255, message = "common.message.data_to_long" )
    @JsonView( DataTablesOutput.View.class )
    private String title;

    @NotEmpty( message = "movie.messages.description_empty" )
    @Column( nullable = false, unique = false )
    @Lob
    @JsonView( DataTablesOutput.View.class )
    private String overview;

    @Column( nullable = true, unique = false )
    @JsonView( DataTablesOutput.View.class )
    private String short_Overview;

    @NotEmpty( message = "movie.messages.poster_Path_empty" )
    @Column( nullable = false, unique = false, length = 255 )
    @Length( max = 255, message = "common.message.data_to_long" )
    @JsonView( DataTablesOutput.View.class )
    private String poster_path;

    @NotEmpty( message = "movie.messages.runtime_empty" )
    @Column( nullable = false, unique = false, length = 255 )
    @Length( max = 255, message = "common.message.data_to_long" )
    @JsonView( DataTablesOutput.View.class )
    private String runtime;

    @NotEmpty( message = "movie.messages.status_empty" )
    @Column( nullable = false, unique = false, length = 255 )
    @Length( max = 255, message = "common.message.data_to_long" )
    @JsonView( DataTablesOutput.View.class )
    private String status;

    @Column( unique = false, columnDefinition = "DATETIME", name = "release_date" )
    @JsonView( DataTablesOutput.View.class )
    private Date release_date;

    @ManyToMany( cascade = CascadeType.ALL, targetEntity = Genres.class )
    @JoinTable( name = "movie_genres", joinColumns = @JoinColumn( name = "movie_id", referencedColumnName = "movie_id" ), inverseJoinColumns = @JoinColumn( name = "genres_id", referencedColumnName = "genres_id" ) )
    @JsonView( DataTablesOutput.View.class )
    private List<Genres> genres;

    @OneToMany( mappedBy = "movie" )
    private List<UserMovie> userMovie = new ArrayList<UserMovie>();

    public Movie() {}

    public Movie( String title, String overview, String short_Overview, String status, Date release_date, List<Genres> genres ) {
        super();
        this.title = title;
        this.overview = overview;
        this.short_Overview = short_Overview;
        this.status = status;
        this.release_date = release_date;
        this.genres = genres;
    }

    public Long getId() {
        return id;
    }

    public void setId( Long id ) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle( String title ) {
        this.title = title;
    }

    public String getOverview() {
        return overview;
    }

    public void setOverview( String overview ) {
        this.overview = overview;
    }

    public String getPoster_path() {
        return poster_path;
    }

    public void setPoster_path( String poster_path ) {
        this.poster_path = poster_path;
    }

    public String getRuntime() {
        return runtime;
    }

    public void setRuntime( String runtime ) {
        this.runtime = runtime;
    }

    public String getStatus() {
        return status;
    }

    public Long getTheMovieDbId() {
        return theMovieDbId;
    }

    public void setTheMovieDbId( Long theMovieDbId ) {
        this.theMovieDbId = theMovieDbId;
    }

    public String getShort_Overview() {
        return short_Overview;
    }

    public void setShort_Overview( String short_Overview ) {
        this.short_Overview = short_Overview;
    }

    public void setStatus( String status ) {
        this.status = status;
    }

    public Date getRelease_date() {
        return release_date;
    }

    public void setRelease_date( Date release_date ) {
        this.release_date = release_date;
    }

    public List<Genres> getGenres() {
        return genres;
    }

    public void setGenres( List<Genres> genres ) {
        this.genres = genres;
    }

    public List<UserMovie> getUserMovie() {
        return userMovie;
    }

    public void setUserMovie( List<UserMovie> userMovie ) {
        this.userMovie = userMovie;
    }
}

Hibernate查询:here

找到MovieDAO

package com.miyava.movie.service;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.*;
import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate5.HibernateCallback;
import org.springframework.stereotype.Service;
import org.springframework.validation.Errors;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.miyava.common.CrudDao;
import com.miyava.movie.model.Movie;
import com.miyava.movie.repository.MovieRepository;

@Service
public class MovieDao
    extends CrudDao<Movie, Long, MovieRepository> {

    @Autowired
    public MovieDao( MovieRepository repository ) {
        super( repository );
    }

    @Override
    public boolean validateOnSaveOrUpdate( Optional<Movie> oldEntity, Movie entity, Errors errors ) {
        if ( entity == null ) {
            return false;
        }

        if ( Strings.isNullOrEmpty( entity.getTitle() ) ) {
            errors.rejectValue( "movie", "movie.messages.movie_empty" );
            return false;
        }

        // Movie are used?
        Movie tmpMovie = findOneByTitle( entity.getTitle() );
        if ( tmpMovie != null && !tmpMovie.getId().equals( entity.getId() ) ) {
            // errors.rejectValue( "movie", "movie.messages.id_already_exists", new Object[] { entity.getTitle() }, null
            // );
            return false;
        }

        return true;
    }

    @Override
    public boolean validateOnDelete( Movie entity, Errors errors ) {
        if ( entity != null ) {
            return true;
        }
        return false;
    }

    public Movie findOneByTitle( String title ) {
        return repository.findOneByTitle( title );
    }

    public Iterable<Movie> findAll() {
        return repository.findAll();
    }

    public ArrayList<Movie> getMovies() {
        Iterable<Movie> source = repository.findAll();
        ArrayList<Movie> target = new ArrayList<Movie>();
        source.forEach( target::add );
        return target;
    }

    private <E> List<E> findListPage( final String hql, final int offset,
                                      final int limit, final Object... args ) {
        return (List<E>) getHibernateTemplate().execute( new HibernateCallback() {
            public Object doInHibernate( Session session )
                throws HibernateException,
                SQLException {
                Query query = createQueryAndPopulateParameters( session, hql,
                    args );
                if ( offset >= 0 ) {
                    query.setFirstResult( offset );
                }
                if ( limit >= 0 ) {
                    query.setMaxResults( limit );
                }
                return query.list();
            }
        } );
    }

    private Query createQueryAndPopulateParameters( Session session,
                                                    final String hql, final Object... args ) {
        Query query = session.createQuery( hql );
        for ( int i = 0; i < args.length; i++ ) {
            query.setParameter( i, args[i] );
        }
        return query;
    }

    public List<Movie> listMoviesPaged( int offset, int limit ) {
        return findListPage( "from Movie m order by m.id", offset, limit );
    }

}

MovieRepository:

    package com.miyava.movie.repository;

import org.springframework.data.jpa.datatables.repository.DataTablesRepository;

import com.miyava.movie.model.Movie;

public interface MovieRepository
    extends DataTablesRepository<Movie, Long> {

    Movie findOneByTitle( String title );
}

1 个答案:

答案 0 :(得分:1)

是的,您是对的,您的代码从数据库中提取所有Movie条记录,只有然后对它们应用分页。如果你有至少数千条记录(如你的情况那样),这远非最佳。

解决此问题的一种标准方法是将页码(或偏移/限制对)传递给对数据库进行查询的代码。

你没有提供完整的代码(MovieRepository,它似乎实际上执行了一个Hibernate查询,但是没有),所以我将给你一个抽象的例子。

private <E> List<E> findListPage(final String hql, final int offset,
        final int limit, final Object... args) {
    return (List<E>) getHibernateTemplate().execute(new HibernateCallback() {
        public Object doInHibernate(Session session) throws HibernateException,
                SQLException {
            Query query = createQueryAndPopulateParameters(session, hql,
                    args);
            if (offset >= 0) {
                query.setFirstResult(offset);
            }
            if (limit >= 0) {
                query.setMaxResults(limit);
            }
            return query.list();
        }
    });
}

private Query createQueryAndPopulateParameters(Session session,
        final String hql, final Object... args) {
    Query query = session.createQuery(hql);
    for (int i = 0; i < args.length; i++) {
        query.setParameter(i, args[i]);
    }
    return query;
}

public List<Movie> listMoviesPaged(int offset, int limit) {
    return findListPage("from Movie m order by m.id", offset, limit);
}

我们的想法是我们在Hibernate的Query实例上设置偏移和限制,并允许数据库进行分页工作,而不是将所有100k记录提取到内存中并将大部分记录丢弃。

当然,当有一个order by子句时,你需要确保在你的排序列上有一个索引,否则性能将被杀死。

<强>更新

现在我们有更多代码,我们可以找到一些具体的解决方案。您使用DataTablesRepository扩展PagingAndSortingRepository https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/PagingAndSortingRepository.html,其findAll(Pageable)方法。这似乎是这里所需要的。

尝试替换

public Iterable<Movie> findAll() {
    return repository.findAll();
}

public Iterable<Movie> findAll(int pageNumber, int pageSize) {
    return repository.findAll(new PageRequest(pageNumber, pageSize));
}

MovieDao中(并删除我之前作为抽象示例展示的方法)。

然后将pageNumberpageSize传递给您的MovieDao#findAll(int, int)方法。