Spring Data JPA: la Creación de la Especificación de la Consulta Recuperar Une

TL;DR: ¿Cómo se replica JPQL la combinación de operaciones de Captura usando las especificaciones en Spring Data JPA?

Estoy tratando de construir una clase que se encargará de consulta dinámica de construcción de entidades JPA usar Spring Data JPA. Para hacer esto, yo soy la definición de una serie de métodos que crear Predicate objetos (como se sugiere en la Spring Data JPA docs y en otros lugares) y, a continuación, el encadenamiento de ellos cuando el correspondiente parámetro de la consulta se somete. Algunos de mis entidades tienen uno-a-muchos de relaciones con otras entidades que ayudan a describir a ellos, que son muy exagerado cuando se consulta y se unieron en colecciones o mapas de DTO creación. Un ejemplo simplificado:

@Entity
public class Gene {

    @Id 
    @Column(name="entrez_gene_id")
    privateLong id;

    @Column(name="gene_symbol")
    private String symbol;

    @Column(name="species")
    private String species;

    @OneToMany(mappedBy="gene", fetch=FetchType.EAGER) 
    private Set<GeneSymbolAlias> aliases;

    @OneToMany(mappedBy="gene", fetch=FetchType.EAGER) 
    private Set<GeneAttributes> attributes;

    //etc...

}

@Entity
public class GeneSymbolAlias {

    @Id 
    @Column(name = "alias_id")
    private Long id;

    @Column(name="gene_symbol")
    private String symbol;

    @ManyToOne(fetch=FetchType.LAZY) 
    @JoinColumn(name="entrez_gene_id")
    private Gene gene;

    //etc...

}

Parámetros de cadena de consulta se pasan de la Controller de la clase a la Service clase como pares clave-valor, donde se procesan y se montan en Predicates:

@Service
public class GeneService {

    @Autowired private GeneRepository repository;
    @Autowired private GeneSpecificationBuilder builder;

    public List<Gene> findGenes(Map<String,Object> params){
        return repository.findAll(builder.getSpecifications(params));
    }

    //etc...

}

@Component
public class GeneSpecificationBuilder {

    public Specifications<Gene> getSpecifications(Map<String,Object> params){
        Specifications<Gene> = null;
        for (Map.Entry param: params.entrySet()){
            Specification<Gene> specification = null;
            if (param.getKey().equals("symbol")){
                specification = symbolEquals((String) param.getValue());
            } else if (param.getKey().equals("species")){
                specification = speciesEquals((String) param.getValue());
            } //etc
            if (specification != null){
               if (specifications == null){
                   specifications = Specifications.where(specification);
               } else {
                   specifications.and(specification);
               }
            }
        } 
        return specifications;
    }

    private Specification<Gene> symbolEquals(String symbol){
        return new Specification<Gene>(){
            @Override public Predicate toPredicate(Root<Gene> root, CriteriaQuery<?> query, CriteriaBuilder builder){
                return builder.equal(root.get("symbol"), symbol);
            }
        };
    }

    //etc...

}

En este ejemplo, cada vez que quiero recuperar un Gene registro, también quiero asociado de GeneAttribute y GeneSymbolAlias registros. Todo esto funciona como se espera, y una solicitud para una sola Gene desencadenará 3 consultas: una por cada uno de los Gene, GeneAttribute, y GeneSymbolAlias tablas.

El problema es que no hay ninguna razón por la que 3 las consultas deben ejecutar para obtener un solo Gene entidad con los atributos y los alias. Esto se puede hacer en formato SQL, y se puede hacer con una consulta JPQL en mi Spring Data JPA repositorio:

@Query(value = "select g from Gene g left join fetch g.attributes join fetch g.aliases where g.symbol = ?1 order by g.entrezGeneId")
List<Gene> findBySymbol(String symbol);

¿Cómo puedo replicar esta estrategia de recuperación con las Especificaciones? He encontrado esta pregunta aquí, pero sólo parece hacer perezoso recupera en con ganas de recuperaciones.

  • Intenta con root.fetch() dentro de toPredicate()? Algo así como root.fetch("attributes", JoinType.LEFT)
  • Que ansiosamente recuperar la attributes, pero todavía se requiere una consulta adicional. Quiero que todos los de las capturas para ser parte de una sola consulta.
  • Sí, pero otra recopilación de aliases debe hacer es: root.fetch("aliases", JoinType.LEFT)
  • He intentado esto antes, como se sugiere en la pregunta que he enlazado, pero no lograr el resultado deseado. El problema no es que la obtención de las entidades vinculadas no se puede hacer con una sola especificación de la consulta, el problema es que la única especificación de la consulta de las necesidades de 3 consultas SQL para obtener esas entidades, lo cual es totalmente innecesario.
  • No lo entendía del todo? Lo que quiere exactamente que necesita la Lista de gen de la entidad con el conjunto de alises y atributo por escrito las especificaciones? En caso de que desee de la Lista de Genes con la especificación para que yo pueda darle la solución más adecuada?
  • he escrito una biblioteca para buscar en una entidad con parámetros, crea consultas hql el uso de objetos de parámetro. yo use el parámetro clases en lugar de hashmap. github.com/ekremucar/hqlplus si se establece una propiedad del objeto de parámetro será añadido a la cláusula where. puede establecer el tipo de búsqueda (como, eq) y selecciona obtener el tipo de alias.

InformationsquelleAutor woemler | 2015-03-30

3 Kommentare

  1. 20

    Especificación de la clase:

    public class MatchAllWithSymbol extends Specification<Gene> {
        private String symbol;
    
        public CustomSpec (String symbol) {
        this.symbol = symbol;
        }
    
        @Override
        public Predicate toPredicate(Root<Gene> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
    
            //This part allow to use this specification in pageable queries
            //but you must be aware that the results will be paged in   
            //application memory!
            Class clazz = query.getResultType();
            if (clazz.equals(Long.class) || clazz.equals(long.class))
                return null;
    
            //building the desired query
            root.fetch("aliases", JoinType.LEFT);
            root.fetch("attributes", JoinType.LEFT);
            query.distinct(true);        
            query.orderBy(cb.asc(root.get("entrezGeneId")));
            return cb.equal(root.get("symbol"), symbol);
        }
    }

    Uso:

        List<Gene> list = GeneRepository.findAll(new MatchAllWithSymbol("Symbol"));
    • Buen consejo sobre cómo hacer una especificación de trabajar con SDJPA paginable consultas. +1.
  2. 6

    Puede especificar el join fetch, mientras que la creación de la Especificación, pero desde la misma especificación será utilizado por paginable métodos también
    como findAll(Especificación var1, Paginable var2) y, a contar de la consulta se quejan porque de join fetch. Por lo tanto, para controlar que podemos comprobar el resultType de CriteriaQuery y aplicar participar sólo si no es Mucho (tipo de resultado para el conde de consulta). vea a continuación el código:

        public static Specification<Item> findByCustomer(Customer customer) {
        return (root, criteriaQuery, criteriaBuilder) -> {
            /*
                Join fetch should be applied only for query to fetch the "data", not for "count" query to do pagination.
                Handled this by checking the criteriaQuery.getResultType(), if it's long that means query is
                for count so not appending join fetch else append it.
             */
            if (Long.class != criteriaQuery.getResultType()) {
                root.fetch(Person_.itemInfo.getName(), JoinType.LEFT);
            }
            return criteriaBuilder.equal(root.get(Person_.customer), customer);
        };
    }
  3. 3

    Sugiero esta biblioteca para la especificación.
    https://github.com/tkaczmarzyk/specification-arg-resolver

    De esta biblioteca : https://github.com/tkaczmarzyk/specification-arg-resolver#join-fetch

    Usted puede usar @JoinFetch anotación para especificar las rutas a realizar fetch unirse en. Por ejemplo:

    @RequestMapping("/customers")
    public Object findByOrderedOrFavouriteItem(
            @Joins({
                @Join(path = "orders", alias = "o")
                @Join(path = "favourites", alias = "f")
            })
            @Or({
                @Spec(path="o.itemName", params="item", spec=Like.class),
                @Spec(path="f.itemName", params="item", spec=Like.class)}) Specification<Customer> customersByItem) {
    
        return customerRepo.findAll(customersByItem);
    }

Kommentieren Sie den Artikel

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

Pruebas en línea