gpt4 book ai didi

java - Hibernate比sql查询慢1000倍

转载 作者:搜寻专家 更新时间:2023-10-31 20:18:21 26 4
gpt4 key购买 nike

我有这个设置

@Table(name ="A")
EntityA {
Long ID;
List<EntityB> children;
}

@Table(name ="B")
EntityB {
Long ID;
EntityA parent;
EntityC grandchild;
}

@Table(name ="C")
EntityC {
Long ID;
}

SQL查询是这样的(我省略了不相关的细节):

select top 300 from A where ... and ID in (select parent from B where ... and grandchild in (select ID from C where ...)) order by ...

在直接数据库或通过 Hibernate (3.5) SQL 中的 sql 查询比使用 Criteria 或 HQL 来表达快 1000。

生成的 SQL 与 HQL 和 Criteria 以及我在此处发布的 SQL 相同

[编辑]:更正 - sql 不相同。我没有在管理工作室端尝试 Hibernate 样式参数设置,因为直到后来我才意识到这一点 - 请参阅我的回答。

如果我将子查询分离成单独的查询,那么它又很快了。

我试过了

  • 删除子项、父项等的所有映射,只使用长 ID 引用 - 同样的事情,所以它不是获取、懒惰、渴望相关的。
  • 使用连接而不是子查询,并且在获取和加载的所有组合中都获得了相同的缓慢行为。
  • 在 ID 上设置投影而不是检索实体,因此没有对象转换 - 仍然很慢

我查看了 Hibernate 代码,它正在做一些令人震惊的事情。它循环遍历所有 300 个最终进入数据库的结果。

private List doQuery(
final SessionImplementor session,
final QueryParameters queryParameters,
final boolean returnProxies) throws SQLException, HibernateException {

final RowSelection selection = queryParameters.getRowSelection();
final int maxRows = hasMaxRows( selection ) ?
selection.getMaxRows().intValue() :
Integer.MAX_VALUE;

final int entitySpan = getEntityPersisters().length;

final ArrayList hydratedObjects = entitySpan == 0 ? null : new ArrayList( entitySpan * 10 );
final PreparedStatement st = prepareQueryStatement( queryParameters, false, session );
final ResultSet rs = getResultSet( st, queryParameters.hasAutoDiscoverScalarTypes(), queryParameters.isCallable(), selection, session );

// would be great to move all this below here into another method that could also be used
// from the new scrolling stuff.
//
// Would need to change the way the max-row stuff is handled (i.e. behind an interface) so
// that I could do the control breaking at the means to know when to stop

final EntityKey optionalObjectKey = getOptionalObjectKey( queryParameters, session );
final LockMode[] lockModesArray = getLockModes( queryParameters.getLockOptions() );
final boolean createSubselects = isSubselectLoadingEnabled();
final List subselectResultKeys = createSubselects ? new ArrayList() : null;
final List results = new ArrayList();

try {

handleEmptyCollections( queryParameters.getCollectionKeys(), rs, session );

EntityKey[] keys = new EntityKey[entitySpan]; //we can reuse it for each row

if ( log.isTraceEnabled() ) log.trace( "processing result set" );

int count;
for ( count = 0; count < maxRows && rs.next(); count++ ) {

if ( log.isTraceEnabled() ) log.debug("result set row: " + count);

Object result = getRowFromResultSet(
rs,
session,
queryParameters,
lockModesArray,
optionalObjectKey,
hydratedObjects,
keys,
returnProxies
);
results.add( result );

if ( createSubselects ) {
subselectResultKeys.add(keys);
keys = new EntityKey[entitySpan]; //can't reuse in this case
}

}

if ( log.isTraceEnabled() ) {
log.trace( "done processing result set (" + count + " rows)" );
}

}
finally {
session.getBatcher().closeQueryStatement( st, rs );
}

initializeEntitiesAndCollections( hydratedObjects, rs, session, queryParameters.isReadOnly( session ) );

if ( createSubselects ) createSubselects( subselectResultKeys, queryParameters, session );

return results; //getResultList(results);

}

在这段代码中

final ResultSet rs = getResultSet( st, queryParameters.hasAutoDiscoverScalarTypes(), queryParameters.isCallable(), selection, session );

它使用完整的 SQL 访问数据库,但没有收集到任何结果。

然后继续执行这个循环

for ( count = 0; count < maxRows && rs.next(); count++ ) {

对于预期的 300 个结果中的每一个,它最终都会访问数据库以获得实际结果。

这看起来很疯狂,因为在 1 次查询后它应该已经有了所有结果。 Hibernate 日志不显示在此期间发出的任何其他 SQL。

有没有人有什么见解?我唯一的选择是通过 Hibernate 进行原生 SQL 查询。

最佳答案

我终于弄清楚了这个问题。问题是由 Hibernate 将参数设置与涉及子查询的实际 SQL 查询分开引起的。所以无论是否使用原生 SQL,如果这样做,性能都会很慢。例如,这会很慢:

String sql = some sql that has named parameter = :value
SQLQuery sqlQuery = session.createSQLQuery(sql);
sqlQuery.setParameter ("value", someValue);
List<Object[]> list = (List<Object[]>)sqlQuery.list();

这会很快

String sql = some native sql where parameter = 'actualValue'
SQLQuery sqlQuery = session.createSQLQuery(sql);
List<Object[]> list = (List<Object[]>)sqlQuery.list();

似乎出于某种原因,让 Hibernate 处理参数最终会卡在 resultSet 获取过程中。这可能是因为对数据库的底层查询需要更长的时间进行参数化。我最终编写了与 Hibernate Criteria and Restrictions 代码等效的代码,直接如上所述设置参数。

关于java - Hibernate比sql查询慢1000倍,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32999904/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com