【问题标题】:What is the best way to query on a foreign key column?查询外键列的最佳方法是什么?
【发布时间】:2015-05-10 23:08:04
【问题描述】:

我正在使用 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;
}

请注意,id 没有引用 Division,而是由 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,这使得切换数据源变得更加困难。

有没有更好的方法来实现我想要做的事情?

【问题讨论】:

    标签: jpa querydsl


    【解决方案1】:

    对于关联映射的逆字段,可以使用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 查询中生成连接。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多