Hibernate多表批量操作总是尝试创建临时表

时间:2015-04-22 14:51:16

标签: java sql oracle hibernate inheritance

我有一些使用join-inheritance的实体,我正在对它们进行批量操作。正如Multi-table Bulk Operations中所解释的,Hibernate使用临时表来执行批量操作。

据我所知,临时表中的数据是临时的(在事务或会话结束时删除),但表本身是永久性的。我看到的是,每次执行这样的查询时,Hibernate都会尝试创建临时表。在我的情况下,每小时超过35.000次。 create table语句显然每次都失败,因为已存在具有该名称的表。这实际上是不必要的,可能会损害性能,DBA也不满意......

有没有办法让Hibernate记得它已经创建了临时表?

如果没有,是否有任何解决方法?我唯一的想法是使用单表继承来避免完全使用临时表。

Hibernate版本是4.2.8,DB是Oracle 11g。

2 个答案:

答案 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设置为此类的完全限定名称来使用。

请注意,这不是一般解决方案,只有在数据库/方言使用全局临时表(与会话/事务特定相对)时才有效。