我想分页和整理电影。但装载很长。 如果他们同时显示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 );
}
答案 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
中(并删除我之前作为抽象示例展示的方法)。
然后将pageNumber
和pageSize
传递给您的MovieDao#findAll(int, int)
方法。