Spring Data JPA: la Création de la Spécification de la Requête d'Extraction des Jointures

TL;DR: Comment avez-vous répliquer JPQL Rejoignez-opérations d'Extraction à l'aide de spécifications dans Spring Data JPA?

Je suis en train de construire une classe qui va gérer les dynamiques de construction des requêtes pour des entités JPA à l'aide de Spring Data JPA. Pour ce faire, je suis à la définition d'un certain nombre de méthodes qui créent Predicate des objets (comme le suggère le Spring Data JPA docs et ailleurs), et ensuite chaîne quand le paramètre de requête est présentée. Certains de mes entités ont un-à-plusieurs relations avec d'autres entités qui aident à décrire, qui sont très récupéré lorsqu'il est interrogé et se fondirent dans des collections ou des cartes pour DTO création. Un exemple simplifié:

@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...

}

Paramètres de chaîne de requête sont transmises à partir du Controller classe à la Service classe comme paires clé-valeur, où ils sont traités et assemblés 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...
}

Dans cet exemple, chaque fois que je veux récupérer un Gene record, je veux aussi ses associés GeneAttribute et GeneSymbolAlias enregistrements. Tout cela fonctionne comme prévu, et une demande pour un seul Gene se déclenche 3 requêtes: un pour le Gene, GeneAttribute, et GeneSymbolAlias tables.

Le problème est qu'il n'y a aucune raison pour que les 3 requêtes doivent exécuter pour obtenir un seul Gene entité incorporées des attributs et des alias. Cela peut être fait dans la plaine SQL, et il peut être fait avec une requête JPQL dans mon Spring Data JPA référentiel:

@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);

Comment puis-je reproduire ce type de stratégies de chargement à l'aide de Spécifications? J'ai trouvé cette question ici, mais il semble que pour rendre paresseux extrait en hâte extrait.

  • Avez-vous essayé avec root.fetch() à l'intérieur de toPredicate()? Quelque chose comme root.fetch("attributes", JoinType.LEFT)
  • Qui seront heureux de récupérer le attributes, mais elle nécessite encore une autre requête. Je veux tous les extractions de faire partie d'une seule requête.
  • Oui, mais une autre extraction de aliases devrait le faire: root.fetch("aliases", JoinType.LEFT)
  • J'ai essayé cela avant, comme il était suggéré dans la question que j'ai lié, mais il ne permet pas d'atteindre le résultat souhaité. Le problème n'est pas que l'extraction des entités liées ne peut être fait avec une seule spécification de la requête, le problème est que la seule spécification de la requête 3 requêtes SQL pour obtenir ces entités, ce qui est totalement inutile.
  • Je n'ai pas tout? Ce que vous voulez exactement que vous avez besoin de la Liste de gènes entité avec l'ensemble des alises et de l'attribut par la rédaction de cahier des charges? Dans le cas où vous voulez la Liste de Gène avec le cahier des charges afin que je puisse vous donner une solution appropriée?
  • j'ai écrit une bibliothèque de recherche sur une entité avec des paramètres, il crée des requêtes hql à l'aide d'objets de paramètre. j'ai utiliser le paramètre classes au lieu de la table de hachage. github.com/ekremucar/hqlplus si vous définissez une propriété de l'objet parameter il sera ajouté à la clause where. vous pouvez définir le type de recherche (comme, eq) et de définir type d'extraction d'alias.

InformationsquelleAutor woemler | 2015-03-30