java - JPA 标准 API : Aggregating on Multiple Columns with IF Condition

标签 java mysql hibernate jpa criteria-api

我有以下 MySQL 模式 -

注意:我知道这不是一个理想的模式,但我使用的是遗留代码,所以我无法完全控制完全更改模式

CREATE TABLE `student` (
    `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(255) NOT NULL COLLATE 'utf8mb4_unicode_ci',
    PRIMARY KEY (`id`)
);

CREATE TABLE `subject` (
    `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    `student_id` INT(10) NOT NULL DEFAULT '0',
    `title` VARCHAR(255) NOT NULL DEFAULT '0' COLLATE 'utf8mb4_unicode_ci',
    `marks` INT(11) NOT NULL DEFAULT '0',
    PRIMARY KEY (`id`)
);

示例数据 -

INSERT INTO `student` (`id`, `name`) VALUES
    (1, 'Foo'),
    (2, 'Bar'),
    (3, 'Baz');

INSERT INTO `subject` (`id`, `student_id`, `title`, `marks`) VALUES
    (1, 1, 'math', 25),
    (2, 1, 'physics', 35),
    (3, 1, 'chemistry', 45),
    (4, 2, 'math', 85),
    (5, 2, 'physics', 65),
    (6, 2, 'chemistry', 65);

我正在尝试使用 JPA Criteria API 编写的查询 -

SELECT st.name
     , max(if(sub.title = 'math', sub.marks, 0)) as math
     , max(if(sub.title = 'physics', sub.marks, 0)) as physics
     , max(if(sub.title = 'chemistry', sub.marks, 0)) as chemistry
FROM student st
LEFT OUTER JOIN subject sub ON st.id = sub.student_id
GROUP BY st.name;

样本结果-

| name | math | physics | chemistry | 
| ---- | -----| --------| --------- | 
| Bar  | 85   | 65      | 65        | 
| Baz  | 0    | 0       | 0         | 
| Foo  | 25   | 35      | 45        | 

到目前为止我尝试了什么-

CriteriaBuilder criteriaBuilder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<StudentMark> dataQuery = criteriaBuilder.createQuery(StudentMark.class);

Root<Student> root = dataQuery.from(Student.class);
Join<Student, Subject> studentSubjectJoin = root.join(ReflectUtil.getFieldName(Student.class, Student::getSubjectMarks), JoinType.LEFT);

dataQuery.multiselect(root.get(ReflectUtil.getFieldName(Student.class, Student::getName)),
        criteriaBuilder.max(studentSubjectJoin.get(ReflectUtil.getFieldName(Subject.class, Subject::getValue))),
        criteriaBuilder.max(studentSubjectJoin.get(ReflectUtil.getFieldName(Subject.class, Subject::getValue))),
        criteriaBuilder.max(studentSubjectJoin.get(ReflectUtil.getFieldName(Subject.class, Subject::getValue))));

dataQuery.groupBy(root.get(ReflectUtil.getFieldName(Student.class, Student::getStudentId)));

this.entityManager.createQuery(dataQuery);

我不知道如何根据 SQL 查询在 MAX 聚合函数中编写 IF

最佳答案

查询等同于:

SELECT st.name
     , MAX(CASE WHEN sub.title = 'math' THEN sub.marks ELSE 0 END) as math
     , MAX(CASE WHEN sub.title = 'physics' THEN sub.marks ELSE 0 END) as physics
     , MAX(CASE WHEN sub.title = 'chemistry' THEN sub.marks ELSE 0 END) as chemistry
FROM student st
LEFT OUTER JOIN subject sub ON st.id = sub.student_id
GROUP BY st.name;

在条件 API 中:

dataQuery.multiselect(root.get(Student_.name),
    criteriaBuilder.max(
        criteriaBuilder.selectCase()
            .when(criteriaBuilder.equal(studentSubjectJoin.get(Subject_.title), criteriaBuilder.literal("math")), studentSubjectJoin.get(Subject_.marks))
            .otherwise(criteriaBuilder.literal(0))
    ).alias("math"),
    criteriaBuilder.max(...)
    ...
);

(为了清晰起见,我使用了静态元模型表示法而不是反射调用;作为旁注,您可能想考虑这样做,它大大提高了可读性)

关于java - JPA 标准 API : Aggregating on Multiple Columns with IF Condition,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57800309/

相关文章:

java - Android系统如何告诉应用程序该应用程序已进入后台(不可见)?

mysql - CakePHP-SQL : joining tables

mysql - 无法访问 phpmyadmin

java - 将 Hibernate 升级到 5.1.0 后如何导出模式?

java - 迷惑与势是有关系的

java - 我在哪里可以找到所有 HQL 关键字的列表?

java - Apache solr.查询语法解释

java - 如何将自纪元以来的秒数转换为 Java 中的小时/分钟/秒?

java - 以编程方式反编译 .class 文件

mysql - 使用 Doctrine 保存行时 Doctrine_Connection_Mysql_Exception