【问题标题】:Custom SQL for Order in JPA Criteria APIJPA Criteria API 中订单的自定义 SQL
【发布时间】:2019-03-13 07:56:03
【问题描述】:

我正在从已弃用(不幸的是)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 用单引号括起来,因此它作为参数而不是作为生成查询的一部分发送到数据库)

【问题讨论】:

    标签: java hibernate jpa hibernate-criteria criteria-api


    【解决方案1】:

    您不能在 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 函数这样做了)。

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

    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) 标记它并使用字节码检测)。 p>

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

    【讨论】:

    • 很有意思,非常感谢!所以,我可以使用第一个解决方案而不创建实际功能,例如Oracle 和简单的实现 SQLFunction 接口吧?
    • 对。事实上,第一个解决方案包含两种“风格”:一种带有自定义 SQL 函数,一种带有映射接口。
    • 太棒了!我想,这是最好的解决方案:)
    • 这种方法似乎不起作用。最终,该函数返回 SQL,但它像通常的字符串一样被括在括号中。甚至 Hibernate 也将其类型显示为 VARCHAR。有没有办法将函数呈现为 SQL,而不是字符串?
    • 你指的是哪种括号?字符串通常用单引号 (') 括起来,而不是用括号括起来。我自己实现了不止一个SQLFunction,并且我确信 Hibernate 通常没有添加引号或括号。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-06
    • 2019-11-10
    • 1970-01-01
    • 2013-06-21
    • 2012-09-19
    • 2011-03-29
    • 2014-02-19
    相关资源
    最近更新 更多