我有一些使用join-inheritance的实体,我正在对它们进行批量操作。正如Multi-table Bulk Operations中所解释的,Hibernate使用临时表来执行批量操作。
据我所知,临时表中的数据是临时的(在事务或会话结束时删除),但表本身是永久性的。我看到的是,每次执行这样的查询时,Hibernate都会尝试创建临时表。在我的情况下,每小时超过35.000次。 create table语句显然每次都失败,因为已存在具有该名称的表。这实际上是不必要的,可能会损害性能,DBA也不满意......
有没有办法让Hibernate记得它已经创建了临时表?
如果没有,是否有任何解决方法?我唯一的想法是使用单表继承来避免完全使用临时表。
Hibernate版本是4.2.8,DB是Oracle 11g。
答案 0 :(得分:2)
我认为这是TemporaryTableBulkIdStrategy中的错误,因为使用Oracle8iDialect时不应删除临时表:
@Override
public boolean dropTemporaryTableAfterUse() {
return false;
}
但只有在删除表格时才会进行此检查:
protected void releaseTempTable(Queryable persister, SessionImplementor session) {
if ( session.getFactory().getDialect().dropTemporaryTableAfterUse() ) {
TemporaryTableDropWork work = new TemporaryTableDropWork( persister, session );
if ( shouldIsolateTemporaryTableDDL( session ) ) {
session.getTransactionCoordinator()
.getTransaction()
.createIsolationDelegate()
.delegateWork( work, shouldTransactIsolatedTemporaryTableDDL( session ) );
}
else {
final Connection connection = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getLogicalConnection()
.getConnection();
work.execute( connection );
session.getTransactionCoordinator()
.getJdbcCoordinator()
.afterStatementExecution();
}
}
else {
// at the very least cleanup the data :)
PreparedStatement ps = null;
try {
final String sql = "delete from " + persister.getTemporaryIdTableName();
ps = session.getTransactionCoordinator().getJdbcCoordinator().getStatementPreparer().prepareStatement( sql, false );
session.getTransactionCoordinator().getJdbcCoordinator().getResultSetReturn().executeUpdate( ps );
}
catch( Throwable t ) {
log.unableToCleanupTemporaryIdTable(t);
}
finally {
if ( ps != null ) {
try {
session.getTransactionCoordinator().getJdbcCoordinator().release( ps );
}
catch( Throwable ignore ) {
// ignore
}
}
}
}
}
但现在在创建表时:
protected void createTempTable(Queryable persister, SessionImplementor session) {
// Don't really know all the codes required to adequately decipher returned jdbc exceptions here.
// simply allow the failure to be eaten and the subsequent insert-selects/deletes should fail
TemporaryTableCreationWork work = new TemporaryTableCreationWork( persister );
if ( shouldIsolateTemporaryTableDDL( session ) ) {
session.getTransactionCoordinator()
.getTransaction()
.createIsolationDelegate()
.delegateWork( work, shouldTransactIsolatedTemporaryTableDDL( session ) );
}
else {
final Connection connection = session.getTransactionCoordinator()
.getJdbcCoordinator()
.getLogicalConnection()
.getConnection();
work.execute( connection );
session.getTransactionCoordinator()
.getJdbcCoordinator()
.afterStatementExecution();
}
}
作为一种解决方法,您可以扩展Oracle方言并覆盖dropTemporaryTableAfterUse
方法以返回false
。
我为此填写了HHH-9744问题。
答案 1 :(得分:1)
随着Vlad指出我正确的方向,我提出了以下解决方法来缓存已创建的临时表的名称:
public class FixedTemporaryTableBulkIdStrategy extends TemporaryTableBulkIdStrategy {
private final Set<String> tables = new CopyOnWriteArraySet<>();
@Override
protected void createTempTable(Queryable persister, SessionImplementor session) {
final String temporaryIdTableName = persister.getTemporaryIdTableName();
if (!tables.contains(temporaryIdTableName)) {
super.createTempTable(persister, session);
tables.add(temporaryIdTableName);
}
}
}
可以通过将属性hibernate.hql.bulk_id_strategy
设置为此类的完全限定名称来使用。
请注意,这不是一般解决方案,只有在数据库/方言使用全局临时表(与会话/事务特定相对)时才有效。