Это упрощенная версия того, что у меня есть, я сократил ее, чтобы сосредоточиться на проблеме. У меня есть большая таблица пользовательского интерфейса, основанная на StudentEntity, и я пытаюсь найти несколько разных столбцов (некоторые в StudentEntity и другие, основанные на совместных объектах)
Объект, который я хочу найти
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID", nullable = false)
private Long id;
@OneToOne(mappedBy = "student", fetch = FetchType.LAZY)
private ReportEntity report;
@ManyToOne
@JoinColumn(name = "FAVOURITE_SUBJECT_ID")
private SubjectEntity subject;
}
Сущность, доставляющая мне неприятности:
public class ReportEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID", nullable = false)
private Long id;
@Column(name = "GRADE")
private double grade;
@Column(name = "REPORT_TITLE")
private String title;
@OneToOne
@JoinColumn(name = "STUDENT_ID", nullable = false, unique = true)
private StudentEntity student;
}
Сущность, которая отлично работает:
public class SubjectEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID", nullable = false)
private Long id;
@Column(name = "NAME")
private String name;
}
Настройка связанных параметров поиска в контроллере:
StudentSearchParameters params = new StudentSearchParameters();
if (favSubjects != null && !favSubjects.isEmpty()) {
Iterable<SubjectEntity> subjects = subjectRepository.findAllByNameIn(favSubjects);
params.setFavSubjects(subjects);
}
if (reportTitle != null) {
Iterable<ReportEntity> reports = reportRepository.findAllByTitleLike(reportTitle);
params.setReportTitles(reports);
}
Поиск с помощью Пользовательская спецификация ученика:
public Predicate toPredicate(Root<StudentEntity> root, CriteriaQuery<?> cq,
CriteriaBuilder cb) {
Predicate p = cb.and();
...
// the keys and the value predicate in this map are used in the order by section,
// but that is working properly so I did not include it here
Map<String, SearchRelation<?>> optionalEntities = new HashMap<>();
optionalEntities.put(Constants.QUERY_PARAM_FAV_SUBJECTS, new
SearchRelation<SubjectEntity>("subject", "name", params.getFavSubjects()));
optionalEntities.put(Constants.QUERY_PARAM_REPORT_TITLE, new
SearchRelation<ReportEntity>("report", "title", params.getReportTitles()));
...
// -- Append optional join entities to predicate
for (Map.Entry<String, SearchRelation<?>> me : optionalEntities.entrySet()) {
if(me.getValue().getSearchList() == null) {
continue;
}
SearchRelation relation = me.getValue();
Predicate theseOptions = cb.or();
for (Object entity : relation.getSearchList()) {
// getSubjectReference is the prop name of the root entity eg 'subject' or 'report'
theseOptions.getExpressions().add(cb.or(cb.equal(root.get(relation.getSubjectReference()), entity)));
}
p.getExpressions().add(cb.and(theseOptions));
}
return p;
}
Наконец, класс SearchRelation, который я использовал:
public class SearchRelation<T> {
private String subjectReference;
private String predicate;
private Iterable<T> searchList;
public SearchRelation(String subjectReference, String predicate, Iterable<T> searchList) {
this.subjectReference = subjectReference;
this.predicate = predicate;
this.searchList = searchList;
}
public String getSubjectReference() {
return subjectReference;
}
public String getPredicate() {
return predicate;
}
public Iterable<T> getSearchList() {
return searchList;
}
}
Я (пытался) построить его таким образом, чтобы Spring мог обрабатывать объединения с помощью репозитории, и чтобы иметь возможность искать любой столбец объекта, который присоединен к root (Студент), просто добавив новый SearchRelation на карту в спецификации.
Как я уже сказал, я могу искать по теме, но при попытке сделать то же самое с отчетом я получаю ошибку:
Missing IN or OUT parameter at index:: 2
Я предполагаю, что это потому что в Student нет столбца с реальным идентификатором? Я бы предпочел не менять структуру БД. Есть ли способ, с помощью которого я могу использовать Criteria Builder и список сущностей из репозитория Spring для поиска объединенных сущностей без наличия столбца ID в root? Или я не в курсе, и есть какая-то другая проблема, которую я не получаю?
Обновление
Я установил P6Spy, чтобы увидеть, что на самом деле SQL выполняется против того, что регистрируется в спящем режиме. Hibernate дает мне (сжато) sql log:
select * from ( select col1, col2, ...... from "STUDENT" studen0_ where studen0_.ID=? ) where rownum <= ?
binding parameter [1] as [BIGINT] - [10960]
Я подтвердил, что в этом примере это правильный идентификатор. Однако, похоже, что он не привязан должным образом как журналы P6Spy:
select * from ( select col1, col2, ...... from "STUDENT" studen0_ where studen0_.ID=10 ) where rownum <= NULL
Я бы ожидал, что будет включено значение 10, так как это число строк, запрошенных из пользовательского интерфейса. Любая идея, почему он, кажется, не связывает параметр?