JPA Criterios de la API con múltiples parámetros

Necesito hacer un método de búsqueda que utiliza la JPA API de Criterios con varios parámetros.
Ahora el problema es que no todos los parámetros necesarios. Para algunos puede ser nulo, y no deben ser incluidos en la consulta. He intentado esto con el CriteriaBuilder pero yo no podía ver cómo hacer que funcione.

Con la Hibernación de los Criterios de la API de esto es bastante fácil. Basta con crear los criterios y, a continuación, agregar Restricciones.

Criteria criteria = session.createCriteria(someClass.class);
if(someClass.getName() != null) {
   criteria.add(Restrictions.like("name", someClass.getName());
}

¿Cómo podría conseguir el mismo con JPA Criterios del API?

InformationsquelleAutor mokuril | 2012-08-30

5 Kommentare

  1. 79

    Concepto es la construcción de la matriz de javax.la persistencia.Predicado que contiene sólo los predicados que queremos utilizar:

    Ejemplo de entidad que se va a consultar:

    @Entity
    public class A {
        @Id private Long id;    
        String someAttribute;
        String someOtherAttribute;
        ...
    }

    Propia consulta:

        //some parameters to your method
        String param1 = "1";
        String paramNull = null;
    
        CriteriaBuilder qb = em.getCriteriaBuilder();
        CriteriaQuery cq = qb.createQuery();
        Root<A> customer = cq.from(A.class);
    
        //Constructing list of parameters
        List<Predicate> predicates = new ArrayList<Predicate>();
    
        //Adding predicates in case of parameter not being null
        if (param1 != null) {
            predicates.add(
                    qb.equal(customer.get("someAttribute"), param1));
        }
        if (paramNull != null) {
            predicates.add(
                    qb.equal(customer.get("someOtherAttribute"), paramNull));
        }
        //query itself
        cq.select(customer)
                .where(predicates.toArray(new Predicate[]{}));
        //execute query and do something with result
        em.createQuery(cq).getResultList();
    • ¿Cómo conseguir que el em instancia ?
    • em, que es EntityManager, probablemente sólo se autowired en. Su configuración de la clase iba a poner esto en marcha.
  2. 6

    Echa un vistazo a este sitio JPA Criterios API. Hay un montón de ejemplos.

    Actualización: Ofreciendo un ejemplo concreto

    Vamos a buscar las Cuentas con un saldo inferior a un valor específico:

    SELECT a FROM Account a WHERE a.balance < :value

    Crear en primer lugar un Criterio Generador de

    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    
    CriteriaQuery<Account> accountQuery = builder.createQuery(Account.class);
    Root<Account> accountRoot = accountQuery.from(Account.class);
    ParameterExpression<Double> value = builder.parameter(Double.class);
    accountQuery.select(accountRoot).where(builder.lt(accountRoot.get("balance"), value));

    Para obtener el resultado que establezca el parámetro(s) y ejecutar la consulta:

    TypedQuery<Account> query = entityManager.createQuery(accountQuery);
    query.setParameter(value, 1234.5);
    List<Account> results = query.getResultList();

    BTW: El entityManager se inyecta en algún lugar en un EJB/Servicio/DAO.

    • Si yo no establece un parámetro, ¿cómo esto se refleja en la consulta? Pues yo todavía tiene que agregar el resto de los parámetros para el CriteriaQuery. Y si uno de ellos es nula, esto va a dar lugar a resultados inesperados.
  3. 1

    Mikko la respuesta funcionaba a la perfección. Único cambio que tenía que hacer, era para reemplazar:

    cq.select(customer).where(predicates.toArray(new Predicate[]{}));

    con:

    Predicate [] predicatesarr = predicates.toArray(new Predicate[predicates.size()]); 
    cq.select(customer).where(predicatesarr);

    Algún lugar de la conversión de la lista a la matriz en el original no funciona.

  4. 0

    Primero, Mikko la respuesta me llevó a mi respuesta. Upvote para que.

    Mi escenario era el que yo quería a la relación padre/hijo y yo quería encontrar una coincidencia en ~ninguna~ niño.

    Empleado tiene múltiples JobTitle(s).

    Quería encontrar a un empleado (donde el tiene muchos títulos de trabajo), pero encontrar en ~cualquiera de los jobtitles puedo enviar.

    SQL quedaría así:

    Select * from dbo.De los empleados e join dbo.JobTitle jt en el correo.EmployeeKey = jt.EmployeeKey
    DONDE ( jt.JobTitleName = ‘programador’ O jt.JobTitleName = ‘badcop’ )

    Me tiré en el género y fecha de nacimiento para completar el ejemplo (y dar más «opcional») criterios)

    Mi JPA código

    import org.apache.commons.lang3.StringUtils;
    import org.springframework.data.jpa.domain.Specification;
    
    import javax.persistence.criteria.CriteriaBuilder;
    import javax.persistence.criteria.CriteriaQuery;
    import javax.persistence.criteria.Join;
    import javax.persistence.criteria.Predicate;
    import javax.persistence.criteria.Root;
    import java.util.ArrayList;
    import java.util.List;
    
    public class MyEmployeeSpecification implements Specification<MyEmployee> {
    
        private MyEmployee filter;
    
        public MyEmployeeSpecification(MyEmployee filter) {
            super();
            this.filter = filter;
        }
    
        public Predicate toPredicate(Root<MyEmployee> root, CriteriaQuery<?> cq,
                                     CriteriaBuilder cb) {
    
            Predicate returnPred = cb.disjunction();
    
            List<Predicate> patientLevelPredicates = new ArrayList<Predicate>();
    
            if (filter.getBirthDate() != null) {
                patientLevelPredicates.add(
                        cb.equal(root.get("birthDate"), filter.getBirthDate()));
            }
    
            if (filter.getBirthDate() != null) {
                patientLevelPredicates.add(
                        cb.equal(root.get("gender"), filter.getGender()));
            }
    
            if (null != filter.getJobTitles() && filter.getJobTitles().size() > 0) {
    
                List<Predicate> jobTitleLevelPredicates = new ArrayList<Predicate>();
    
                Join<JobTitle, JobTitle> hnJoin = root.join("jobtitles");
    
                for (JobTitle hnw : filter.getJobTitles()) {
                    if (null != hnw) {
                        if (StringUtils.isNotBlank(hnw.getJobTitleName())) {
                            jobTitleLevelPredicates.add(cb.equal(hnJoin.get("getJobTitleName"), hnw.getFamily()));
                        }
                    }
                }
    
                patientLevelPredicates.add(cb.or(jobTitleLevelPredicates.toArray(new Predicate[]{})));
            }
    
            returnPred = cb.and(patientLevelPredicates.toArray(new Predicate[]{}));
    
            return returnPred;
        }
    }

    Pero pensé que la mina porque de predicados.toArray(nuevo Predicado[]{}) , conocido también como la varargs truco. (Gracias Mikko)

    También estoy haciendo el «implementa Specifiction» método.

    Otros enlaces útiles:

    JPA Especificaciones por Ejemplo

    JPA CriteriaBuilder conjunto de criterios en una disyunción de los criterios de

  5. 0

    Una solución simple para la Primavera, el uso de las expresiones lambda:

    Specification<User> specification = (root, query, builder) -> {
        List<Predicate> predicates = new ArrayList<>();
        if (criteria.getName() != null) {
            //like
            predicates.add(builder.like(root.get("name"), "%" + criteria.getName() + "%"));
        }
        if (criteria.getParentId() != 0) {
            //equal
            predicates.add(builder.equal(root.get("parent"), criteria.getParentId()));
        }
    
        //AND all predicates
        return builder.and(predicates.toArray(new Predicate[0]));
    };
    
    repository.findAll(specification);

Kommentieren Sie den Artikel

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

Pruebas en línea