2016-05-16 3 views
2

Я пытаюсь использовать QueryDSL со Spring Data JPA, я хочу использовать findAll с разбивкой по страницам, но счет всегда выполняется, также если тип возврата - List. Мне не нужен этот счет, потому что он очень медленный, и я могу потерять преимущество разбивки на страницы.QueryDsl SpringData Jpa findAll, как избежать count()

Любые решения этой проблемы?

Это count(), это требует около 30 секунд на MySQL:

Mysql too slow on simple query between two tables

В любом случае я не хочу повторять счетчик для каждой страницы я требую, этой информация необходима только для первого вызов.

+0

Может пожалуйста, поделитесь кодом с нами? Запрос 'count' обычно является частью разбивки на страницы, поэтому вы можете представить количество общих страниц/результатов в пользовательском интерфейсе. Почему в вашем случае часть 'count' замедляется? – miensol

ответ

4

Поскольку QueryDslPredicateExecutor не поддерживает возврат Slice в качестве возвращаемого значения findAll(Predicate, Pageable), поэтому граф запросов кажется неизбежным. Но вы можете определить новый интерфейс базового репозитория и реализовать метод findAll таким образом, чтобы он не выдавал запрос подсчета для разбивки на страницы. Для начала, вы должны определить интерфейс, который будет использоваться в качестве базового интерфейса для всех остальных Хранилища:

/** 
* Interface for adding one method to all repositories. 
* 
* <p>The main motivation of this interface is to provide a way 
* to paginate list of items without issuing a count query 
* beforehand. Basically we're going to get one element more 
* than requested and form a {@link Page} object out of it.</p> 
*/ 
@NoRepositoryBean 
public interface SliceableRepository<T, ID extends Serializable> 
     extends JpaRepository<T, ID>, 
     QueryDslPredicateExecutor<T> { 

    Page<T> findAll(Predicate predicate, Pageable pageable); 
} 

Затем реализовать этот интерфейс, как:

public class SliceableRepositoryImpl<T, ID extends Serializable> 
     extends QueryDslJpaRepository<T, ID> 
     implements SliceableRepository<T, ID> { 
    private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE; 
    private final EntityPath<T> path; 
    private final PathBuilder<T> builder; 
    private final Querydsl querydsl; 

    public SliceableRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager) { 
     super(entityInformation, entityManager); 
     path = DEFAULT_ENTITY_PATH_RESOLVER.createPath(entityInformation.getJavaType()); 
     this.builder = new PathBuilder<>(path.getType(), path.getMetadata()); 
     this.querydsl = new Querydsl(entityManager, builder); 
    } 

    @Override 
    public Page<T> findAll(Predicate predicate, Pageable pageable) { 
     int oneMore = pageable.getPageSize() + 1; 
     JPQLQuery query = createQuery(predicate) 
       .offset(pageable.getOffset()) 
       .limit(oneMore); 

     Sort sort = pageable.getSort(); 
     query = querydsl.applySorting(sort, query); 

     List<T> entities = query.list(path); 

     int size = entities.size(); 
     if (size > pageable.getPageSize()) 
      entities.remove(size - 1); 

     return new PageImpl<>(entities, pageable, pageable.getOffset() + size); 
    } 
} 

В основном, эта реализация принесла бы еще один элемент, чем запрошенный размер, и использовать результат для построения Page. Тогда вы должны сказать Spring Data использовать эту реализацию в качестве хранилища базового класса:

@EnableJpaRepositories(repositoryBaseClass = SliceableRepositoryImpl.class) 

И, наконец, расширить SliceableRepository в качестве базового интерфейса:

public SomeRepository extends SliceableRepository<Some, SomeID> {} 
+1

Спасибо за ваш ответ, он решает эту проблему, но в конце кажется, что всегда нужна какая-то оптимизация для querydsl + Spring Data JPA, я искал более стандартизованный подход. еще раз спасибо – Etantonio

Смежные вопросы