问题
The project I'm working on was generated with JHipster with support for entity filtering, which uses Spring Data JPA Specifications under the hood.
The model is as follows (in 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 generates boilerplate for filtering Attendance by enrollmentId, but I'd like to extend it to be able to filter by studentId and courseId as well.
So, how can I implement a Specification that would perform a query like:
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;
回答1:
Following @GaëlMarziou's advice, I've implemented simple methods create the Specification<Attendance> for joining with Student and Course.
I added the fields, getters and setters to AttendanceCriteria class, and recompiled to update the JPA metamodel:
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;
}
Here's the updated snippet from AttencanceQueryService class:
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;
};
}
I hope this helps someone, and the credit goes to @GaëlMarziou and Blackdread/Yoann Caplain.
来源:https://stackoverflow.com/questions/52424456/how-to-write-a-spring-data-jpa-specification-with-multiple-joins