Мне нужно построить запрос типа пересечения со следующими сущностями (уменьшенными для ясности).JPA Criteria API произвольное количество соединений/подзапросов
@Entity // and other @ stuff
public class Member {
@Id
private Long id;
private String name;
...
}
@Entity
public class Program {
@Id
private Long id;
private Long programName;
private List<ProgramLevel> levels;
...
}
@Entity
public class ProgramLevel {
@Id
private Long id
private String levelName;
}
член может принадлежать к одной или нескольким программам, и он всегда имеет свой программный уровень, связанный этим:
public class Membership {
@Id
private Long id;
private Long memberId; // this is the member
private Long programId; // in which program is he
private Long programLevel; // and on what level
...
}
Пример: У меня есть три программы по математике, английский, наука. Каждый из них имеет несколько уровней, например, MAth имеет алгебру, геометрию, английский язык имеет литературу, правописание и грамматику, у науки есть эксперименты и теория.
Кроме того, пример пользователя Джо имел бы математику: алгебру, английский: грамматические программы и уровни. Пример пользователя Макс мог бы иметь английский: литература. Таким образом, член может иметь несколько программ, но только один уровень для каждой программы.
Теперь мне нужно подсчитать или выбрать всех участников, которые соответствуют нескольким программам и некоторым уровням в них. Пример: Я хочу, чтобы все пользователи имели математику: алгебра или геометрия, а также английский: литература или грамматика и наука: теория.
Я не очень разбираюсь в материалах JPA, поэтому я застреваю.
В SQL я бы сделал пересечение. Как мне это сделать с JPA?
У меня есть что-то вроде этого:
HashMap<Long, List<ProgramLevel>> levels = new HashMap<Long, List<ProgramLevel>>();
// then I fetch the levels map.
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Long> criteriaQuery = cb.createQuery(Long.class);
Root<Membership> root = query.from(Membership.class);
// this is the part where I'm stuck.
// I figured I could try with multiple inner joins?
for(Map.Entry<Long, List<ProgramLevel>> curentLevel: levels.entrySet()) {
// cb.equal(root.join ??? what comes here?
// what what what?
// root.get("programId") = currentLevel.getKey()
// AND root.get("programLevelId") IN currentLevel.getValue()
}
...
Как бы я это?
Помимо выполнения этого как нескольких внутренних объединений, я не знаю, можно ли это сделать как INTERSECT (db - PostgreSQL, если это имеет значение)?
Кроме того, я не уверен, где в этом вопросе я бы поставил условие только для того, чтобы получить отличное членство в этих условиях.
В качестве бонуса я должен будет создать запрос OR. Это одно, где я должен соответствовать всем программам, следующий нужен ЛЮБОЙ соответствие программы/уровня для члена, который будет включен в него. Но как только я это увижу, я надеюсь, что это будет один.
EDIT: возможный (псевдо) SQL выбрать будет выглядеть следующим образом:
SELECT count(membership) FROM membership m
WHERE (m.program == :program1 and m.programLevel in :programLevels1ArrayOfLevels)
INTERSECT
SELECT count(membership) FROM membership m
WHERE (m.program == :program2 and m.programLevel in :programLevels2ArrayOfLevels)
INTERSECT...
... /* this would go on for as many (Program, List<ProgramLevel>) pairs I have on input. Which is variable).
Или что-то вроде этого:
SELECT count(membership) FROM membership m
JOIN membership m1 ON (m1.program = :program1 AND m1.programLevel IN m1.programLevels1Aray)
JOIN membership m2 ON (m2.program = :program2 AND m2.programLevel IN m1.programLevels2Aray)
/* Continued to as many input pairs I have */
Обычно вы делаете это, присоединяясь к одной таблице несколько раз с разными псевдонимами. JPA должна предложить способ сделать это, но мне блаженно не пришлось использовать его или API-интерфейс Criteria около года, поэтому я собираюсь указать вам примерно в правильном направлении, а затем убежать от криков для холмов. –
Хм. И как я могу назвать имена этих объединений? Потому что я не знаю, сколько их там.Я не знаю, показывает ли это, но Java - это мой второй язык :) – Zlatko
Не могли бы вы опубликовать [скрипку] (http://sqlfiddle.com/) или что-то о том, что вы хотите с точки зрения SQL? Также, сколько возможных объединений вам понадобится. Если вы только разбрасываете список значений, вы можете достичь чего-то похожего на [this] (http://stackoverflow.com/questions/403336/getting-a-query-intersection-in-jpa), зацикливая список значений и создавая объединения по требованию с помощью API критериев. Таким образом, вы также можете использовать псевдоним по запросу (например, 'q1',' q2', 'q3',' qn'). –