如何向 Hibernate criteria api 查询插入“优化器提示”

问题描述 投票:0回答:5

我有一个使用标准 API 动态组合的 Hibernate 查询。 如果按原样执行,它会生成慢得难以忍受的查询。

但我注意到,如果我在查询前添加 /*+ FIRST_ROWS(10) */ ,它们的速度大约会快 1000%。我如何使用标准 API 来做到这一点?

我尝试了 criteria.setComment(..),但这似乎被忽略了。

在 hibernate 文档中,3.4.1.7。提到了查询提示,但它明确指出:“请注意,这些不是 SQL 查询提示”

查询的结果将会分页,所以在99%的情况下我会显示结果1-10。

java sql oracle hibernate criteria
5个回答
10
投票

我有另一个通用解决方案,它应该适用于每个条件查询:
使用标准注释和 Hibernate 拦截器将最终 SQL 更改为数据库。
(我在 Hibernate 3.3 中使用它,但应该适用于每个版本,拦截器的注册可能会有所不同。)

在您的查询代码中使用:

criteria.setComment("$HINT$ push_pred(viewAlias)");

编写一个拦截器来更改为SQL文本(这个使用commons.lang3.StringUtils):

public class HibernateEntityInterceptor extends EmptyInterceptor {

@Override
public String onPrepareStatement(String sql) {
    if (sql.startsWith("/* $HINT$")) {
        String hintText = StringUtils.substringBetween(sql, "/* $HINT$", "*/");
        sql = sql.replaceFirst("select ", "select /*+" + hintText + "*/ ");
    }
    return sql;
}

以上适用于 Oracle,但应该可以轻松地针对每个 DBMS 进行调整。
也许您可以/应该为提示标记“$HINT$”创建一个常量。
日志记录也应该完成(这样你就可以轻松地看到拦截器的正确调用),为了简单起见,我在上面省略了它。

拦截器必须注册。在春季,这是在

applicationContext.xml
:

中完成的
<bean id="entityListener" class="your.package.HibernateEntityInterceptor"/>

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="entityInterceptor" ref="entityListener"/>
    [...]

或者(从 Hibernate 3.3 文档复制):

打开会话时指定会话范围的拦截器 使用重载的 SessionFactory.openSession() 方法之一 接受拦截器。

Session session = sf.openSession( new HibernateEntityInterceptor() );

SessionFactory 范围内的拦截器已注册到 构建 SessionFactory 之前的配置对象。除非一个 会话被打开并显式指定要使用的拦截器, 提供的拦截器将应用于从该拦截器打开的所有会话 会话工厂。 SessionFactory 范围的拦截器必须是线程 安全的。确保您不存储特定于会话的状态,因为 多个会话可能会同时使用此拦截器。

new Configuration().setInterceptor( new HibernateEntityInterceptor() );


7
投票

我能够通过向条件添加 ProjectionList 来放入 Oracle 提示。

ProjectionList proList = Projections.projectionList();
proList.add(Projections.sqlProjection("/*+INDEX_DESC(this_ MY_INDEX_NAME)*/ 1 as MYHINT",
    new String[]{},
    new Type[]{}));
//add properties from your class
proList.add(Projections.property("field1"));
proList.add(Projections.property("field2"));
proList.add(Projections.property("field3"));
c.setProjection(proList);

c.list()
按 ProjectionList
 的顺序返回 
List<Object[]>


5
投票

您可以在会话级别修改优化器模式:

ALTER SESSION SET optimizer_mode = FIRST_ROWS;

要么在查询之前,然后将其恢复为默认值 (

ALL_ROWS
),要么在您的情况下,因为 99% 的查询都会从中受益,您可以在架构级别修改它(使用
ON LOGON
触发器)示例)甚至在实例级别(修改 init 参数)。


1
投票

问题是提示语法不是注释,它只是看起来有点像注释。它确实必须位于

SELECT
和所选列之间,而
setComment()
SELECT
之前添加注释。

除此之外,没有什么灵丹妙药。

FIRST_ROWS
不是性能增强工具。最终可能需要更长的时间才能恢复“所有”行。当然,在面向用户的程序中,检索前十行可能就是我们需要做的全部。 但是,无论您采用哪种方式反弹它,如果您想使用 Oracle 的提示语法,您都需要走本机 SQL 路线。

你还能做什么?我(还)没有太多调整 Hibernate 的经验。有一次我执行这样的任务时,查询是从一大堆表中获取行来实例化具有许多子类型的对象。每个子类型都是一个单独的表。 Hibernate 生成的查询有很多 OUTER JOIN,这让优化器感到困惑。将这个怪物分解为几个仅使用 INNER JOIN 的集中查询(每个子类型一个),从而使检索时间减少了两百倍。

这可能对您没有任何立即用处。但原则是,看看 Hibernate 查询,看看是否可以用不同的、更高效的方式实现。


0
投票

© www.soinside.com 2019 - 2024. All rights reserved.