【问题标题】:JPA Criteria Builder query with inner SELECT statement带有内部 SELECT 语句的 JPA Criteria Builder 查询
【发布时间】:2020-11-08 16:30:44
【问题描述】:

我需要使用 JPA 标准构建器创建以下 SQL 连接条件,

SELECT * FROM student s1
INNER JOIN
(SELECT subject,teacher,MIN(marks) AS marks FROM student GROUP BY subject, teacher) s2
ON s1.subject = s2.subject
AND s1.teacher = s2.teacher
AND s1.marks = s2.marks

下面是实体类和 JPA 查询构建器。

@Entity
@JsonIgnoreProperties(ignoreUnknown = true)
public class Student implements Serializable {
    
    private static final long serialVersionUID = 1L;

    @Id
    @Column(name="id")
    Long id;
    
    @Column(name="name")
    String name;
    
    @Column(name="subject")
    public
    String subject;
    
    @Column(name="teacher")
    String teacher;
    
    @Column(name="marks")
    String marks;
    
    @JsonIgnore
    @ManyToOne
    @JoinColumns({
    @JoinColumn(insertable=false, updatable=false, name="subject",referencedColumnName="subject"),
    @JoinColumn(insertable=false, updatable=false, name="teacher",referencedColumnName="teacher"),
    @JoinColumn(insertable=false, updatable=false, name="marks",referencedColumnName="marks")
    })
    Student studentSelf;
    
    @JsonIgnore
    @OneToMany(cascade = CascadeType.ALL, mappedBy="studentSelf")
    Set<Student> studentref;
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Student> query = cb.createQuery(Student.class);
Root<Student> mainStudent = query.from(Student.class);
List<Predicate> predicates = new ArrayList<>();

Join<Student, Student> studentJoin = mainStudent.join("studentSelf", JoinType.INNER);
        
List<Student> list = entityManager.createQuery(query).getResultList();

我可以使用连接条件构建查询,但无法创建内部 SELECT 查询。如何为联接子句创建内部选择查询?

需求说明:下面是所需的输入表和输出。

【问题讨论】:

  • 一种方法可能是使用WHERE 子句中的相关子查询重写查询以检查最小标记值。
  • 但我正在尝试根据教师和科目的独特组合提取最低分数。相关子查询可能会满足此要求。
  • 我正在尝试了解您的模型和查询的目的,以便可以以不同的方式重新编写它。并且studentSelf 不是@ManyToOne 关系(即subjectteachermarks 不能唯一识别学生)。如果您想将studentRef(代表classmates)保持为单向@OneToMany
  • @KavithakaranKanapathippillai,要求是提取每个科目和教师组合的最低分数。我在查询中添加了要求。

标签: sql spring-boot spring-data-jpa criteria-api self-join


【解决方案1】:

我还没有尝试过这个,它云失败了,但有一个例外。但是你可以试一试吗?

  • 如果您只是为此查询添加了studentSelfstudentRef,请删除它们。 @ManyToOneonstudentSelf` 也不是,因为它会指向许多记录

  • 我认为以下查询等同于您要实现的目标。

    SELECT * 
    FROM student s1
    WHERE s1.marks 
    IN
    (
      SELECT MIN(s2.marks) 
      FROM student s2
      where s1.subject = s2.subject
      AND  s1.teacher = s2.teacher
    )
  • 然后我想把它翻译成CriteriaQuery
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Student> query = cb.createQuery(Student.class);
    Root<Student> mainStudent = query.from(Student.class);

    Subquery<Student> subQuery = query.subquery(Student.class);
    Root<Student> subQueryRoot = subQuery.from(Student.class);

    Predicate predicate1 = cb.equal(mainStudent.get("teacher"), 
                                    subQueryRoot.get("teacher"));
    Predicate predicate2 = cb.equal(mainStudent.get("subject"), 
                                    subQueryRoot.get("subject"));
    Predicate finalPredicate = cb.and(predicate1, predicate2);

    subQuery.select(cb.min(subQueryRoot.get("marks"))).where(finalPredicate);

    query.select(mainStudent).where(mainStudent.get("marks").in(subQuery));
    em.createQuery(issueQuery).getResultList();

更新

基于vinay-s-g cmets 将subquery.correlate(mainStudent); 更新为subQuery.from(Student.class);

【讨论】:

  • 我还没有尝试过解决方案。我会等待OP的反馈,如果不起作用,将设置一个示例和实验。
  • 谢谢@KavithakaranKanapathippillai,您的解决方案做了一些小的改动。但是问题仍然存在,我们不能使用 Hibernate 加入 SELECT 查询吗?我正在尝试使用 API EasyCriteria 2.0,但仍然无法做到。
  • 我会更新答案以供参考。关于第二个问题,不可能Joining unrelated entities is only possible when using JPQL or HQL. This feature is not available when using the JPA Criteria API
【解决方案2】:

对上述答案进行了细微更改,

        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<Student> query = cb.createQuery(Student.class);
        Root<Student> mainStudent = query.from(Student.class);

        Subquery<Student> subQuery = query.subquery(Student.class);
        Root subQueryRoot = subQuery.from(Student.class);

        Predicate predicate1 = cb.equal(mainStudent.get("teacher"), 
                                        subQueryRoot.get("teacher"));
        Predicate predicate2 = cb.equal(mainStudent.get("subject"), 
                                        subQueryRoot.get("subject"));
        Predicate finalPredicate = cb.and(predicate1, predicate2);

        subQuery.select(cb.min(subQueryRoot.get("marks"))).where(finalPredicate);

        query.select(mainStudent).where(mainStudent.get("marks").in(subQuery));
        return entityManager.createQuery(query).getResultList();

【讨论】:

    猜你喜欢
    • 2017-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-01
    • 2012-11-04
    • 1970-01-01
    • 2017-07-11
    相关资源
    最近更新 更多