【问题标题】:How to write a Spring Data JPA Specification with multiple joins?如何编写具有多个连接的 Spring Data JPA 规范?
【发布时间】:2018-09-20 11:45:52
【问题描述】:

我正在处理的项目是使用支持实体过滤的 JHipster 生成的,它在后台使用 Spring Data JPA 规范。

模型如下(在JDL中):

entity Student {
    name String
}

entity Course {
    name String
}

entity Enrollment {
}

entity Attendance {
    date LocalDate
}

relationship OneToMany {
    Student to Enrollment(student required),
    Course to Enrollment(course required),
    Enrollment to Attendance(enrollment required)
}

filter all
service all with serviceClass

JHipster 生成样板以通过enrollmentId 过滤Attendance,但我想扩展它以能够通过studentIdcourseId 进行过滤。

那么,我怎样才能实现一个能执行如下查询的规范:

SELECT
  attendance.date, student.name as student, course.name as course
FROM attendance
JOIN enrollment
  ON enrollment.id = attendance.enrollment_id
JOIN student
  ON student.id = enrollment.student_id
  AND studend.id = 1
JOIN course
  ON course.id = enrollment.course_id
  AND course.id = 2;

【问题讨论】:

  • jhipster 中正在做的工作:github.com/jhipster/jhipster/pull/97
  • @GaëlMarziou 我根据您的链接中的代码实现了一个简单的解决方案:return (root, query, builder) -> builder.equal(root.join(Attendance_.enrollment).join(Enrollment_.student).get(student_.id), studentId);。也许我不需要加入Attendance,因为我有一个Root<Attendance>,但它确实有效。您要提交答案以便我接受吗?
  • 谢谢,但答案是你的,你可以发布它。我很高兴链接有所帮助。

标签: java spring-data-jpa jhipster


【解决方案1】:

按照@GaëlMarziou 的建议,我实现了简单的方法,创建Specification<Attendance> 用于与StudentCourse 连接。

我将字段、getter 和 setter 添加到 AttendanceCriteria 类,并重新编译以更新 JPA 元模型:

private LongFilter studentId;

private LongFilter courseId;

public LongFilter getStudentId() {
    return studentId;
}

public void setStudentId(LongFilter studentId) {
    this.studentId = studentId;
}

public LongFilter getCourseId() {
    return courseId;
}

public void setCourseId(LongFilter courseId) {
    this.courseId = courseId;
}

这是来自 AttencanceQueryService 类的更新后的 sn-p:

private Specification<Attendance> createSpecification(AttendanceCriteria criteria) {
    Specification<Attendance> specification = Specification.where(null);
    if (criteria != null) {
        if (criteria.getId() != null) {
            specification = specification.and(buildSpecification(criteria.getId(), Attendance_.id));
        }
        if (criteria.getDate() != null) {
            specification = specification.and(buildRangeSpecification(criteria.getDate(), Attendance_.date));
        }
        if (criteria.getEnrollmentId() != null) {
            specification = specification.and(buildReferringEntitySpecification(criteria.getEnrollmentId(), Attendance_.enrollment, Enrollment_.id));
        }
        if (criteria.getStudentId() != null) {
            specification = specification.and(buildJoinSpecification(criteria.getStudentId(), Attendance_.enrollment, Enrollment_.student, Student_.id));
        }
        if (criteria.getCourseId() != null) {
            specification = specification.and(buildJoinSpecification(criteria.getCourseId(), Attendance_.enrollment, Enrollment_.course, Course_.id));
        }
    }
    return specification;
}

private <REFERENCE, JOIN, FILTER extends Comparable<? super FILTER>> Specification<Attendance> buildJoinSpecification(RangeFilter<FILTER> filter, SingularAttribute<? super Attendance, REFERENCE> reference, SingularAttribute<REFERENCE, JOIN> joinField, SingularAttribute<JOIN, FILTER> valueField) {
    Specification<Attendance> result = Specification.where((Specification) null);
    if (filter.getEquals() != null) {
        result = this.equalsSpecification(reference, joinField, valueField, filter.getEquals());
    }
    if (filter.getIn() != null) {
        result = this.valueIn((SingularAttribute) reference, joinField, valueField, filter.getIn());
    }
    return result;
}

private <REFERENCE, JOIN, FILTER> Specification<Attendance> equalsSpecification(SingularAttribute<? super Attendance, REFERENCE> reference, SingularAttribute<REFERENCE, JOIN> joinField, SingularAttribute<JOIN, FILTER> idField, FILTER value) {
    return (root, query, builder) ->
        builder.equal(root.join(reference).join(joinField).get(idField), value);
}

private <REFERENCE, JOIN, FILTER> Specification<Attendance> valueIn(SingularAttribute<? super Attendance, REFERENCE> reference, SingularAttribute<REFERENCE, JOIN> joinField, SingularAttribute<JOIN, FILTER> valueField, Collection<FILTER> values) {
    return (root, query, builder) -> {
        CriteriaBuilder.In<FILTER> in = builder.in(root.join(reference).join(joinField).get(valueField));
        for (FILTER value : values) {
            in = in.value(value);
        }
        return in;
    };
}

我希望这对某人有所帮助,感谢 @GaëlMarziouBlackdread/Yoann Caplain

【讨论】:

    猜你喜欢
    • 2017-05-12
    • 1970-01-01
    • 2021-04-20
    • 2020-05-16
    • 1970-01-01
    • 2017-10-28
    • 1970-01-01
    • 2019-05-06
    • 2021-09-07
    相关资源
    最近更新 更多