Solución:
Parece que la implementación del repositorio personalizado es el camino a seguir por ahora hasta que haya algo similar disponible en los datos de Spring.
Pasé por http://static.springsource.org/spring-data/data-jpa/docs/current/reference/html/repositories.html#repositories.custom-implementations
Aquí está mi implementación que funciona. Sin embargo, sería bueno tener este método disponible directamente en Spring-Data-JPA
Paso 1: interfaz intermedia para comportamiento compartido
public interface CustomQueryDslJpaRepository <T, ID extends Serializable>
extends JpaRepository<T, ID>, QueryDslPredicateExecutor<T> {
/**
* Returns a {@link org.springframework.data.domain.Page} of entities matching the given {@link com.mysema.query.types.Predicate}.
* This also uses provided projections ( can be JavaBean or constructor or anything supported by QueryDSL
* @param constructorExpression this constructor expression will be used for transforming query results
* @param predicate
* @param pageable
* @return
*/
Page<T> findAll(FactoryExpression<T> factoryExpression, Predicate predicate, Pageable pageable);
}
Paso 2: implementación de la interfaz intermedia
public class CustomQueryDslJpaRepositoryImpl<T, ID extends Serializable> extends QueryDslJpaRepository<T, ID>
implements CustomQueryDslJpaRepository<T, ID> {
//All instance variables are available in super, but they are private
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 CustomQueryDslJpaRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager) {
this(entityInformation, entityManager, DEFAULT_ENTITY_PATH_RESOLVER);
}
public CustomQueryDslJpaRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager,
EntityPathResolver resolver) {
super(entityInformation, entityManager);
this.path = resolver.createPath(entityInformation.getJavaType());
this.builder = new PathBuilder<T>(path.getType(), path.getMetadata());
this.querydsl = new Querydsl(entityManager, builder);
}
@Override
public Page<T> findAll(FactoryExpression<T> factoryExpression, Predicate predicate, Pageable pageable) {
JPQLQuery countQuery = createQuery(predicate);
JPQLQuery query = querydsl.applyPagination(pageable, createQuery(predicate));
Long total = countQuery.count();
List<T> content = total > pageable.getOffset() ? query.list(factoryExpression) : Collections.<T> emptyList();
return new PageImpl<T>(content, pageable, total);
}
}
Paso 3: cree una fábrica de repositorios personalizada para reemplazar la predeterminada
public class CustomQueryDslJpaRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable>
extends JpaRepositoryFactoryBean<R, T, I> {
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new CustomQueryDslJpaRepositoryFactory(entityManager);
}
private static class CustomQueryDslJpaRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {
private EntityManager entityManager;
public CustomQueryDslJpaRepositoryFactory(EntityManager entityManager) {
super(entityManager);
this.entityManager = entityManager;
}
protected Object getTargetRepository(RepositoryMetadata metadata) {
return new CustomQueryDslJpaRepositoryImpl<>(getEntityInformation(metadata.getDomainType()), entityManager);
}
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return CustomQueryDslJpaRepository.class;
}
}
}
Paso 4: usa la fábrica de repositorios personalizados
Usando anotación
@EnableJpaRepositories(repositoryFactoryBeanClass=CustomQueryDslJpaRepositoryFactoryBean.class)
O usando XML
<repositories base-package="com.acme.repository" factory-class="com.acme.CustomQueryDslJpaRepositoryFactoryBean" />
Nota: No coloque la interfaz y la implementación del repositorio personalizado en el mismo directorio que el paquete base. Si está colocando, excluirlos del escaneo; de lo contrario, Spring intentará crear frijoles para ellos.
Uso de muestra
public interface UserDemoRepository extends CustomQueryDslJpaRepository<UserDemo, Long>{
}
public class UserDemoService {
@Inject
UserDemoRepository userDemoRepository;
public Page<User> findAll(UserSearchCriteria userSearchCriteria, Pageable pageable) {
QUserDemo user = QUserDemo.userDemo;
return userDemoRepository.findAll(Projections.bean(UserDemo.class, user.id, user.username), UserPredicate.defaultUserSearch(userSearchCriteria), pageable);
}
}
Para versiones más recientes de Spring Data, no pude hacer que la respuesta aceptada funcione sin problemas, pero descubrí que seguir la ruta de los documentos de Spring Data funciona al revisar esa respuesta de la siguiente manera:
1. La interfaz del repositorio
@NoRepositoryBean
public interface QueryDslPredicateAndProjectionExecutor<T, ID extends Serializable>
extends JpaRepository<T, ID>, QueryDslPredicateExecutor<T> {
<PROJ> Page<PROJ> customFindWithProjection(FactoryExpression<PROJ> factoryExpression, Predicate predicate, Pageable pageable);
}
2. La implementación del repositorio
public class QueryDslJpaEnhancedRepositoryImpl<T, ID extends Serializable> extends QueryDslJpaRepository<T, ID>
implements QueryDslPredicateAndProjectionExecutor<T, ID> {
//All instance variables are available in super, but they are private
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 QueryDslJpaEnhancedRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager) {
this(entityInformation, entityManager, DEFAULT_ENTITY_PATH_RESOLVER);
}
public QueryDslJpaEnhancedRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager,
EntityPathResolver resolver) {
super(entityInformation, entityManager, resolver);
this.path = resolver.createPath(entityInformation.getJavaType());
this.builder = new PathBuilder<T>(path.getType(), path.getMetadata());
this.querydsl = new Querydsl(entityManager, builder);
}
@Override
public <PROJ> Page<PROJ> customFindWithProjection(FactoryExpression<PROJ> factoryExpression, Predicate predicate, Pageable pageable) {
JPQLQuery countQuery = createQuery(predicate);
JPQLQuery query = querydsl.applyPagination(pageable, createQuery(predicate));
Long total = countQuery.count();
List<PROJ> content = total > pageable.getOffset() ? query.list(factoryExpression) : Collections.<PROJ>emptyList();
return new PageImpl<PROJ>(content, pageable, total);
}
}
3. Configuración de la implementación del repositorio predeterminada
@EnableJpaRepositories(
repositoryBaseClass=QueryDslJpaEnhancedRepositoryImpl.class,
basePackageClasses=SomeRepository.class)
Para las versiones actuales de Spring Data (1.11.1) y QueryDSL (4), debe cambiar la implementación del método customFindWithProjection de esta manera:
@Override
public <PROJ> Page<PROJ> customFindWithProjection(FactoryExpression<PROJ> factoryExpression, Predicate predicate, Pageable pageable) {
final JPQLQuery<?> countQuery = createCountQuery(predicate);
JPQLQuery<PROJ> query = querydsl.applyPagination(pageable, createQuery(predicate).select(factoryExpression));
long total = countQuery.fetchCount();
List<PROJ> content = pageable == null || total > pageable.getOffset() ? query.fetch() : Collections.<PROJ> emptyList();
return new PageImpl<PROJ>(content, pageable, total);
}
El resto del código sigue siendo el mismo.