1

在 web-app 休眠标准中,oracle db 的时间过长。我启用 log4j.logger.org.hibernate.SQL=debug SQL 并在 sql 中使用相同的绑定变量运行 sql 查询,结果是实例。启用休眠日志并浏览日志。是什么导致hibernate花费太长时间摩擦查询?有什么建议么?

更新1:

当我通过 SQLPlus 运行休眠生成的相同查询时,oracle 似乎使用不同的执行计划。来自休眠的 SQL 查询:

select count(*) as y0_ from SUMMARY_VIEW this_ where this_.ser_id like :1 and this_.TYPE=:2 and this_.TIME_LOCAL>=:3 and this_.TIME_LOCAL<=:4 

在 SQLPlus 上运行 SQL 查询:

select count(*) as y0_ from SUMMARY_VIEW this_ where this_.ser_id like :ser_id and this_.TYPE=:type and this_.TIME_LOCAL>=:startdate and this_.TIME_LOCAL<=:enddate

更新 2: 进一步调查发现 startdate 和 endate 绑定变量作为 varchar2 从 sqlplus 传递,但这些作为时间戳从 app (:) 传递。由于这个执行计划是不同的。

select sql_text, v.sql_id, name, value_string, datatype_string from v$sql_bind_capture vbc  join v$sql v using (hash_value) where v.sql_id in (?)

绑定变量类型会影响执行计划吗?如果是这样,是否有任何其他工具可以将日期变量作为绑定参数传递给查询?

更新 3: 由于数据类型不兼容导致的性能问题。看来列数据类型(DATE)和休眠数据类型(TIMESTAMP)不匹配会导致隐式数据类型转换。Oracle 使用 INTERNAL_FUNCTION 来传输日期列以匹配传递的绑定变量休眠数据类型 TimeStamp。

类似问题:

使用 jdbc Timestamp 或 Date 时与 Oracle 的不可忽略的执行计划差异

当我为 DATE 列传递 java.sql.Timestamp 时,为什么 Oracle 这么慢?

4

1 回答 1

1

如果您使用的是 Hibernate,则可以使用此处指示的解决方案解决此问题:

http://blog.jooq.org/2014/12/29/leaky-abstractions-or-how-to-bind-oracle-date-correctly-with-hibernate/

本质上,您需要使用UserType

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Objects;

import oracle.sql.DATE;

import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.usertype.UserType;

public class OracleDate implements UserType {

    @Override
    public int[] sqlTypes() {
        return new int[] { Types.TIMESTAMP };
    }

    @Override
    public Class<?> returnedClass() {
        return Timestamp.class;
    }

    @Override
    public Object nullSafeGet(
        ResultSet rs, 
        String[] names, 
        SessionImplementor session, 
        Object owner
    )
    throws SQLException {
        return rs.getTimestamp(names[0]);
    }

    @Override
    public void nullSafeSet(
        PreparedStatement st, 
        Object value, 
        int index, 
        SessionImplementor session
    )
    throws SQLException {
        // The magic is here: oracle.sql.DATE!
        st.setObject(index, new DATE(value));
    }

    // The other method implementations are omitted
}

并使用该类型注释所有实体:

@Entity
@TypeDefs(
    value = @TypeDef(
        name = "oracle_date", 
        typeClass = OracleDate.class
    )
)
public class Rental {

    @Id
    @Column(name = "rental_id")
    public Long rentalId;

    @Column(name = "rental_date")
    @Type(type = "oracle_date")
    public Timestamp rentalDate;
}

恐怕有很多重复的样板,但至少你可以让执行计划再次加快速度。

JDBC 和其他 API

作为记录,一篇类似的文章介绍了如何在 JDBC 层解决这个问题,或者特别是使用 jOOQ:

http://blog.jooq.org/2014/12/22/are-you-binding-your-oracle-dates-correctly-i-bet-you-arent/

于 2014-12-29T15:12:35.557 回答