代码之家  ›  专栏  ›  技术社区  ›  Andreas Petersson

如何将“优化器提示”插入休眠条件API查询

  •  7
  • Andreas Petersson  · 技术社区  · 15 年前

    我有一个使用条件API动态组合在一起的休眠查询。 如果按原样执行,它会生成速度非常慢的查询。

    但是我已经注意到,如果我在查询前加上/*+第一行(10)*/,它们会快1000%。如何使用标准API来实现这一点?

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

    在休眠文档中,3.4.1.7。提到了查询提示,但它清楚地指出:“请注意,这些不是SQL查询提示。”

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

    4 回复  |  直到 8 年前
        1
  •  5
  •   Vincent Malgrat    15 年前

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

    ALTER SESSION SET optimizer_mode = FIRST_ROWS;
    

    要么就在查询之前,然后将其恢复为默认值( ALL_ROWS )或者在您的案例中,因为99%的查询将从中受益,所以您可以在模式级别修改它(使用 ON LOGON 例如触发器),甚至在实例级别(修改init参数)。

        2
  •  6
  •   takrl cck    13 年前

    我可以通过在条件中添加项目列表来输入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() 收益率 List<Object[]> 按项目顺序列表

        3
  •  5
  •   Thies    8 年前

    我有另一个通用解决方案,它适用于每个条件查询:
    使用标准注释和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之前配置对象。除非A 会话将显式打开,指定要使用的拦截器, 提供的拦截器将应用于从该拦截器打开的所有会话 会议工厂。sessionFactory范围的拦截器必须是线程 安全。确保不存储会话特定的状态,因为 多个会话可能同时使用这个拦截器。

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

        4
  •  1
  •   APC    15 年前

    问题是提示语法不是注释,它看起来有点像注释。它真的必须介于 SELECT 以及选定的列,而 setComment() 选择 .

    除此之外,没有银弹。 FIRST_ROWS 不是性能增强工具。最终可能需要更长的时间 全部的 排在后面。当然,在面向用户的程序中,检索前十行可能是我们需要做的全部工作。

    但是,不管怎样,如果您想使用Oracle的提示语法,您都需要沿着本机SQL路径走下去。

    你还能做什么?我还没有调整休眠的经验。有一次在这样的任务下,查询从一大堆表中获取行,以实例化具有许多子类型的对象。每个子类型都是一个单独的表。由Hibernate生成的查询有许多外部联接,这使检查优化器感到困惑。把那个怪物分解成几个集中的查询(每个子类型一个),只使用内部连接,从而减少了检索时间的百分之二。

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