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).