Spring Data JPA. Cómo obtener una lista de los Identificadores de findAll() método

Tengo una muy complicado modelo. La entidad tiene mucha relación y así sucesivamente.

Trato de usar Spring Data JPA y he preparado un repositorio.

pero cuando invoco una metod findAll() con la especificación para el objeto a tiene un problema de rendimiento debido a que los objetos son muy grandes. Yo lo sé porque cuando me invocar a un método como este:

@Query(value = "select id, name from Customer ")
List<Object[]> myFindCustomerIds();

No tengo problemas con el rendimiento.

Pero cuando invoco

List<Customer> findAll(); 

Tuve un gran problema con el rendimiento.

El problema es que tengo que invocar el método findAll con las Especificaciones del Cliente es por eso que no puedo utilizar el método que devuelve una lista de las matrices de objetos.

Cómo escribir un método para encontrar todos los clientes con las especificaciones del Cliente de la entidad, pero que sólo devuelve un Id.

como este:

List<Long> findAll(Specification<Customer> spec);
  • No puedo usar en este caso la paginación.

Ayuda por favor.

Esto suena como exactamente lo que FetchType.PEREZOSO se pretende resolver.
Es mejor, pero todavía hay un 10 – 15 segundos. De encontrar a todos con la consulta me han resultado de 1-2 segundos. Es posible resolver este problema mediante la Primavera de Datos. Esto significa obtener únicamente el valor de la columna en particular en lugar de todos los objetos?
No me puedo imaginar cómo recuperar toda una fila podría ser significativamente más lento que el de una sola columna. Su esquema de base de datos me aterra! Sin embargo, la fijación es probablemente la pregunta que asumir. Espero que alguien te conteste. Spring JPA es increíblemente flexible y me gustaría pensar que usted podría hacer esto fácilmente con una [email protected] Pero yo nunca lo he hecho personalmente
Mira, esto no es un problema con la base de datos. Puede usted imaginar que, por ejemplo, algunos de proxy entre la aplicación y la base de datos. ¿Puedes ver la diferente cuando se desea transferir una escasa objeto, por ejemplo, con dos campos y cuando se desea transferir un objeto con muchas relaciones y usted no puede usar una recuperación perezosa? Este es el problema.

OriginalEl autor user6778654 | 2015-05-19

4 Kommentare

  1. 25

    ¿Por qué no utilizar el @Query anotación?

    @Query("select p.id from #{#entityName} p")
    List<Long> getAllIds();

    La única desventaja que veo es cuando el atributo id cambios, pero ya que este es un nombre muy común y no es probable que cambie (id = primary key), esto se debe aceptar.

    OriginalEl autor eav

  2. 16

    Este es ahora el apoyo de la Primavera de Datos utilizando Proyecciones:

    interface SparseCustomer {  
    
      String getId(); 
    
      String getName();  
    }

    Que en su Customer repositorio

    List<SparseCustomer> findAll(Specification<Customer> spec);

    EDICIÓN:

    Como señaló Radouane ROUFID las Proyecciones con las Especificaciones actualmente no funciona porque de error.

    Pero puede utilizar especificación-con-proyección biblioteca que soluciones este Spring Data Jpa deficiencia.

    ¿Esto realmente funciona para este caso de uso? Parece que java no se puede distinguir entre este y el método original de JpaSpecificationExecutor<OriginalEntity> extendida por el repositorio y no sé que nombre darle el método en su lugar.
    Las proyecciones no funciona con las especificaciones. JpaSpecificationExecutor devolver sólo un tipo Lista con el agregado de raíz administrado por el repositorio ( List<T> findAll(Specification<T> var1); )

    OriginalEl autor Ondrej Bozek

  3. 7

    He resuelto el problema.

    (Como resultado tendremos una escasa objeto de Cliente sólo con el id y nombre)

    Definir su propio repositorio:

    public interface SparseCustomerRepository {
        List<Customer> findAllWithNameOnly(Specification<Customer> spec);
    }

    Y una aplicación (recordar sobre el sufijo – Impl como predeterminado)

    @Service
    public class SparseCustomerRepositoryImpl implements SparseCustomerRepository {
        private final EntityManager entityManager;
    
        @Autowired
        public SparseCustomerRepositoryImpl(EntityManager entityManager) {
            this.entityManager = entityManager;
        }
    
        @Override
        public List<Customer> findAllWithNameOnly(Specification<Customer> spec) {
            CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
            CriteriaQuery<Tuple> tupleQuery = criteriaBuilder.createTupleQuery();
            Root<Customer> root = tupleQuery.from(Customer.class);
            tupleQuery.multiselect(getSelection(root, Customer_.id),
                    getSelection(root, Customer_.name));
            if (spec != null) {
                tupleQuery.where(spec.toPredicate(root, tupleQuery, criteriaBuilder));
            }
    
            List<Tuple> CustomerNames = entityManager.createQuery(tupleQuery).getResultList();
            return createEntitiesFromTuples(CustomerNames);
        }
    
        private Selection<?> getSelection(Root<Customer> root,
                SingularAttribute<Customer, ?> attribute) {
            return root.get(attribute).alias(attribute.getName());
        }
    
        private List<Customer> createEntitiesFromTuples(List<Tuple> CustomerNames) {
            List<Customer> customers = new ArrayList<>();
            for (Tuple customer : CustomerNames) {
                Customer c = new Customer();
                c.setId(customer.get(Customer_.id.getName(), Long.class));
                c.setName(customer.get(Customer_.name.getName(), String.class));
                c.add(customer);
            }
            return customers;
        }
    }

    OriginalEl autor user6778654

  4. 0

    Lamentablemente Proyecciones no funciona con especificaciones. JpaSpecificationExecutor devolver sólo una Lista mecanografiada con el agregado de raíz administrado por el repositorio ( List<T> findAll(Specification<T> var1); )

    Una real solución es utilizar Tupla. Ejemplo :

        @Override
        public <D> D findOne(Projections<DOMAIN> projections, Specification<DOMAIN> specification, SingleTupleMapper<D> tupleMapper) {
            Tuple tuple = this.getTupleQuery(projections, specification).getSingleResult();
            return tupleMapper.map(tuple);
        }
    
        @Override
        public <D extends Dto<ID>> List<D> findAll(Projections<DOMAIN> projections, Specification<DOMAIN> specification, TupleMapper<D> tupleMapper) {
            List<Tuple> tupleList = this.getTupleQuery(projections, specification).getResultList();
            return tupleMapper.map(tupleList);
        }
    
        private TypedQuery<Tuple> getTupleQuery(Projections<DOMAIN> projections, Specification<DOMAIN> specification) {
    
            CriteriaBuilder cb = entityManager.getCriteriaBuilder();
            CriteriaQuery<Tuple> query = cb.createTupleQuery();
    
            Root<DOMAIN> root = query.from((Class<DOMAIN>) domainClass);
    
            query.multiselect(projections.project(root));
            query.where(specification.toPredicate(root, query, cb));
    
            return entityManager.createQuery(query);
        }

    donde Projections es una interfaz funcional para el usuario root de proyección.

    @FunctionalInterface
    public interface Projections<D> {
    
        List<Selection<?>> project(Root<D> root);
    
    }

    SingleTupleMapper y TupleMapper se utilizan para asignar la TupleQuery resultado para el Objeto que desea devolver.

    @FunctionalInterface
    public interface SingleTupleMapper<D> {
    
        D map(Tuple tuple);
    }
    
    @FunctionalInterface
    public interface TupleMapper<D> {
    
        List<D> map(List<Tuple> tuples);
    
    }

    Ejemplo de uso :

            Projections<User> userProjections = (root) -> Arrays.asList(
                    root.get(User_.uid).alias(User_.uid.getName()),
                    root.get(User_.active).alias(User_.active.getName()),
                    root.get(User_.userProvider).alias(User_.userProvider.getName()),
                    root.join(User_.profile).get(Profile_.firstName).alias(Profile_.firstName.getName()),
                    root.join(User_.profile).get(Profile_.lastName).alias(Profile_.lastName.getName()),
                    root.join(User_.profile).get(Profile_.picture).alias(Profile_.picture.getName()),
                    root.join(User_.profile).get(Profile_.gender).alias(Profile_.gender.getName())
            );
    
            Specification<User> userSpecification = UserSpecifications.withUid(userUid);
    
            SingleTupleMapper<BasicUserDto> singleMapper = tuple -> {
    
                BasicUserDto basicUserDto = new BasicUserDto();
    
                basicUserDto.setUid(tuple.get(User_.uid.getName(), String.class));
                basicUserDto.setActive(tuple.get(User_.active.getName(), Boolean.class));
                basicUserDto.setUserProvider(tuple.get(User_.userProvider.getName(), UserProvider.class));
                basicUserDto.setFirstName(tuple.get(Profile_.firstName.getName(), String.class));
                basicUserDto.setLastName(tuple.get(Profile_.lastName.getName(), String.class));
                basicUserDto.setPicture(tuple.get(Profile_.picture.getName(), String.class));
                basicUserDto.setGender(tuple.get(Profile_.gender.getName(), Gender.class));
    
                return basicUserDto;
            };
    
            BasicUserDto basicUser = findOne(userProjections, userSpecification, singleMapper);

    Espero te sirva de ayuda.

    OriginalEl autor Radouane ROUFID

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea