Hibernate Multi-table Bulk Operations always try to create the temporary table Hibernate Multi-table Bulk Operations always try to create the temporary table oracle oracle

Hibernate Multi-table Bulk Operations always try to create the temporary table


I think this is a bug in TemporaryTableBulkIdStrategy, because when using the Oracle8iDialect says that temporary tables shouldn't be deleted:

@Overridepublic boolean dropTemporaryTableAfterUse() {    return false;}

But this check is made only when deleting the table:

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                }            }        }    }}

but now when creating the table:

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();    }}

As a workaround, you could extend the Oracle dialect and override the dropTemporaryTableAfterUse method to return false.

I filled the HHH-9744 issue for this.


With Vlad pointing me in the right direction I came up with the following workaround to cache the names of already created temporary tables:

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);        }    }}

This can be used by setting the property hibernate.hql.bulk_id_strategy to the fully qualified name of this class.

Please not that this is not a general solution and only works if the database/dialect uses global temporary tables (opposed to session/transaction specific).