首页 > 解决方案 > JPA Criteria API 中订单的自定义 SQL

问题描述

我正在从已弃用的(不幸的是)Hibernate Criteria API 切换到 JPA Criteria API。我们有一个自定义的 Order(来自 Hibernate)接口实现来重新定义为其生成的 SQL。这个案例非常复杂,因为我们需要使用带有子查询的巨大SELECT。我们实现toSqlString了接口的方法来返回这个巨大的 SQL,我们需要一种方法将它迁移到JPA Criteria API.

问题是:JPA Criteria API 中有没有办法重新定义生成的 SQL?或者有没有一种奇怪的方式来使用 Hibernate Order 和 JPA Criteria API?

谢谢!

更新虽然@Tobias Liefke 的建议很有趣,但我的 SQL 变化太大,无法为每个 SQL 创建一个函数类。我尝试实现一个函数类并将 SQL 作为参数传递给那里,但这不起作用(呈现的 SQL 用单引号括起来,因此它作为参数而不是作为生成查询的一部分发送到数据库)

标签: javahibernatejpahibernate-criteriacriteria-api

解决方案


您不能在 JPQL 或条件查询中使用 SQL 片段...

... 除非...

1.调用函数

JPA 和 Hibernate 允许在其表达式中使用函数,例如:

... ORDER BY trim(entity.label) ASC

响应。

query.orderBy(criteriaBuilder.asc(
    criteriaBuilder.function("trim", String.class, root.get(ExampleEntity_.label))));

问题是,这实际上不是对 SQL 函数trim的调用,而是对 JPA 函数的调用,它必须被注册(Hibernate 已经为最常见的 SQL 函数这样做了)。

幸运的是,您可以在 a 中定义自己的 JPA 函数DialectResolver

public class MyDialectResolver implements DialectResolver {

  public Dialect resolveDialect(final DialectResolutionInfo info) {
    Dialect dialect = StandardDialectResolver.INSTANCE.resolve(info);
    dialect.registerFunction("myOrderFunction", ...);
    return dialect;
  }

}

registerFunction有两个参数,第一个是JPA中的函数名,另一个是到SQL的映射。

不要忘记在你的声明你的方言解析器persistence.xml

<persistence-unit name="database">
  <properties>
    <property name="hibernate.dialect_resolvers" 
              value="my.package.MyDialectResolver" />
  </properties>
</persistence-unit>

你现在可以在你的 SQL 服务器中创建你自己的函数,它包含你巨大的 SQL并将其注册为函数:

dialect.registerFunction("myOrderFunction", 
  new StandardSQLFunction("myOrderFunctionInSQL", StringType.INSTANCE));

或者您可以编写自己的映射,其中包括您庞大的 SQL

public class MyOrderFunction implements SQLFunction {

  public String render((Type firstArgumentType, List arguments,
      SessionFactoryImplementor factory) throws QueryException) {
    return my_huge_SQL;
  }

  // ...
}

并注册那个:

dialect.registerFunction("myOrderFunction",  new MyOrderFunction());

此解决方案的另一个优点:您可以根据实际的数据库方言定义不同的 SQL。

2. 使用公式

您可以为您的实体使用附加属性:

@Formula("my huge SQL")
private String orderAttribute;

您现在可以按此属性排序:

... ORDER BY entity.orderAttribute ASC

响应。

query.orderBy(criteriaBuilder.asc(root.get(ExampleEntity_.orderAttribute))));

我只推荐这个解决方案,如果你仍然需要模型中庞大的 SQL的结果。否则,它只会污染您的实体模型并将 SQL 添加到您的实体的每个查询中(除非您使用字节码检测@Basic(fetch = FetchType.lazy)标记它并使用它)。

一个类似的解决方案是@Subselect使用巨大的 SQL 定义一个实体 - 具有相同的缺点。


推荐阅读