Spring Data JPA:创建规范查询提取连接
2022-09-01 00:36:14
TL;DR:如何使用 Spring Data JPA 中的规范来复制 JPQL Join-Fetch 操作?
我正在尝试构建一个类,该类将使用Spring Data JPA处理JPA实体的动态查询构建。为此,我定义了许多创建对象的方法(例如Spring Data JPA文档和其他地方的建议),然后在提交适当的查询参数时将它们链接起来。我的一些实体与其他实体具有一对多关系,这些实体有助于描述它们,当查询并合并到集合或映射以创建DTO时,这些实体会被热切地获取。一个简化的例子:Predicate
@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...
}
查询字符串参数作为键值对从类传递到类,并在其中处理并组装成:Controller
Service
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...
}
在此示例中,每次要检索记录时,我还希望将其关联和记录。这一切都按预期工作,对单个的请求将触发 3 个查询:分别针对 、 和 表。Gene
GeneAttribute
GeneSymbolAlias
Gene
Gene
GeneAttribute
GeneSymbolAlias
问题是,没有理由需要运行3个查询才能获得具有嵌入式属性和别名的单个实体。这可以在普通的SQL中完成,也可以在我的Spring Data JPA存储库中使用JPQL查询来完成:Gene
@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);
如何使用规范复制此获取策略?我在这里发现了这个问题,但它似乎只会将懒惰的抓取变成急切的抓取。