如何使用 jpa 规范添加不同的属性以进行查询

Posted

技术标签:

【中文标题】如何使用 jpa 规范添加不同的属性以进行查询【英文标题】:How to add distinct property to query with jpa specification 【发布时间】:2019-02-25 15:02:05 【问题描述】:

我正在使用 jhipster 标准和 jpa 规范来实现一个端点进行研究。

它工作正常,但请继续向我发送副本。

有这个模型的prestations

public class Prestation extends AbstractAuditingEntity implements Serializable 

private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
@SequenceGenerator(name = "sequenceGenerator")
private Long id;

@NotNull
@Column(name = "jhi_label", nullable = false)
private String label;

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

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

@NotNull
@Column(name = "activated", nullable = false)
private boolean activated;

@ManyToOne(optional = false)
@NotNull
@JsonIgnoreProperties("prestations")
private SubCategory subCategory;

@OneToMany(mappedBy = "prestation", cascade = CascadeType.ALL, orphanRemoval = true)
private List<CompanyPrestation> companies = new ArrayList<>();

以及公司与财产的关系

@OneToMany(mappedBy = "company", cascade = CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
private List<CompanyPrestation> prestations = new ArrayList<>();

这里是 CompanySpecification 类,我用来创建我给存储库的规范

public class CompanySpecification extends QueryService<Company> implements Specification<Company> 

private static final long serialVersionUID = 1L;
private CompanyCriteria criteria;

public CompanySpecification(CompanyCriteria criteria) 
    this.criteria = criteria;


@Override
public Predicate toPredicate(Root<Company> roots, CriteriaQuery<?> query, CriteriaBuilder builder) 

    Specification<Company> specification = Specification.where(null);
    if (criteria != null) 
        if (criteria.getName() != null) 
            specification = specification.or(buildStringSpecification(criteria.getName(), Company_.name));
        
        if (criteria.getSiret() != null) 
            specification = specification.or(buildStringSpecification(criteria.getSiret(), Company_.siret));
        
        if (criteria.getCodeAPE() != null) 
            specification = specification.or(buildStringSpecification(criteria.getCodeAPE(), Company_.codeAPE));
        
        if (criteria.getLegalForm() != null) 
            specification = specification.or(buildStringSpecification(criteria.getLegalForm(), Company_.legalForm));
        
        if (criteria.getVille() != null) 
            specification = specification
                    .and(buildReferringEntitySpecification(criteria.getVille(), Company_.address, Address_.ville));
        
        if (criteria.getExercicePlace() != null && !criteria.getExercicePlace().getIn().isEmpty()) 
            Filter<ExercicePlace> exercicePlaceFilter = new Filter<>();
            exercicePlaceFilter.setIn(criteria.getExercicePlace().getIn().stream().map(ExercicePlace::valueOf)
                    .collect(Collectors.toList()));
            specification = specification.and(buildSpecification(exercicePlaceFilter, Company_.exercicePlace));
        
        if (criteria.getCodePostal() != null) 
            specification = specification.and(buildReferringEntitySpecification(criteria.getCodePostal(),
                    Company_.address, Address_.codePostal));
        
        if (criteria.getPrestationsId() != null) 
            specification = specification.and(buildSpecification(criteria.getPrestationsId(),
                    root -> root.join(Company_.prestations, JoinType.LEFT)
                            .join(CompanyPrestation_.prestation, JoinType.LEFT).get(Prestation_.id)));
        
        if (criteria.getCatId() != null) 
            specification = specification.and(buildSpecification(criteria.getCatId(),
                    root -> root.join(Company_.prestations, JoinType.LEFT)
                            .join(CompanyPrestation_.prestation, JoinType.LEFT)
                            .join(Prestation_.subCategory, JoinType.LEFT).join(SubCategory_.category, JoinType.LEFT)
                            .get(Category_.id)));
        
        if (criteria.getSubCatId() != null) 
            specification = specification.and(buildSpecification(criteria.getSubCatId(),
                    root -> root.join(Company_.prestations, JoinType.LEFT)
                            .join(CompanyPrestation_.prestation, JoinType.LEFT)
                            .join(Prestation_.subCategory, JoinType.LEFT).get(SubCategory_.id)));
        
        if (criteria.getPrestationName() != null) 
            specification = specification.or(buildSpecification(criteria.getPrestationName(),
                    root -> root.join(Company_.prestations, JoinType.LEFT)
                            .join(CompanyPrestation_.prestation, JoinType.LEFT).get(Prestation_.label)));
        
        if (criteria.getPriceMinimum() != null || criteria.getPriceMaximum() != null) 
            specification = specification.and((lroot, lquery, lcriteriaBuilder) -> 
                ListJoin<Company, CompanyPrestation> joinCompnayToCompanyPrestations = lroot
                        .join(Company_.prestations, JoinType.LEFT);
                if (criteria.getPriceMinimum() != null && criteria.getPriceMaximum() != null) 
                    return lcriteriaBuilder.between(
                            joinCompnayToCompanyPrestations.get(CompanyPrestation_.pricePerUnit),
                            criteria.getPriceMinimum().getGreaterOrEqualThan(),
                            criteria.getPriceMaximum().getLessOrEqualThan()
                    );
                 else if (criteria.getPriceMinimum() != null) 
                    return lcriteriaBuilder.greaterThanOrEqualTo(
                            joinCompnayToCompanyPrestations.get(CompanyPrestation_.pricePerUnit),
                            criteria.getPriceMinimum().getGreaterOrEqualThan());
                 else 
                    return lcriteriaBuilder.lessThanOrEqualTo(
                            joinCompnayToCompanyPrestations.get(CompanyPrestation_.pricePerUnit),
                            criteria.getPriceMaximum().getLessOrEqualThan());
                
            );
        
    
    return specification.toPredicate(roots, query, builder);

这是我使用它的服务方法

@Transactional(readOnly = true)
public Page<CompanyDTO> findByCriteria(CompanyCriteria criteria, Pageable page) 
    log.debug("find by criteria : , page: ", criteria, page);
    Specification<Company> spec = Specification.where(null);
    CompanySpecification specification =  new CompanySpecification(criteria);
    spec = (Specification<Company>) specification;
    Page<Company> vitrinesPage = companyRepository.findAll(spec, page);
    List<CompanyDTO> list = companyMapper.toDto(vitrinesPage.getContent());
    Page<CompanyDTO> listPage = new PageImpl<>(list, page, vitrinesPage.getTotalElements());
    return listPage;

我在 CriteriaQuery 中找到了一个不同的方法,但我真的不知道如何在我的规范中添加它。

请帮忙!

【问题讨论】:

【参考方案1】:

在你的toPredicate 方法里面,你能做到吗?

@Override
public Predicate toPredicate(Root<Company> roots, CriteriaQuery<?> query, CriteriaBuilder builder) 
    ....
    query.distinct(true);
    return ...;

发件人 > CriteriaQuery#distinct

尽管您尝试做的事情可能不可能,但据说是limitation of JPA

您可以尝试做的是在获取后删除代码中的重复项,或者尝试覆盖 equalshashCode 方法以尝试破解不同的定义?

【讨论】:

谢谢!我没想到在那里做!好吧,我试过了,但我一直在重复!我复制了休眠 sql 请求并在我的 pgAdmin 中执行它,并了解 distinct 正在工作,但我认为重复行不是! companyPrestations 中的 price_per_unitprestation_id 之类的列使行不同 @ouatrahim 哦,当然,如果你将 distinct 应用到 SELECT * . 查询,它就行不通,因为在数据库中所有行都必须是唯一的。您能举例说明您需要什么以及重复项是什么吗?您是否有多行具有相同(price_per_unitprestation_id)的情侣?而你只想要其中一个?也许您可以尝试仅选择那些列,并将 distinct 应用于该结果?然后你会消除重复。或者尝试覆盖 equalshashCode 方法以仅使用您的唯一列,因此如果任何两个实体具有相同的一对,它们将被视为重复。 你是对的!我尝试在 pgAdmin 中使用 *select distinct company. ** 来消除潜在的问题列,并且直接从 db 的结果中不再重复!我还不知道如何用标准查询和规范翻译这个,如果我只选择公司列,公司的陈述是否仍会被映射 @ouatrahim 似乎这是 JPA 中规范功能的限制,check here,你能像我之前所说的那样尝试覆盖equalshashCode 方法吗?也许你可以欺骗 JPA 假设实体与它重复?我不确定它如何检查相等性,也许明天我可以检查源代码。 哦,我们终于找到了解决方案,但真的不性感!我们采用重复的结果并使用 Stream groupingBy 来消除重复!但是它破坏了分页,所以我们使用请求的分页选项通过某种自定义分页实现来欺骗它!再次感谢@buræquete!

以上是关于如何使用 jpa 规范添加不同的属性以进行查询的主要内容,如果未能解决你的问题,请参考以下文章

如何获取 JPA 生成的 SQL 查询?

Spring Data JPA方法定义规范

如何添加到列表的链接以使用 spring-jpa 进行排序?

JPA 中的传递列表命名本机查询

如何对 JPA 查询进行分页

如何在spring data jpa中进行POJO投影以进行本机查询