Я использую JPA и Querydsl для динамического создания запросов на основе критериев, которые передаются через REST API. По большей части это работает нормально, но я не уверен, как лучше всего запрашивать объекты на основе значения в столбце внешнего ключа.
Чтобы проиллюстрировать сценарий, рассмотрим следующие (упрощенные) объекты.
@Entity
@Table(name = "division")
public class Division {
@Id
private Long id;
@Column(unique = true)
private String uniqueKey;
@OneToMany(mappedBy = "division")
private List<DivisionGroup> groups;
}
@Entity
@Table(name = "division_group")
public class DivisionGroup {
@ManyToOne
@JoinColumn(referencedColumnName = "uniqueKey")
private Division division;
}
Обратите внимание, что на Division ссылается не id
, а uniqueKey
. Скажем так, у меня есть на это свои причины.
Теперь я хотел бы выполнить запрос, подобный следующему:
select * from division_group where division = 'someUniqueKey'
Кажется достаточно простым, но для использования JPA требуется объединение и знание того, какое поле содержит значение внешнего ключа (в данном случае uniqueKey
). Я предполагаю, что запрос JPA будет похож на подход 1.
Обратите внимание, что я не использую сгенерированные Q-типы, потому что типы определяются динамически на основе запроса.
Подход 1 — JPAQuery
String divisionKey = "abc"; // the division's uniqueKey value from the request
// entity classes would be determined dynamically; hardcoded for this example
PathBuilder division = new PathBuilder<>(Division.class, "division");
PathBuilder divisionGroup = new PathBuilder<>(DivisionGroup.class, "divisionGroup");
JPAQuery query = new JPAQuery(entityManager)
.from(divisionGroup)
.innerJoin(divisionGroup.get("division"), division)
.where(division.get("uniqueKey").eq(divisionKey))
.list(divisionGroup);
У меня есть следующие проблемы с этим подходом:
- Присоединение кажется ненужным (но я мог бы с этим жить)
- Это требует от меня знания того факта, что поле
uniqueKey
используется в качестве столбца соединения. Это настоящая проблема, поскольку код должен уметь обрабатывать все виды сущностей с различными отображениями. - Я каким-то образом получаю критерии
null=null
, смешанные с запросом. Я предполагаю, что это может быть не связано, хотя.
В качестве альтернативы я рассмотрел возможность использования JPASQLQuery
вместо JPAQuery
. Это выглядит примерно так:
Подход 2 — JPASQLQuery
String divisionKey = "abc"; // the division's uniqueKey value from the request
MySQLTemplates templates = new MySQLTemplates();
JPASQLQuery jpasqlQuery = new JPASQLQuery(entityManager, templates);
CustomHibernateNamingStrategy namingStrategy = new CustomHibernateNamingStrategy();
Class entityClass = DivisionGroup.class; // hardcoded for example
String tableName;
if (entityClass.isAnnotationPresent(Table.class)) {
tableName = namingStrategy.tableName(((Table) entityClass.getAnnotation(Table.class)).name());
} else {
tableName = namingStrategy.classToTableName(entityClass.getSimpleName());
}
PathBuilder qDivisionGroup = new PathBuilder(entityClass, tableName);
Path sDivisionGroup = new PathImpl(entityClass, tableName);
List<DivisionGroup> groups = jpasqlQuery.from(sDivisionGroup)
.where(Expressions.stringPath("division").eq(divisionKey))
.list(qDivisionGroup);
Этот подход работает и не требует каких-либо знаний о том, как выглядит объект Division
и что поле uniqueKey
используется для отношения FK. Что мне не нравится в этом подходе:
- Мне нужно выяснить имя таблицы. Было бы неплохо, если бы это можно было сделать неявно, так как я все равно использую объект JPA.
- Я должен предоставить
MySQLTemplates
, что затрудняет переключение источников данных.
Есть ли лучший способ добиться того, что я пытаюсь сделать?