springframework sort query org jpaspecificationexecutor examples example data jpa spring-data-jpa querydsl

sort - Spring Data JPA y Querydsl para recuperar un subconjunto de columnas usando la proyección bean/constructor



spring data sort (5)

Como una solución temporal (aunque muy fea e ineficiente), simplemente recuperé una Page normal que contenía las entidades de mi repositorio y las asigné manualmente a una proyección en el controlador de la siguiente manera:

@GetMapping(value = "/columns") public Page<ColumnProjection> getColumns(@QuerydslPredicate(root = Column.class) final Predicate predicate, final Pageable pageable) { Page<Column> filteredColumns = columnRepository.findAll(predicate, pageable); List<ColumnProjection> filteredColumnProjections = new ArrayList<>(); filteredColumns.forEach(c -> filteredColumnProjections.add(new ColumnProjectionImpl(c))); return new PageImpl<>(filteredColumnProjections, pageable, filteredColumnProjections.size()); }

Donde ColumnProjectionImpl es una clase que implementa mi interfaz ColumnProjection .

Esta fue la solución más fácil que pude encontrar sin tener que adaptar mi ColumnRepository existente.

Tengo una clase de entidad como abajo:

@Entity public class UserDemo implements Serializable { @Id private Long id; private String username; private String createdBy; @Version private int version; /*** * * Getters and setters */ }


Con Spring Data JPA y Querydsl, ¿cómo obtengo una página de UserDemo con solo las propiedades de id y username ? Necesito utilizar la paginación, así como la búsqueda. En definitiva me gustaría conseguir el mismo resultado que

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

pero con campo limitado de UserDemo poblado.


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.


Para las versiones más recientes de Spring Data, no conseguí que la respuesta aceptada funcionara sin encontrar problemas, pero descubrí que seguir la ruta de los documentos de Spring Data, funciona revisando 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 por defecto

@EnableJpaRepositories( repositoryBaseClass=QueryDslJpaEnhancedRepositoryImpl.class, basePackageClasses=SomeRepository.class)


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.

He pasado 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: Crea 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 la 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 personalizadas en el mismo directorio que el paquete base. Si está colocando, exclúyalos del escaneo, de lo contrario Spring intentará crear beans para ellos.

Uso de la 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); } }


1. CustomJpaRepositoryFactoryBean

import java.io.Serializable; import javax.persistence.EntityManager; import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.RepositoryFactorySupport; public class CustomJpaRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends JpaRepositoryFactoryBean<T, S, ID> { public CustomJpaRepositoryFactoryBean(Class<? extends T> repositoryInterface) { super(repositoryInterface); } protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) { return new CustomJpaRepositoryFactory(entityManager); } }

2. CustomJpaRepositoryFactory

import static org.springframework.data.querydsl.QueryDslUtils.QUERY_DSL_PRESENT; import javax.persistence.EntityManager; import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; import org.springframework.data.jpa.repository.support.QueryDslJpaRepository; import org.springframework.data.jpa.repository.support.SimpleJpaRepository; import org.springframework.data.querydsl.QueryDslPredicateExecutor; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.RepositoryMetadata; public class CustomJpaRepositoryFactory extends JpaRepositoryFactory { public CustomJpaRepositoryFactory(EntityManager entityManager) { super(entityManager); } @Override protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) { if(QUERY_DSL_PRESENT) { Class<?> repositoryInterface = metadata.getRepositoryInterface(); if(CustomQueryDslPredicateExecutor.class.isAssignableFrom(repositoryInterface)) { return CustomQueryDslJpaRepository.class; } else if(QueryDslPredicateExecutor.class.isAssignableFrom(repositoryInterface)) { return QueryDslJpaRepository.class; } } return SimpleJpaRepository.class; } }

3. CustomQueryDslJpaRepository

import java.io.Serializable; import javax.persistence.EntityManager; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.support.JpaEntityInformation; import org.springframework.data.jpa.repository.support.QueryDslJpaRepository; import org.springframework.data.jpa.repository.support.Querydsl; import org.springframework.data.querydsl.EntityPathResolver; import org.springframework.data.querydsl.SimpleEntityPathResolver; import com.querydsl.core.QueryResults; import com.querydsl.core.types.EntityPath; import com.querydsl.core.types.FactoryExpression; import com.querydsl.core.types.Predicate; import com.querydsl.core.types.Projections; import com.querydsl.core.types.dsl.PathBuilder; import com.querydsl.jpa.JPQLQuery; public class CustomQueryDslJpaRepository<T, ID extends Serializable> extends QueryDslJpaRepository<T, ID> implements CustomQueryDslPredicateExecutor<T> { private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE; private final Querydsl querydsl; public CustomQueryDslJpaRepository(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager) { this(entityInformation, entityManager, DEFAULT_ENTITY_PATH_RESOLVER); } public CustomQueryDslJpaRepository(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager, EntityPathResolver resolver) { super(entityInformation, entityManager, resolver); EntityPath<T> path = resolver.createPath(entityInformation.getJavaType()); PathBuilder<T> builder = new PathBuilder<T>(path.getType(), path.getMetadata()); this.querydsl = new Querydsl(entityManager, builder); } public <DTO> Page<DTO> findAll(Predicate predicate, Pageable pageable, FactoryExpression<DTO> factoryExpression) { JPQLQuery<DTO> query = createQuery(predicate).select(factoryExpression); querydsl.applyPagination(pageable, query); querydsl.applySorting(pageable.getSort(), query); QueryResults<DTO> queryResults = query.fetchResults(); return new PageImpl<>(queryResults.getResults(), pageable, queryResults.getTotal()); } }

4. CustomQueryDslPredicateExecutor

import com.querydsl.core.types.FactoryExpression; import com.querydsl.core.types.Predicate; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.querydsl.QueryDslPredicateExecutor; public interface CustomQueryDslPredicateExecutor<T> extends QueryDslPredicateExecutor<T> { <DTO> Page<DTO> findAll(Predicate predicate, Pageable pageable, FactoryExpression<DTO> factoryExpression); }

5. ejemplo

@EnableJpaRepositories( ... repositoryFactoryBeanClass = CustomJpaRepositoryFactoryBean.class ) public interface ProductRepository extends JpaRepository<Product, Long> implements CustomQueryDslPredicateExecutor<Product> { }