如何在Hibernate条件api查询中插入"优化器提示"

And*_*son 7 java sql oracle hibernate criteria

我有一个使用条件api动态组合的hibernate查询.如果按原样执行,它会生成难以忍受的慢速查询.

但我已经注意到,如果我在查询前加上/*+ FIRST_ROWS(10)*/,它们的速度提高了大约1000%.我怎么能用标准api做到这一点?

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

在hibernate docs中,3.4.1.7.提到了查询提示,但它明确指出:"请注意,这些不是SQL查询提示"

查询的结果将被分页,因此在99%的情况下,我将显示结果1-10.

小智 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);
Run Code Online (Sandbox Code Playgroud)

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


Thi*_*ies 6

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

在您的查询代码中使用:

criteria.setComment("$HINT$ push_pred(viewAlias)");
Run Code Online (Sandbox Code Playgroud)

编写一个Interceptor来更改为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;
}
Run Code Online (Sandbox Code Playgroud)

以上是针对Oracle的,但应该可以轻松调整每个DBMS.
也许你可以/应该为提示标记"$ HINT $"创建一个常量.
记录也应该完成(这样你就可以很容易地看到Interceptor的正确调用),为了简单起见,我把它留在了上面.

拦截器必须注册.在Spring中,这是在applicationContext.xml:

<bean id="entityListener" class="your.package.HibernateEntityInterceptor"/>

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="entityInterceptor" ref="entityListener"/>
    [...]
Run Code Online (Sandbox Code Playgroud)

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

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

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

在构建SessionFactory之前,会在Configuration对象中注册SessionFactory范围的拦截器.除非明确指定要使用的拦截器打开会话,否则提供的拦截器将应用于从该SessionFactory打开的所有会话.SessionFactory范围的拦截器必须是线程安全的.确保您不存储特定于会话的状态,因为多个会话可能会同时使用此拦截器.

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


Vin*_*rat 5

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

ALTER SESSION SET optimizer_mode = FIRST_ROWS;
Run Code Online (Sandbox Code Playgroud)

在您的查询之前,然后将其恢复到其默认值(ALL_ROWS)或在您的情况下,因为99%的查询将从中受益,您可以在架构级别(使用ON LOGON示例的触发器)或甚至在实例处修改它level(修改init参数).