diff --git a/hibernate-core/src/main/java/org/hibernate/context/internal/JTASessionContext.java b/hibernate-core/src/main/java/org/hibernate/context/internal/JTASessionContext.java index 142692241536..74a3e420d386 100644 --- a/hibernate-core/src/main/java/org/hibernate/context/internal/JTASessionContext.java +++ b/hibernate-core/src/main/java/org/hibernate/context/internal/JTASessionContext.java @@ -9,6 +9,8 @@ import jakarta.transaction.Synchronization; import jakarta.transaction.Transaction; +import jakarta.transaction.TransactionManager; +import org.checkerframework.checker.nullness.qual.NonNull; import org.hibernate.FlushMode; import org.hibernate.HibernateException; import org.hibernate.Session; @@ -62,18 +64,50 @@ public Session currentSession() throws HibernateException { throw new HibernateException( "No TransactionManagerLookup specified" ); } - Transaction txn; + final var txn = getTransaction( transactionManager ); + final Object txnIdentifier = jtaPlatform.getTransactionIdentifier( txn ); + + Session currentSession = currentSessionMap.get( txnIdentifier ); + if ( currentSession == null ) { + currentSession = buildOrObtainSession(); + registerSynchronization( txn, txnIdentifier, currentSession ); + currentSessionMap.put( txnIdentifier, currentSession ); + } + else { + validateExistingSession( currentSession ); + } + + return currentSession; + } + + private void registerSynchronization(Transaction txn, Object txnIdentifier, Session currentSession) { + try { + txn.registerSynchronization( buildCleanupSynch( txnIdentifier ) ); + } + catch ( Throwable t ) { + try { + currentSession.close(); + } + catch ( Throwable e ) { + CURRENT_SESSION_LOGGER.unableToReleaseGeneratedCurrentSessionOnFailedSynchronizationRegistration(e); + } + throw new HibernateException( "Unable to register cleanup Synchronization with TransactionManager" ); + } + } + + private static @NonNull Transaction getTransaction(TransactionManager transactionManager) { try { - txn = transactionManager.getTransaction(); - if ( txn == null ) { + final var transaction = transactionManager.getTransaction(); + if ( transaction == null ) { throw new HibernateException( "Unable to locate current JTA transaction" ); } - if ( !isActive( txn.getStatus() ) ) { + if ( !isActive( transaction.getStatus() ) ) { // We could register the session against the transaction even though it is // not started, but we'd have no guarantee of ever getting the map // entries cleaned up (aside from spawning threads). throw new HibernateException( "Current transaction is not in progress" ); } + return transaction; } catch ( HibernateException e ) { throw e; @@ -81,34 +115,6 @@ public Session currentSession() throws HibernateException { catch ( Throwable t ) { throw new HibernateException( "Problem locating/validating JTA transaction", t ); } - - final Object txnIdentifier = jtaPlatform.getTransactionIdentifier( txn ); - - Session currentSession = currentSessionMap.get( txnIdentifier ); - - if ( currentSession == null ) { - currentSession = buildOrObtainSession(); - - try { - txn.registerSynchronization( buildCleanupSynch( txnIdentifier ) ); - } - catch ( Throwable t ) { - try { - currentSession.close(); - } - catch ( Throwable e ) { - CURRENT_SESSION_LOGGER.unableToReleaseGeneratedCurrentSessionOnFailedSynchronizationRegistration(e); - } - throw new HibernateException( "Unable to register cleanup Synchronization with TransactionManager" ); - } - - currentSessionMap.put( txnIdentifier, currentSession ); - } - else { - validateExistingSession( currentSession ); - } - - return currentSession; } /** diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/ExecuteWithTemporaryTableHelper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/ExecuteWithTemporaryTableHelper.java index da2b5ec53728..6026f939bc87 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/ExecuteWithTemporaryTableHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/ExecuteWithTemporaryTableHelper.java @@ -5,19 +5,15 @@ package org.hibernate.query.sqm.mutation.internal.temptable; import org.hibernate.LockMode; -import org.hibernate.LockOptions; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.temptable.TemporaryTable; -import org.hibernate.dialect.temptable.TemporaryTableColumn; import org.hibernate.dialect.temptable.TemporaryTableHelper; import org.hibernate.dialect.temptable.TemporaryTableHelper.TemporaryTableCreationWork; import org.hibernate.dialect.temptable.TemporaryTableHelper.TemporaryTableDropWork; -import org.hibernate.dialect.temptable.TemporaryTableSessionUidColumn; import org.hibernate.dialect.temptable.TemporaryTableStrategy; -import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; -import org.hibernate.engine.jdbc.spi.JdbcCoordinator; -import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.jdbc.AbstractReturningWork; +import org.hibernate.jdbc.AbstractWork; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.query.sqm.ComparisonOperator; @@ -32,12 +28,10 @@ import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.ast.tree.from.NamedTableReference; import org.hibernate.sql.ast.tree.from.StandardTableGroup; -import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; import org.hibernate.sql.ast.tree.predicate.Predicate; -import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation; @@ -45,7 +39,6 @@ import org.hibernate.sql.results.internal.SqlSelectionImpl; import java.sql.PreparedStatement; -import java.sql.ResultSet; import java.sql.SQLException; import java.util.Map; import java.util.UUID; @@ -68,30 +61,30 @@ public static CacheableSqmInterpretation { - querySpec.getFromClause().visitTableJoins( - tableJoin -> { - if ( tableJoin.isInitialized() - && tableJoin.getJoinType() != SqlAstJoinType.INNER ) { - lockOptions.setLockMode( lockMode ); - } - } - ); - } - ); - } - final var jdbcInsert = jdbcEnvironment.getSqlAstTranslatorFactory() - .buildMutationTranslator( factory, idTableInsert ) - .translate( jdbcParameterBindings, executionContext.getQueryOptions() ); - lockOptions.setLockMode( lockMode ); - - return new CacheableSqmInterpretation<>( - idTableInsert, - jdbcInsert, - Map.of(), - Map.of() - ); + return createTemporaryTableInsert( idTableInsert, jdbcParameterBindings, executionContext ); } public static CacheableSqmInterpretation createTemporaryTableInsert( @@ -164,74 +123,67 @@ public static CacheableSqmInterpretation { - querySpec.getFromClause().visitTableJoins( - tableJoin -> { - if ( tableJoin.isInitialized() - && tableJoin.getJoinType() != SqlAstJoinType.INNER ) { - lockOptions.setLockMode( lockMode ); - } - } - ); + sourceSelectStatement.visitQuerySpecs( querySpec -> { + querySpec.getFromClause().visitTableJoins( tableJoin -> { + if ( tableJoin.isInitialized() + && tableJoin.getJoinType() != SqlAstJoinType.INNER ) { + lockOptions.setLockMode( lockMode ); } - ); + } ); + } ); } - final var jdbcInsert = jdbcEnvironment.getSqlAstTranslatorFactory() - .buildMutationTranslator( factory, temporaryTableInsert ) - .translate( jdbcParameterBindings, executionContext.getQueryOptions() ); lockOptions.setLockMode( lockMode ); return new CacheableSqmInterpretation<>( temporaryTableInsert, - jdbcInsert, + jdbcEnvironment.getSqlAstTranslatorFactory() + .buildMutationTranslator( factory, temporaryTableInsert ) + .translate( jdbcParameterBindings, executionContext.getQueryOptions() ), Map.of(), Map.of() ); } + @Deprecated(forRemoval = true, since = "7.3") // no longer used public static int saveIntoTemporaryTable( InsertSelectStatement temporaryTableInsert, JdbcParameterBindings jdbcParameterBindings, ExecutionContext executionContext) { final var factory = executionContext.getSession().getFactory(); - final JdbcServices jdbcServices = factory.getJdbcServices(); - final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment(); - final LockOptions lockOptions = executionContext.getQueryOptions().getLockOptions(); - final LockMode lockMode = lockOptions.getLockMode(); + final var jdbcEnvironment = factory.getJdbcServices().getJdbcEnvironment(); + final var lockOptions = executionContext.getQueryOptions().getLockOptions(); + final var lockMode = lockOptions.getLockMode(); // Acquire a WRITE lock for the rows that are about to be modified lockOptions.setLockMode( LockMode.WRITE ); // Visit the table joins and reset the lock mode if we encounter OUTER joins that are not supported - final QueryPart sourceSelectStatement = temporaryTableInsert.getSourceSelectStatement(); + final var sourceSelectStatement = temporaryTableInsert.getSourceSelectStatement(); if ( sourceSelectStatement != null && !jdbcEnvironment.getDialect().supportsOuterJoinForUpdate() ) { - sourceSelectStatement.visitQuerySpecs( - querySpec -> { - querySpec.getFromClause().visitTableJoins( - tableJoin -> { - if ( tableJoin.isInitialized() - && tableJoin.getJoinType() != SqlAstJoinType.INNER ) { - lockOptions.setLockMode( lockMode ); - } - } - ); + sourceSelectStatement.visitQuerySpecs( querySpec -> { + querySpec.getFromClause().visitTableJoins( tableJoin -> { + if ( tableJoin.isInitialized() + && tableJoin.getJoinType() != SqlAstJoinType.INNER ) { + lockOptions.setLockMode( lockMode ); } - ); + } ); + } ); } - final var jdbcInsert = jdbcEnvironment.getSqlAstTranslatorFactory() - .buildMutationTranslator( factory, temporaryTableInsert ) - .translate( jdbcParameterBindings, executionContext.getQueryOptions() ); lockOptions.setLockMode( lockMode ); - return saveIntoTemporaryTable( jdbcInsert, jdbcParameterBindings, executionContext ); + return saveIntoTemporaryTable( + jdbcEnvironment.getSqlAstTranslatorFactory() + .buildMutationTranslator( factory, temporaryTableInsert ) + .translate( jdbcParameterBindings, executionContext.getQueryOptions() ), + jdbcParameterBindings, + executionContext + ); } public static int saveIntoTemporaryTable( @@ -253,7 +205,13 @@ public static QuerySpec createIdTableSelectQuerySpec( JdbcParameter sessionUidParameter, EntityMappingType entityDescriptor, ExecutionContext executionContext) { - return createIdTableSelectQuerySpec( idTable, null, sessionUidParameter, entityDescriptor, executionContext ); + return createIdTableSelectQuerySpec( + idTable, + null, + sessionUidParameter, + entityDescriptor, + executionContext + ); } public static QuerySpec createIdTableSelectQuerySpec( @@ -262,14 +220,14 @@ public static QuerySpec createIdTableSelectQuerySpec( JdbcParameter sessionUidParameter, EntityMappingType entityDescriptor, ExecutionContext executionContext) { - final QuerySpec querySpec = new QuerySpec( false ); + final var querySpec = new QuerySpec( false ); - final NamedTableReference idTableReference = new NamedTableReference( + final var idTableReference = new NamedTableReference( idTable.getTableExpression(), TemporaryTable.DEFAULT_ALIAS, true ); - final TableGroup idTableGroup = new StandardTableGroup( + final var idTableGroup = new StandardTableGroup( true, new NavigablePath( idTableReference.getTableExpression() ), entityDescriptor, @@ -282,7 +240,7 @@ public static QuerySpec createIdTableSelectQuerySpec( querySpec.getFromClause().addRoot( idTableGroup ); applyIdTableSelections( querySpec, idTableReference, idTable, fkModelPart, entityDescriptor ); - applyIdTableRestrictions( querySpec, idTableReference, idTable, sessionUidParameter, executionContext ); + applyIdTableRestrictions( querySpec, idTableReference, idTable, sessionUidParameter ); return querySpec; } @@ -314,22 +272,20 @@ private static void applyIdTableSelections( } } else { - fkModelPart.forEachSelectable( - (i, selectableMapping) -> { - querySpec.getSelectClause().addSqlSelection( - new SqlSelectionImpl( - i, - new ColumnReference( - tableReference, - selectableMapping.getSelectionExpression(), - false, - null, - selectableMapping.getJdbcMapping() - ) + fkModelPart.forEachSelectable( (i, selectableMapping) -> { + querySpec.getSelectClause().addSqlSelection( + new SqlSelectionImpl( + i, + new ColumnReference( + tableReference, + selectableMapping.getSelectionExpression(), + false, + null, + selectableMapping.getJdbcMapping() ) - ); - } - ); + ) + ); + } ); } } @@ -337,17 +293,17 @@ private static void applyIdTableRestrictions( QuerySpec querySpec, TableReference idTableReference, TemporaryTable idTable, - JdbcParameter sessionUidParameter, - ExecutionContext executionContext) { - if ( idTable.getSessionUidColumn() != null ) { + JdbcParameter sessionUidParameter) { + final var sessionUidColumn = idTable.getSessionUidColumn(); + if ( sessionUidColumn != null ) { querySpec.applyPredicate( new ComparisonPredicate( new ColumnReference( idTableReference, - idTable.getSessionUidColumn().getColumnName(), + sessionUidColumn.getColumnName(), false, null, - idTable.getSessionUidColumn().getJdbcMapping() + sessionUidColumn.getJdbcMapping() ), ComparisonOperator.EQUAL, sessionUidParameter @@ -362,7 +318,8 @@ public static void performBeforeTemporaryTableUseActions( ExecutionContext executionContext) { performBeforeTemporaryTableUseActions( temporaryTable, - executionContext.getSession().getDialect().getTemporaryTableBeforeUseAction(), + executionContext.getSession().getDialect() + .getTemporaryTableBeforeUseAction(), executionContext ); } @@ -383,36 +340,21 @@ private static boolean performBeforeTemporaryTableUseActions( BeforeUseAction beforeUseAction, ExecutionContext executionContext) { final var factory = executionContext.getSession().getFactory(); - final Dialect dialect = factory.getJdbcServices().getDialect(); - if ( beforeUseAction == BeforeUseAction.CREATE ) { - final var temporaryTableCreationWork = - new TemporaryTableCreationWork( temporaryTable, factory ); - final var ddlTransactionHandling = dialect.getTemporaryTableDdlTransactionHandling(); - if ( ddlTransactionHandling == NONE ) { - return executionContext.getSession().doReturningWork( temporaryTableCreationWork ); - } - else { - final var isolationDelegate = - executionContext.getSession().getJdbcCoordinator().getJdbcSessionOwner() - .getTransactionCoordinator().createIsolationDelegate(); - return isolationDelegate.delegateWork( temporaryTableCreationWork, - ddlTransactionHandling == ISOLATE_AND_TRANSACT ); - } - } - else { - return false; - } + final var dialect = factory.getJdbcServices().getDialect(); + return beforeUseAction == BeforeUseAction.CREATE + && doWork( executionContext, dialect, + new TemporaryTableCreationWork( temporaryTable, factory ) ); } - public static int[] loadInsertedRowNumbers( - TemporaryTable temporaryTable, - Function sessionUidAccess, - int rows, - ExecutionContext executionContext) { - final String sqlSelect = - createInsertedRowNumbersSelectSql( temporaryTable, sessionUidAccess, executionContext ); - return loadInsertedRowNumbers( sqlSelect, temporaryTable, sessionUidAccess, rows, executionContext ); - } +// public static int[] loadInsertedRowNumbers( +// TemporaryTable temporaryTable, +// Function sessionUidAccess, +// int rows, +// ExecutionContext executionContext) { +// final String sqlSelect = +// createInsertedRowNumbersSelectSql( temporaryTable, sessionUidAccess, executionContext ); +// return loadInsertedRowNumbers( sqlSelect, temporaryTable, sessionUidAccess, rows, executionContext ); +// } public static int[] loadInsertedRowNumbers( String sqlSelect, @@ -420,9 +362,9 @@ public static int[] loadInsertedRowNumbers( Function sessionUidAccess, int rows, ExecutionContext executionContext) { - final TemporaryTableSessionUidColumn sessionUidColumn = temporaryTable.getSessionUidColumn(); - final SharedSessionContractImplementor session = executionContext.getSession(); - final JdbcCoordinator jdbcCoordinator = session.getJdbcCoordinator(); + final var sessionUidColumn = temporaryTable.getSessionUidColumn(); + final var session = executionContext.getSession(); + final var jdbcCoordinator = session.getJdbcCoordinator(); PreparedStatement preparedStatement = null; try { preparedStatement = jdbcCoordinator.getStatementPreparer().prepareStatement( sqlSelect ); @@ -435,8 +377,8 @@ public static int[] loadInsertedRowNumbers( session ); } - final ResultSet resultSet = jdbcCoordinator.getResultSetReturn().execute( preparedStatement, sqlSelect ); - final int[] rowNumbers = new int[rows]; + final var resultSet = jdbcCoordinator.getResultSetReturn().execute( preparedStatement, sqlSelect ); + final var rowNumbers = new int[rows]; try { int rowIndex = 0; while (resultSet.next()) { @@ -466,18 +408,19 @@ public static int[] loadInsertedRowNumbers( public static String createInsertedRowNumbersSelectSql( TemporaryTable temporaryTable, - Function sessionUidAccess, ExecutionContext executionContext) { - final TemporaryTableSessionUidColumn sessionUidColumn = temporaryTable.getSessionUidColumn(); + final var sessionUidColumn = temporaryTable.getSessionUidColumn(); - final TemporaryTableColumn rowNumberColumn = temporaryTable.getColumns() - .get( temporaryTable.getColumns().size() - (sessionUidColumn == null ? 1 : 2 ) ); + final var rowNumberColumn = + temporaryTable.getColumns() + .get( temporaryTable.getColumns().size() - (sessionUidColumn == null ? 1 : 2 ) ); assert rowNumberColumn != null; - final SharedSessionContractImplementor session = executionContext.getSession(); - final SimpleSelect simpleSelect = new SimpleSelect( session.getFactory() ) - .setTableName( temporaryTable.getQualifiedTableName() ) - .addColumn( rowNumberColumn.getColumnName() ); + final var session = executionContext.getSession(); + final var simpleSelect = + new SimpleSelect( session.getFactory() ) + .setTableName( temporaryTable.getQualifiedTableName() ) + .addColumn( rowNumberColumn.getColumnName() ); if ( sessionUidColumn != null ) { simpleSelect.addRestriction( sessionUidColumn.getColumnName() ); } @@ -490,7 +433,7 @@ public static void performAfterTemporaryTableUseActions( AfterUseAction afterUseAction, ExecutionContext executionContext) { final var factory = executionContext.getSession().getFactory(); - final Dialect dialect = factory.getJdbcServices().getDialect(); + final var dialect = factory.getJdbcServices().getDialect(); switch ( afterUseAction ) { case CLEAN: TemporaryTableHelper.cleanTemporaryTableRows( @@ -501,18 +444,43 @@ public static void performAfterTemporaryTableUseActions( ); break; case DROP: - final var temporaryTableDropWork = new TemporaryTableDropWork( temporaryTable, factory ); - final var ddlTransactionHandling = dialect.getTemporaryTableDdlTransactionHandling(); - if ( ddlTransactionHandling == NONE ) { - executionContext.getSession().doWork( temporaryTableDropWork ); - } - else { - final var isolationDelegate = - executionContext.getSession().getJdbcCoordinator().getJdbcSessionOwner() - .getTransactionCoordinator().createIsolationDelegate(); - isolationDelegate.delegateWork( temporaryTableDropWork, + doWork( executionContext, dialect, + new TemporaryTableDropWork( temporaryTable, factory ) ); + } + } + + private static T doWork( + ExecutionContext executionContext, Dialect dialect, + AbstractReturningWork temporaryTableCreationWork) { + final var ddlTransactionHandling = dialect.getTemporaryTableDdlTransactionHandling(); + if ( ddlTransactionHandling == NONE ) { + return executionContext.getSession().doReturningWork( temporaryTableCreationWork ); + } + else { + // this branch is obsolete, since + // dialect.getTemporaryTableDdlTransactionHandling() + // now always returns NONE + return executionContext.getSession().getJdbcCoordinator().getJdbcSessionOwner() + .getTransactionCoordinator().createIsolationDelegate() + .delegateWork( temporaryTableCreationWork, + ddlTransactionHandling == ISOLATE_AND_TRANSACT ); + } + } + private static void doWork( + ExecutionContext executionContext, Dialect dialect, + AbstractWork temporaryTableDropWork) { + final var ddlTransactionHandling = dialect.getTemporaryTableDdlTransactionHandling(); + if ( ddlTransactionHandling == NONE ) { + executionContext.getSession().doWork( temporaryTableDropWork ); + } + else { + // this branch is obsolete, since + // dialect.getTemporaryTableDdlTransactionHandling() + // now always returns NONE + executionContext.getSession().getJdbcCoordinator().getJdbcSessionOwner() + .getTransactionCoordinator().createIsolationDelegate() + .delegateWork( temporaryTableDropWork, ddlTransactionHandling == ISOLATE_AND_TRANSACT ); - } } } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedInsertHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedInsertHandler.java index 4963dbb7be21..0941785e37a7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedInsertHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedInsertHandler.java @@ -677,7 +677,6 @@ private RootTableInserter createRootTableInserter( .translate( null, executionContext.getQueryOptions() ); temporaryTableRowNumberSelectSql = ExecuteWithTemporaryTableHelper.createInsertedRowNumbersSelectSql( entityTable, - sessionUidAccess, executionContext ); }