HHH-15550 New global and persistent temporary `create_tables` params

This commit is contained in:
Marco Belladelli 2022-12-15 10:43:34 +01:00 committed by Christian Beikov
parent a12a108f59
commit caaaa71ffd
6 changed files with 147 additions and 24 deletions

View File

@ -565,11 +565,20 @@ therefore reusing its execution plan.
`*hibernate.query.mutation_strategy*` (e.g. A fully-qualified class name, an instance, or a `Class` object reference)::
Provide a custom https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/query/sqm/mutation/spi/SqmMultiTableMutationStrategy.html[`org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy`] implementation for handling multi-table bulk HQL operations.
`*hibernate.hql.bulk_id_strategy.global_temporary.create_tables*` (e.g. `true` (default value) or `false`)::
For databases that don't support local tables, but just global ones, this configuration property allows you to control whether to CREATE the global tables used for multi-table bulk HQL operations at `SessionFactory` or the `EntityManagerFactory` startup.
`*hibernate.hql.bulk_id_strategy.global_temporary.drop_tables*` (e.g. `true` or `false` (default value))::
For databases that don't support local tables, but just global ones, this configuration property allows you to DROP the global tables used for multi-table bulk HQL operations when the `SessionFactory` or the `EntityManagerFactory` is closed.
`*hibernate.hql.bulk_id_strategy.local_temporary.drop_tables*` (e.g. `true` or `false` (default value))::
This configuration property allows you to DROP the local temporary tables used for multi-table bulk HQL operations when the `SessionFactory` or the `EntityManagerFactory` is closed. This is useful when testing with a single connection pool against different schemas.
This configuration property allows you to DROP the local temporary tables used for multi-table bulk HQL operations when the `SessionFactory` or `EntityManagerFactory` is closed. This is useful when testing with a single connection pool against different schemas.
`*hibernate.hql.bulk_id_strategy.persistent.create_tables*` (e.g. `true` (default value) or `false`)::
This configuration property is used by the https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/hql/spi/id/persistent/PersistentTableBulkIdStrategy.html[`PersistentTableBulkIdStrategy`], that mimics temporary tables for databases which do not support temporary tables.
It follows a pattern similar to the ANSI SQL definition of the global temporary table using a "session id" column to segment rows from the various sessions.
+
This configuration property allows you to control whether to CREATE the tables used for multi-table bulk HQL operations at `SessionFactory` or `EntityManagerFactory` startup.
`*hibernate.hql.bulk_id_strategy.persistent.drop_tables*` (e.g. `true` or `false` (default value))::
This configuration property is used by the https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/hql/spi/id/persistent/PersistentTableBulkIdStrategy.html[`PersistentTableBulkIdStrategy`], that mimics temporary tables for databases which do not support temporary tables.

View File

@ -28,6 +28,9 @@ public class GlobalTemporaryTableStrategy {
private static final Logger log = Logger.getLogger( GlobalTemporaryTableStrategy.class );
public static final String SHORT_NAME = "global_temporary";
public static final String CREATE_ID_TABLES = "hibernate.hql.bulk_id_strategy.global_temporary.create_tables";
public static final String DROP_ID_TABLES = "hibernate.hql.bulk_id_strategy.global_temporary.drop_tables";
private final TemporaryTable temporaryTable;
@ -35,6 +38,7 @@ public class GlobalTemporaryTableStrategy {
private final SessionFactoryImplementor sessionFactory;
private boolean prepared;
private boolean dropIdTables;
public GlobalTemporaryTableStrategy(
@ -55,6 +59,19 @@ public class GlobalTemporaryTableStrategy {
return;
}
final ConfigurationService configService = mappingModelCreationProcess.getCreationContext()
.getBootstrapContext()
.getServiceRegistry().getService( ConfigurationService.class );
boolean createIdTables = configService.getSetting(
CREATE_ID_TABLES,
StandardConverters.BOOLEAN,
true
);
if ( !createIdTables ) {
return;
}
prepared = true;
log.debugf( "Creating global-temp ID table : %s", getTemporaryTable().getTableExpression() );
@ -79,9 +96,6 @@ public class GlobalTemporaryTableStrategy {
try {
temporaryTableCreationWork.execute( connection );
final ConfigurationService configService = mappingModelCreationProcess.getCreationContext()
.getBootstrapContext()
.getServiceRegistry().getService( ConfigurationService.class );
this.dropIdTables = configService.getSetting(
DROP_ID_TABLES,
StandardConverters.BOOLEAN,

View File

@ -22,13 +22,6 @@ import org.hibernate.query.sqm.tree.insert.SqmInsertStatement;
*/
public class PersistentTableInsertStrategy extends PersistentTableStrategy implements SqmMultiTableInsertStrategy {
public static final String SHORT_NAME = "persistent";
public static final String DROP_ID_TABLES = "hibernate.hql.bulk_id_strategy.persistent.drop_tables";
public static final String SCHEMA = "hibernate.hql.bulk_id_strategy.persistent.schema";
public static final String CATALOG = "hibernate.hql.bulk_id_strategy.persistent.catalog";
public PersistentTableInsertStrategy(
TemporaryTable entityTable,
SessionFactoryImplementor sessionFactory) {

View File

@ -11,6 +11,8 @@ import java.sql.SQLException;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.dialect.temptable.TemporaryTableHelper;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.config.spi.StandardConverters;
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
@ -29,9 +31,12 @@ public abstract class PersistentTableStrategy {
public static final String SHORT_NAME = "persistent";
public static final String CREATE_ID_TABLES = "hibernate.hql.bulk_id_strategy.persistent.create_tables";
public static final String DROP_ID_TABLES = "hibernate.hql.bulk_id_strategy.persistent.drop_tables";
public static final String SCHEMA = "hibernate.hql.bulk_id_strategy.persistent.schema";
public static final String CATALOG = "hibernate.hql.bulk_id_strategy.persistent.catalog";
private final TemporaryTable temporaryTable;
@ -39,8 +44,8 @@ public abstract class PersistentTableStrategy {
private final SessionFactoryImplementor sessionFactory;
private boolean prepared;
private boolean created;
private boolean released;
private boolean dropIdTables;
public PersistentTableStrategy(
TemporaryTable temporaryTable,
@ -60,6 +65,19 @@ public abstract class PersistentTableStrategy {
return;
}
final ConfigurationService configService = mappingModelCreationProcess.getCreationContext()
.getBootstrapContext()
.getServiceRegistry().getService( ConfigurationService.class );
boolean createIdTables = configService.getSetting(
CREATE_ID_TABLES,
StandardConverters.BOOLEAN,
true
);
if (!createIdTables ) {
return;
}
prepared = true;
log.debugf( "Creating persistent ID table : %s", getTemporaryTable().getTableExpression() );
@ -84,7 +102,11 @@ public abstract class PersistentTableStrategy {
try {
temporaryTableCreationWork.execute( connection );
created = true;
this.dropIdTables = configService.getSetting(
DROP_ID_TABLES,
StandardConverters.BOOLEAN,
false
);
}
finally {
try {
@ -93,24 +115,16 @@ public abstract class PersistentTableStrategy {
catch (SQLException ignore) {
}
}
if ( created ) {
// todo (6.0) : register strategy for dropping of the table if requested - DROP_ID_TABLES
}
}
public void release(
SessionFactoryImplementor sessionFactory,
JdbcConnectionAccess connectionAccess) {
if ( released ) {
if ( !dropIdTables ) {
return;
}
released = true;
if ( !created ) {
return;
}
dropIdTables = false;
final TemporaryTable temporaryTable = getTemporaryTable();
log.debugf( "Dropping persistent ID table : %s", temporaryTable.getTableExpression() );

View File

@ -0,0 +1,91 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.test.persister.entity;
import java.util.Set;
import org.hibernate.dialect.OracleDialect;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableStrategy;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.logger.LoggerInspectionRule;
import org.hibernate.testing.logger.Triggerable;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.RequiresDialect;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.Rule;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.jboss.logging.Logger;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.SecondaryTable;
import jakarta.persistence.Table;
import static org.junit.jupiter.api.Assertions.assertFalse;
/**
* @author Marco Belladelli
*/
@ServiceRegistry(settings = {
@Setting(name = GlobalTemporaryTableStrategy.CREATE_ID_TABLES, value = "false")
})
@DomainModel(annotatedClasses = {
TemporaryTableStrategyTest.Bar.class
})
@SessionFactory
@RequiresDialect(OracleDialect.class)
public class TemporaryTableStrategyTest {
@Rule
public LoggerInspectionRule logInspection = new LoggerInspectionRule(
Logger.getMessageLogger( CoreMessageLogger.class, GlobalTemporaryTableStrategy.class.getName() )
);
private final Triggerable triggerable = logInspection.watchForLogMessages( Set.of(
"Creating global-temp ID table",
"Dropping global-temp ID table"
) );
@Test
@TestForIssue(jiraKey = "HHH-15550")
public void testGlobalTemporaryTableStrategy(SessionFactoryScope scope) {
scope.inTransaction( s -> {
Bar bar = new Bar();
bar.id = 1;
bar.name = "Noble";
bar.name2 = "Experiment";
s.persist( bar );
assertFalse( triggerable.wasTriggered(), "Message was triggered" );
} );
}
@Entity(name = "BAR")
@Table(name = "BAR")
@SecondaryTable(name = "BAR2")
public static class Bar {
@Id
@Column(name = "ID")
public Integer id;
@Column(name = "name")
public String name;
@Column(name = "name2", table = "BAR2")
public String name2;
}
}

View File

@ -104,6 +104,8 @@ logger.entity-action.name=org.hibernate.action.internal.EntityAction
logger.cascade.name=org.hibernate.engine.internal.Cascade
#logger.cascade.level=trace
logger.global-temp-strategy.name=org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableStrategy
logger.global-temp-strategy.level=trace
logger.merged-entity-copies.name=org.hibernate.event.internal.EntityCopyAllowedLoggedObserver
### When entity copy merge functionality is enabled using: