Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/python-2.7/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 多个关系中按外键划分的最大id组的JPA条件_Java_Hibernate_Jpa_Subquery_Jpa Criteria - Fatal编程技术网

Java 多个关系中按外键划分的最大id组的JPA条件

Java 多个关系中按外键划分的最大id组的JPA条件,java,hibernate,jpa,subquery,jpa-criteria,Java,Hibernate,Jpa,Subquery,Jpa Criteria,我正在使用springboot和springdatajpa开发一个应用程序 以下为相关实体: @Entity public class Certification { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "certification

我正在使用
springboot
springdatajpa
开发一个应用程序

以下为相关实体:

@Entity
public class Certification {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "certification_type_id")
    private CertificationType certificationType;
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "certification", cascade = CascadeType.ALL)
    private Set<CertificationStatus> certificationStatuses;
}
@Entity
public class CertificationType {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Enumerated(EnumType.STRING)
    private Code code;
    private String name;
    private String description;
    ...
    public enum Code {
        ...
    }
}

@Entity
public class CertificationStatus {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @ManyToOne
    @JoinColumn(name = "certification_id")
    private Certification certification;
    @ManyToOne
    @JoinColumn(name = "certification_status_type_id")
    private CertificationStatusType certificationStatusType;
    private String remarks;
}

@Entity
public class CertificationStatusType {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Enumerated(EnumType.STRING)
    private Code code;
    private String name;
    private String description;
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "certificationStatusType", cascade = CascadeType.ALL)
    private Set<CertificationStatus> certificationStatuses;
    
    public enum Code {
        ...
    }
}
组合可以是零(0)到五(5)。因此,我选择应用JPA标准。到目前为止,我已经编写了以下代码:

@Override
public List<CertificationDto> findAll(
        Integer certificationTypeId,
        Integer certificationStatusTypeId,
        LocalDate dateFrom,
        LocalDate dateTo,
        String client) {
            
    var certifications = this.certificationRepository.findAll((root, criteriaQuery, criteriaBuilder) -> {
        var predicates = new ArrayList<Predicate>();
        // This is working
        if (certificationTypeId != null && certificationTypeId > 0) {
            predicates.add(criteriaBuilder.and(criteriaBuilder.equal(root.join(Certification_.certificationType, JoinType.INNER), certificationTypeId)));
        }

        // To do
        if (certificationStatusTypeId != null && certificationStatusTypeId > 0) {
            /*
             * I have no idea how to apply my SQL statement:
             * SELECT * FROM certification_status WHERE id IN (SELECT MAX(id) FROM certification_status GROUP BY certification_id) and certification_status_type_id = ?;
             */
             
             // Not sure what is the direction of the ff codes
            var certificationStatuses = root.join(Certification_.certificationStatuses);
            predicates.add(criteriaBuilder.and(criteriaBuilder.max(certificationStatuses.get(CertificationStatus_.id)))); 
            // It says Cannot resolve method 'and(javax.persistence.criteria.Expression<N>)'
            // Not also sure how to pass in parameter certificationStatusTypeId
        }

        // This is working
        if (dateFrom != null) {
            var offsetDateTimeFrom = OffsetDateTime.of(dateFrom, LocalTime.MIDNIGHT, ZoneOffset.ofHours(8));
            var dateTimeFrom = Date.from(offsetDateTimeFrom.toInstant());
            var offsetDateTimeTo = OffsetDateTime.of(Objects.requireNonNullElse(dateTo, dateFrom), LocalTime.MAX, ZoneOffset.ofHours(8));
            var dateTimeTo = Date.from(offsetDateTimeTo.toInstant());
            predicates.add(criteriaBuilder.and(criteriaBuilder.between(
                    root.get(Certification_.createdAt),
                    criteriaBuilder.literal(dateTimeFrom),
                    criteriaBuilder.literal(dateTimeTo)))
            );
        }

        // This is working
        if (client != null && !client.isBlank()) {
            predicates.add(criteriaBuilder.and(criteriaBuilder.like(criteriaBuilder.lower(root.join(Certification_.client).get(Client_.name)), "%" + client.toLowerCase() + "%")));
        }

        return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
    });
    return certifications.stream()
            .map(this.certificationMapper::fromEntityToDto)
            .collect(Collectors.toUnmodifiableList());
}
select
    // props of Certification
from certification c
inner join certification_type ct on ct.id = c.certification_type_id
inner join client cl on cl.certification_id = c.id
inner join certification_status cs on cs.certification_id = c.id
inner join certification_status_type cst on cst.id = cs.certification_status_type_id
where cs.id in (select max(cs2.id) form certification_status cs2 group by cs2.certification_id) and cs.certification_status_type_id = ?;
此语句正在数据库中运行。但不确定这是否是根据
MAX(id)
按认证分组\u id
其中认证状态\u type\u id=?
选择记录的最佳查询组合

谢谢你的帮助。多谢各位

**更新

这是我的代码:

    if (certificationStatusTypeId != null && certificationStatusTypeId > 0) {
        Subquery<Long> subQuery = criteriaQuery.subquery(Long.class);
        Root<CertificationStatus> subRoot = subQuery.from(CertificationStatus.class);
        subQuery.select(criteriaBuilder.max(subRoot.get(CertificationStatus_.id)))
                .groupBy(subRoot.get(CertificationStatus_.certification).get(Certification_.id));
        predicates.add(criteriaBuilder.and(criteriaBuilder.in(subRoot.get(CertificationStatus_.id)).value(subQuery)));
        predicates.add(criteriaBuilder.and(criteriaBuilder.equal(subRoot.get(CertificationStatus_.certificationStatusType).get(CertificationStatusType_.id), certificationStatusTypeId)));
    }
查看生成的JPQL,它看起来类似于我的SQL语句。我只是不明白为什么路径是无效的

**更新 我道歉!我的SQL语句实际上类似于以下内容:

@Override
public List<CertificationDto> findAll(
        Integer certificationTypeId,
        Integer certificationStatusTypeId,
        LocalDate dateFrom,
        LocalDate dateTo,
        String client) {
            
    var certifications = this.certificationRepository.findAll((root, criteriaQuery, criteriaBuilder) -> {
        var predicates = new ArrayList<Predicate>();
        // This is working
        if (certificationTypeId != null && certificationTypeId > 0) {
            predicates.add(criteriaBuilder.and(criteriaBuilder.equal(root.join(Certification_.certificationType, JoinType.INNER), certificationTypeId)));
        }

        // To do
        if (certificationStatusTypeId != null && certificationStatusTypeId > 0) {
            /*
             * I have no idea how to apply my SQL statement:
             * SELECT * FROM certification_status WHERE id IN (SELECT MAX(id) FROM certification_status GROUP BY certification_id) and certification_status_type_id = ?;
             */
             
             // Not sure what is the direction of the ff codes
            var certificationStatuses = root.join(Certification_.certificationStatuses);
            predicates.add(criteriaBuilder.and(criteriaBuilder.max(certificationStatuses.get(CertificationStatus_.id)))); 
            // It says Cannot resolve method 'and(javax.persistence.criteria.Expression<N>)'
            // Not also sure how to pass in parameter certificationStatusTypeId
        }

        // This is working
        if (dateFrom != null) {
            var offsetDateTimeFrom = OffsetDateTime.of(dateFrom, LocalTime.MIDNIGHT, ZoneOffset.ofHours(8));
            var dateTimeFrom = Date.from(offsetDateTimeFrom.toInstant());
            var offsetDateTimeTo = OffsetDateTime.of(Objects.requireNonNullElse(dateTo, dateFrom), LocalTime.MAX, ZoneOffset.ofHours(8));
            var dateTimeTo = Date.from(offsetDateTimeTo.toInstant());
            predicates.add(criteriaBuilder.and(criteriaBuilder.between(
                    root.get(Certification_.createdAt),
                    criteriaBuilder.literal(dateTimeFrom),
                    criteriaBuilder.literal(dateTimeTo)))
            );
        }

        // This is working
        if (client != null && !client.isBlank()) {
            predicates.add(criteriaBuilder.and(criteriaBuilder.like(criteriaBuilder.lower(root.join(Certification_.client).get(Client_.name)), "%" + client.toLowerCase() + "%")));
        }

        return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
    });
    return certifications.stream()
            .map(this.certificationMapper::fromEntityToDto)
            .collect(Collectors.toUnmodifiableList());
}
select
    // props of Certification
from certification c
inner join certification_type ct on ct.id = c.certification_type_id
inner join client cl on cl.certification_id = c.id
inner join certification_status cs on cs.certification_id = c.id
inner join certification_status_type cst on cst.id = cs.certification_status_type_id
where cs.id in (select max(cs2.id) form certification_status cs2 group by cs2.certification_id) and cs.certification_status_type_id = ?;
更新

这个问题解决了。感谢Thorben Janssen耐心地调查我的问题

这是我的代码的最后一部分:

    if (certificationStatusTypeId != null && certificationStatusTypeId > 0) {
        var certificationStatuses = root.join(Certification_.certificationStatuses);
        Subquery<Long> subQuery = criteriaQuery.subquery(Long.class);
        Root<CertificationStatus> subRoot = subQuery.from(CertificationStatus.class);
        subQuery.select(criteriaBuilder.max(subRoot.get(CertificationStatus_.id)))
                .groupBy(subRoot.get(CertificationStatus_.certification).get(Certification_.id));
        predicates.add(criteriaBuilder.and(certificationStatuses.get(CertificationStatus_.id).in(subQuery)));
        predicates.add(criteriaBuilder.and(criteriaBuilder.equal(certificationStatuses.get(CertificationStatus_.certificationStatusType).get(CertificationStatusType_.id), certificationStatusTypeId)));
    }
if(certificationStatusTypeId!=null&&certificationStatusTypeId>0){
var certificationStatuses=root.join(Certifications_u2;certificationStatuses);
Subquery Subquery=criteriaQuery.Subquery(Long.class);
Root subRoot=subQuery.from(CertificationStatus.class);
subQuery.select(criteriaBuilder.max(subRoot.get(CertificationStatus_uu.id)))
.groupBy(subRoot.get(CertificationStatus_uu.certification).get(certification_u.id));
add(criteriaBuilder.and(CertificationStatus.get(CertificationStatus_uuu.id).in(子查询));
add(criteriaBuilder.and(criteriaBuilder.equal(CertificationStatus.get(CertificationStatus_uu.certificationStatusType).get(certificationStatusType_uu.id),certificationStatusTypeId));
}

以下代码片段创建了一个与SQL语句相同的CriteriaQuery。因为您使用的是Spring数据JPA,所以可以忽略第一个块和最后一行。SpringDataJPA为您提供了它们

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<CertificationStatus> criteriaQuery = cb.createQuery(CertificationStatus.class);
Root<CertificationStatus> root = criteriaQuery.from(CertificationStatus.class);
criteriaQuery.select(root);

Subquery<Long> sub = criteriaQuery.subquery(Long.class);
Root<CertificationStatus> subRoot = sub.from(CertificationStatus.class);
sub.select(cb.max(subRoot.get("id")));
sub.groupBy(subRoot.get("certification").get("id"));

criteriaQuery.where(root.get("id").in(sub));

em.createQuery(criteriaQuery).getResultList();
**更新


定义IN子句时,似乎引用了错误的表。请尝试更换

predicates.add(criteriaBuilder.and(criteriaBuilder.in(subRoot.get(CertificationStatus_.id)).value(subQuery)));


我的错!我发布的SQL语句只是Biger语句的一部分。我的
选择。。。from
实际上是主实体,
认证
。查看我的实体。我将更新我的帖子。我已更新我的帖子。结果应该是
认证列表
。考虑到关系
@manytomy
,将包括
证书状态的列表。看起来您在创建IN子句时引用了错误的根接口。请看一下我的最新消息,谢谢!我根据你的建议更新了代码。它给了我一个更新下一行的想法:
predicates.add(criteriaBuilder.and(criteriaBuilder.equal)(subRoot.get(CertificationStatus_uu.certificationStatusType).get(certificationStatusType_uu.id),certificationStatusTypeId))
to
predicates.add(criteriaBuilder.and(criteriaBuilder.equal(certificationStatuses.get(CertificationStatus_u.certificationStatusType).get(certificationStatusType_u.id),certificationStatusTypeId))。它现在正在发挥魅力。
select
    certificat0_.id as id1_4_,
    certificat0_.certification_id as certific3_4_,
    certificat0_.certification_status_type_id as certific4_4_,
    certificat0_.remarks as remarks2_4_ 
from
    CertificationStatus certificat0_ 
where
    certificat0_.id in (
        select
            max(certificat1_.id) 
        from
            CertificationStatus certificat1_ 
        group by
            certificat1_.certification_id
    )
predicates.add(criteriaBuilder.and(criteriaBuilder.in(subRoot.get(CertificationStatus_.id)).value(subQuery)));
    predicates.add(criteriaBuilder.and(certificationStatuses.get(CertificationStatus_.id).in(subQuery)));