我正在使用 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;
}
Run Code Online (Sandbox Code Playgroud)
请注意,该 Division 不是被引用id而是被引用uniqueKey。让我们说我有这样做的理由。
现在,我想运行如下查询:
select * from division_group where division = 'someUniqueKey'
Run Code Online (Sandbox Code Playgroud)
看起来很简单,但使用 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);
Run Code Online (Sandbox Code Playgroud)
我对这种方法有以下问题:
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);
Run Code Online (Sandbox Code Playgroud)
这种方法有效并且不需要任何关于Division实体外观和uniqueKey字段用于 FK 关系的知识。我不喜欢这种方法的地方:
MySQLTemplates这使得切换数据源变得更加困难。有没有更好的方法来实现我想要做的事情?
对于关联映射的逆字段,可以使用ID解引用。例如,divisionGroup.division.id = 1这是有效的 JPQL/HQL,并且不需要您描述的联接。以 QueryDSL 为例divisionGroup.get("division").get("id").eq(1)。即使关联是由外键(即不是主键)映射的,这也是可能的。通过您的映射divisionGroup.get("division").get("uniqueKey")实际上应该生成有效的 JPQL。请注意,Hibernate 最近才开始支持自然 ID 的 ID 取消引用(我自己贡献了它),因此在旧版本的 Hibernate 中,您仍然会看到在生成的 SQL 查询中生成连接。
| 归档时间: |
|
| 查看次数: |
996 次 |
| 最近记录: |