* Cleanup temporary table handling in dialects

* Fix insert handling with respect to version, discriminator and generated identifier insertion
* Introduce SqmMultiTableInsertStrategy to handle multi-table inserts
* Introduce the notion of an "entity table" similar to the "id table" to handle multi-table insertions
* Implement table based and cte based multi-table insertion to support all dialects
* Implement identifier generator optimizer support for multi-table insert
* Fix validation of insert target paths against select item types
* Fix some DML validations
* Implement over-clause support in SQL AST
* Fix multi-valued filter parameter support
This commit is contained in:
Christian Beikov 2021-11-30 01:40:02 +01:00
parent c959c7656c
commit 6c4ec95182
253 changed files with 6674 additions and 1830 deletions

View File

@ -40,10 +40,11 @@ import org.hibernate.query.CastType;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction;
import org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy;
import org.hibernate.query.sqm.mutation.internal.idtable.IdTable;
import org.hibernate.query.sqm.mutation.internal.idtable.TempIdTableExporter;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableInsertStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableMutationStrategy;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.dialect.temptable.TemporaryTableKind;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
@ -484,6 +485,11 @@ public class FirebirdDialect extends Dialect {
return getVersion() < 400 ? 20 : 52;
}
@Override
public int getMaxIdentifierLength() {
return getVersion() < 400 ? 31 : 63;
}
public IdentifierHelper buildIdentifierHelper(
IdentifierHelperBuilder builder,
DatabaseMetaData dbMetaData) throws SQLException {
@ -869,16 +875,39 @@ public class FirebirdDialect extends Dialect {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType entityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) {
return getVersion() < 210
? super.getFallbackSqmMutationStrategy( entityDescriptor, runtimeModelCreationContext )
: new GlobalTemporaryTableStrategy(
new IdTable( entityDescriptor, name -> "HT_" + name, this, runtimeModelCreationContext ),
() -> new TempIdTableExporter( false, this::getTypeName ) {
@Override
protected String getCreateOptions() {
return "on commit delete rows";
}
},
AfterUseAction.CLEAN,
: new GlobalTemporaryTableMutationStrategy(
TemporaryTable.createIdTable(
entityDescriptor,
name -> TemporaryTable.ID_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(EntityMappingType entityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) {
return getVersion() < 210
? super.getFallbackSqmInsertStrategy( entityDescriptor, runtimeModelCreationContext )
: new GlobalTemporaryTableInsertStrategy(
TemporaryTable.createEntityTable(
entityDescriptor,
name -> TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public TemporaryTableKind getSupportedTemporaryTableKind() {
return TemporaryTableKind.GLOBAL;
}
@Override
public String getTemporaryTableCreateOptions() {
return "on commit delete rows";
}
}

View File

@ -6,7 +6,6 @@
*/
package org.hibernate.community.dialect;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.Replacer;
import org.hibernate.dialect.function.CommonFunctionFactory;
@ -33,10 +32,13 @@ import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction;
import org.hibernate.query.sqm.mutation.internal.idtable.IdTable;
import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy;
import org.hibernate.query.sqm.mutation.internal.idtable.TempIdTableExporter;
import org.hibernate.query.sqm.mutation.internal.temptable.AfterUseAction;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.query.sqm.mutation.internal.temptable.BeforeUseAction;
import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableInsertStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableMutationStrategy;
import org.hibernate.dialect.temptable.TemporaryTableKind;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.sql.SqmTranslator;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
@ -387,23 +389,55 @@ public class InformixDialect extends Dialect {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new LocalTemporaryTableStrategy(
new IdTable( rootEntityDescriptor, basename -> "HT_" + basename, this, runtimeModelCreationContext ),
() -> new TempIdTableExporter( true, this::getTypeName ) {
return new LocalTemporaryTableMutationStrategy(
TemporaryTable.createIdTable(
rootEntityDescriptor,
basename -> TemporaryTable.ID_TABLE_PREFIX + basename,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
protected String getCreateCommand() {
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new LocalTemporaryTableInsertStrategy(
TemporaryTable.createEntityTable(
rootEntityDescriptor,
name -> TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public TemporaryTableKind getSupportedTemporaryTableKind() {
return TemporaryTableKind.LOCAL;
}
@Override
public String getTemporaryTableCreateOptions() {
return "with no log";
}
@Override
public String getTemporaryTableCreateCommand() {
return "create temp table";
}
@Override
protected String getCreateOptions() {
return "with no log";
public AfterUseAction getTemporaryTableAfterUseAction() {
return AfterUseAction.NONE;
}
},
AfterUseAction.NONE,
TempTableDdlTransactionHandling.NONE,
runtimeModelCreationContext.getSessionFactory()
);
@Override
public BeforeUseAction getTemporaryTableBeforeUseAction() {
return BeforeUseAction.CREATE;
}
@Override

View File

@ -31,10 +31,11 @@ import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction;
import org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy;
import org.hibernate.query.sqm.mutation.internal.idtable.IdTable;
import org.hibernate.query.sqm.mutation.internal.idtable.TempIdTableExporter;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableInsertStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableMutationStrategy;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.dialect.temptable.TemporaryTableKind;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.sql.SqmTranslator;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
@ -412,23 +413,46 @@ public class IngresDialect extends Dialect {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new GlobalTemporaryTableStrategy(
new IdTable( rootEntityDescriptor, name -> "session." + name, this, runtimeModelCreationContext ),
() -> new TempIdTableExporter( false, this::getTypeName ) {
return new GlobalTemporaryTableMutationStrategy(
TemporaryTable.createIdTable(
rootEntityDescriptor,
name -> "session." + TemporaryTable.ID_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
protected String getCreateOptions() {
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new GlobalTemporaryTableInsertStrategy(
TemporaryTable.createEntityTable(
rootEntityDescriptor,
name -> "session." + TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public TemporaryTableKind getSupportedTemporaryTableKind() {
return TemporaryTableKind.GLOBAL;
}
@Override
public String getTemporaryTableCreateOptions() {
return "on commit preserve rows with norecovery";
}
@Override
protected String getCreateCommand() {
public String getTemporaryTableCreateCommand() {
return "declare global temporary table";
}
},
AfterUseAction.CLEAN,
runtimeModelCreationContext.getSessionFactory()
);
}
// union subclass support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -31,10 +31,11 @@ import org.hibernate.query.IntervalType;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction;
import org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy;
import org.hibernate.query.sqm.mutation.internal.idtable.IdTable;
import org.hibernate.query.sqm.mutation.internal.idtable.TempIdTableExporter;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableInsertStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableMutationStrategy;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.dialect.temptable.TemporaryTableKind;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.sql.ForUpdateFragment;
import org.hibernate.sql.ast.SqlAstTranslator;
@ -294,43 +295,52 @@ public class TeradataDialect extends Dialect {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new GlobalTemporaryTableStrategy(
new IdTable( rootEntityDescriptor, basename -> "HT_" + basename, this, runtimeModelCreationContext ),
() -> new TempIdTableExporter( false, this::getTypeName ) {
@Override
public String getCreateOptions() {
return "on commit preserve rows";
}
},
AfterUseAction.CLEAN,
return new GlobalTemporaryTableMutationStrategy(
TemporaryTable.createIdTable(
rootEntityDescriptor,
basename -> TemporaryTable.ID_TABLE_PREFIX + basename,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
// @Override
// public String generateIdTableName(String baseName) {
// return IdTableSupportStandardImpl.INSTANCE.generateIdTableName( baseName );
// }
//
// @Override
// public String getCreateIdTableCommand() {
// return "create global temporary table";
// }
//
// @Override
// public String getCreateIdTableStatementOptions() {
// return " on commit preserve rows";
// }
//
// @Override
// public String getDropIdTableCommand() {
// return "drop table";
// }
//
// @Override
// public String getTruncateIdTableCommand() {
// return "delete from";
// }
@Override
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new GlobalTemporaryTableInsertStrategy(
TemporaryTable.createEntityTable(
rootEntityDescriptor,
name -> TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public TemporaryTableKind getSupportedTemporaryTableKind() {
return TemporaryTableKind.GLOBAL;
}
@Override
public String getTemporaryTableCreateOptions() {
return "on commit preserve rows";
}
@Override
public int getMaxAliasLength() {
// Max identifier length is 30, but Hibernate needs to add "uniqueing info" so we account for that
return 20;
}
@Override
public int getMaxIdentifierLength() {
return 30;
}
@Override
public boolean supportsCascadeDelete() {

View File

@ -25,10 +25,11 @@ import org.hibernate.persister.entity.Lockable;
import org.hibernate.query.IntervalType;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction;
import org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy;
import org.hibernate.query.sqm.mutation.internal.idtable.IdTable;
import org.hibernate.query.sqm.mutation.internal.idtable.TempIdTableExporter;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableInsertStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableMutationStrategy;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.dialect.temptable.TemporaryTableKind;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
@ -313,24 +314,42 @@ public class TimesTenDialect extends Dialect {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new GlobalTemporaryTableStrategy(
new IdTable(
return new GlobalTemporaryTableMutationStrategy(
TemporaryTable.createIdTable(
rootEntityDescriptor,
name -> name.length() > 30 ? name.substring( 0, 30 ) : name,
name -> TemporaryTable.ID_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
() -> new TempIdTableExporter( false, this::getTypeName ) {
@Override
protected String getCreateOptions() {
return "on commit delete rows";
}
},
AfterUseAction.CLEAN,
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new GlobalTemporaryTableInsertStrategy(
TemporaryTable.createEntityTable(
rootEntityDescriptor,
name -> TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public TemporaryTableKind getSupportedTemporaryTableKind() {
return TemporaryTableKind.GLOBAL;
}
@Override
public String getTemporaryTableCreateOptions() {
return "on commit delete rows";
}
@Override
public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) {
// TimesTen has no known variation of a "SELECT ... FOR UPDATE" syntax...
@ -354,6 +373,17 @@ public class TimesTenDialect extends Dialect {
}
}
@Override
public int getMaxAliasLength() {
// Max identifier length is 30, but Hibernate needs to add "uniqueing info" so we account for that
return 20;
}
@Override
public int getMaxIdentifierLength() {
return 30;
}
@Override
public boolean supportsCircularCascadeDeleteConstraints() {
return false;

View File

@ -64,6 +64,7 @@ import org.hibernate.query.criteria.ValueHandlingMode;
import org.hibernate.query.hql.HqlTranslator;
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
@ -217,6 +218,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
// Queries
private HqlTranslator hqlTranslator;
private SqmMultiTableMutationStrategy sqmMultiTableMutationStrategy;
private SqmMultiTableInsertStrategy sqmMultiTableInsertStrategy;
private SqmFunctionRegistry sqmFunctionRegistry;
private SqmTranslatorFactory sqmTranslatorFactory;
private Boolean useOfJdbcNamedParametersEnabled;
@ -430,6 +432,18 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
strategySelector
);
final String sqmInsertStrategyImplName = ConfigurationHelper.extractValue(
AvailableSettings.QUERY_MULTI_TABLE_INSERT_STRATEGY,
configurationSettings,
() -> null
);
this.sqmMultiTableInsertStrategy = resolveSqmInsertStrategy(
sqmInsertStrategyImplName,
serviceRegistry,
strategySelector
);
this.useOfJdbcNamedParametersEnabled = cfgService.getSetting( CALLABLE_NAMED_PARAMS_ENABLED, BOOLEAN, true );
this.querySubstitutions = ConfigurationHelper.toMap( QUERY_SUBSTITUTIONS, " ,=;:\n\t\r\f", configurationSettings );
@ -658,6 +672,54 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
);
}
private SqmMultiTableInsertStrategy resolveSqmInsertStrategy(
String strategyName,
StandardServiceRegistry serviceRegistry,
StrategySelector strategySelector) {
if ( strategyName == null ) {
return null;
}
return strategySelector.resolveStrategy(
SqmMultiTableInsertStrategy.class,
strategyName,
(SqmMultiTableInsertStrategy) null,
strategyClass -> {
Constructor<SqmMultiTableInsertStrategy> dialectConstructor = null;
Constructor<SqmMultiTableInsertStrategy> emptyConstructor = null;
// todo (6.0) : formalize the allowed constructor parameterizations
for ( Constructor<?> declaredConstructor : strategyClass.getDeclaredConstructors() ) {
final Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
if ( parameterTypes.length == 1 && parameterTypes[0] == Dialect.class ) {
dialectConstructor = (Constructor<SqmMultiTableInsertStrategy>) declaredConstructor;
break;
}
else if ( parameterTypes.length == 0 ) {
emptyConstructor = (Constructor<SqmMultiTableInsertStrategy>) declaredConstructor;
}
}
try {
if ( dialectConstructor != null ) {
return dialectConstructor.newInstance(
serviceRegistry.getService( JdbcServices.class ).getDialect()
);
}
else if ( emptyConstructor != null ) {
return emptyConstructor.newInstance();
}
}
catch (Exception e) {
throw new StrategySelectionException(
String.format( "Could not instantiate named strategy class [%s]", strategyClass.getName() ),
e
);
}
throw new IllegalArgumentException( "Cannot instantiate the class [" + strategyClass.getName() + "] because it does not have a constructor that accepts a dialect or an empty constructor!" );
}
);
}
private HqlTranslator resolveHqlTranslator(
String producerName,
StandardServiceRegistry serviceRegistry,
@ -898,6 +960,11 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
return sqmMultiTableMutationStrategy;
}
@Override
public SqmMultiTableInsertStrategy getCustomSqmMultiTableInsertStrategy() {
return sqmMultiTableInsertStrategy;
}
@Override
public boolean isUseOfJdbcNamedParametersEnabled() {
return this.useOfJdbcNamedParametersEnabled;

View File

@ -26,10 +26,10 @@ import org.hibernate.cache.internal.SimpleCacheKeysFactory;
import org.hibernate.cache.spi.CacheKeysFactory;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
import org.hibernate.query.sqm.mutation.internal.cte.CteStrategy;
import org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy;
import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy;
import org.hibernate.query.sqm.mutation.internal.idtable.PersistentTableStrategy;
import org.hibernate.query.sqm.mutation.internal.cte.CteMutationStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableMutationStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableMutationStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.PersistentTableMutationStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl;
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl;
@ -171,23 +171,23 @@ public class StrategySelectorBuilder {
private void addSqmMultiTableMutationStrategies(StrategySelectorImpl strategySelector) {
strategySelector.registerStrategyImplementor(
SqmMultiTableMutationStrategy.class,
CteStrategy.SHORT_NAME,
CteStrategy.class
CteMutationStrategy.SHORT_NAME,
CteMutationStrategy.class
);
strategySelector.registerStrategyImplementor(
SqmMultiTableMutationStrategy.class,
GlobalTemporaryTableStrategy.SHORT_NAME,
GlobalTemporaryTableStrategy.class
GlobalTemporaryTableMutationStrategy.SHORT_NAME,
GlobalTemporaryTableMutationStrategy.class
);
strategySelector.registerStrategyImplementor(
SqmMultiTableMutationStrategy.class,
LocalTemporaryTableStrategy.SHORT_NAME,
LocalTemporaryTableStrategy.class
LocalTemporaryTableMutationStrategy.SHORT_NAME,
LocalTemporaryTableMutationStrategy.class
);
strategySelector.registerStrategyImplementor(
SqmMultiTableMutationStrategy.class,
PersistentTableStrategy.SHORT_NAME,
PersistentTableStrategy.class
PersistentTableMutationStrategy.SHORT_NAME,
PersistentTableMutationStrategy.class
);
}

View File

@ -32,6 +32,7 @@ import org.hibernate.query.criteria.ValueHandlingMode;
import org.hibernate.query.hql.HqlTranslator;
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
@ -129,6 +130,11 @@ public class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOp
return delegate.getCustomSqmMultiTableMutationStrategy();
}
@Override
public SqmMultiTableInsertStrategy getCustomSqmMultiTableInsertStrategy() {
return delegate.getCustomSqmMultiTableInsertStrategy();
}
@Override
public StatementInspector getStatementInspector() {
return delegate.getStatementInspector();

View File

@ -950,6 +950,13 @@ public interface AvailableSettings {
*/
String QUERY_MULTI_TABLE_MUTATION_STRATEGY = "hibernate.query.mutation_strategy";
/**
* Defines the "global" strategy to use for handling HQL and Criteria insert queries.
*
* Names the {@link org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy} to use.
*/
String QUERY_MULTI_TABLE_INSERT_STRATEGY = "hibernate.query.insert_strategy";
/**
* A comma-separated list of token substitutions to use when translating a Hibernate
* query to SQL

View File

@ -1179,6 +1179,11 @@ public abstract class AbstractHANADialect extends Dialect {
return 128;
}
@Override
public int getMaxIdentifierLength() {
return 127;
}
@Override
public LimitHandler getLimitHandler() {
return LimitOffsetLimitHandler.INSTANCE;

View File

@ -8,9 +8,9 @@ package org.hibernate.dialect;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.dialect.function.CastingConcatFunction;
import org.hibernate.dialect.function.TransactSQLStrFunction;
import org.hibernate.query.NullOrdering;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.CaseLeastGreatestEmulation;
@ -20,11 +20,15 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.query.TrimSpec;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction;
import org.hibernate.query.sqm.mutation.internal.idtable.IdTable;
import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy;
import org.hibernate.query.sqm.mutation.internal.idtable.TempIdTableExporter;
import org.hibernate.query.sqm.mutation.internal.temptable.AfterUseAction;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.query.sqm.mutation.internal.temptable.BeforeUseAction;
import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableInsertStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableMutationStrategy;
import org.hibernate.dialect.temptable.TemporaryTableKind;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaTypeDescriptor;
import org.hibernate.type.descriptor.jdbc.JdbcType;
@ -126,6 +130,15 @@ public abstract class AbstractTransactSQLDialect extends Dialect {
queryEngine.getSqmFunctionRegistry().register( "least", new CaseLeastGreatestEmulation( true ) );
queryEngine.getSqmFunctionRegistry().register( "greatest", new CaseLeastGreatestEmulation( false ) );
queryEngine.getSqmFunctionRegistry().register( "str", new TransactSQLStrFunction( queryEngine.getTypeConfiguration() ) );
queryEngine.getSqmFunctionRegistry().register(
"concat",
new CastingConcatFunction(
this,
"+",
SqlAstNodeRenderingMode.DEFAULT,
queryEngine.getTypeConfiguration()
)
);
}
@Override
@ -259,21 +272,53 @@ public abstract class AbstractTransactSQLDialect extends Dialect {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
EntityMappingType entityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new LocalTemporaryTableStrategy(
new IdTable( entityDescriptor, basename -> "#" + basename, this, runtimeModelCreationContext ),
() -> new TempIdTableExporter( true, this::getTypeName ) {
@Override
protected String getCreateCommand() {
return "create table";
}
},
// // sql-server, at least needed this dropped after use; strange!
AfterUseAction.DROP,
TempTableDdlTransactionHandling.NONE,
return new LocalTemporaryTableMutationStrategy(
TemporaryTable.createIdTable(
entityDescriptor,
basename -> '#' + TemporaryTable.ID_TABLE_PREFIX + basename,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType entityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new LocalTemporaryTableInsertStrategy(
TemporaryTable.createEntityTable(
entityDescriptor,
name -> '#' + TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public TemporaryTableKind getSupportedTemporaryTableKind() {
return TemporaryTableKind.LOCAL;
}
@Override
public String getTemporaryTableCreateCommand() {
return "create table";
}
@Override
public AfterUseAction getTemporaryTableAfterUseAction() {
// sql-server, at least needed this dropped after use; strange!
return AfterUseAction.DROP;
}
@Override
public BeforeUseAction getTemporaryTableBeforeUseAction() {
return BeforeUseAction.CREATE;
}
@Override
public String getSelectGUIDString() {
return "select newid()";

View File

@ -348,6 +348,11 @@ public class CockroachDialect extends Dialect {
return NationalizationSupport.IMPLICIT;
}
@Override
public int getMaxIdentifierLength() {
return 63;
}
@Override
public void appendDateTimeLiteral(
SqlAppender appender,

View File

@ -31,7 +31,9 @@ import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.query.IntervalType;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.mutation.internal.cte.CteStrategy;
import org.hibernate.query.sqm.mutation.internal.cte.CteInsertStrategy;
import org.hibernate.query.sqm.mutation.internal.cte.CteMutationStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
@ -169,8 +171,20 @@ public class DB2Dialect extends Dialect {
CommonFunctionFactory.trim2( queryEngine );
CommonFunctionFactory.space( queryEngine );
CommonFunctionFactory.repeat( queryEngine );
CommonFunctionFactory.substr( queryEngine );
//also natively supports ANSI-style substring()
queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "substr" )
.setInvariantType(
queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.STRING )
)
.setArgumentCountBetween( 2, 4 )
.setArgumentListSignature( "(string, start[, length[, units]])" )
.register();
queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "substring" )
.setInvariantType(
queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.STRING )
)
.setArgumentCountBetween( 2, 4 )
.setArgumentListSignature( "(string{ from|,} start[{ for|,} length[, units]])" )
.register();
CommonFunctionFactory.translate( queryEngine );
CommonFunctionFactory.bitand( queryEngine );
CommonFunctionFactory.bitor( queryEngine );
@ -460,7 +474,14 @@ public class DB2Dialect extends Dialect {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new CteStrategy( rootEntityDescriptor, runtimeModelCreationContext );
return new CteMutationStrategy( rootEntityDescriptor, runtimeModelCreationContext );
}
@Override
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new CteInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext );
}
@Override
@ -576,6 +597,11 @@ public class DB2Dialect extends Dialect {
return uniqueDelegate;
}
@Override
public int getMaxIdentifierLength() {
return 128;
}
@Override
public LimitHandler getLimitHandler() {
return limitHandler;

View File

@ -8,11 +8,10 @@ package org.hibernate.dialect;
import org.hibernate.HibernateException;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.DerbyConcatFunction;
import org.hibernate.dialect.function.CastingConcatFunction;
import org.hibernate.dialect.function.DerbyLpadEmulation;
import org.hibernate.dialect.function.DerbyRpadEmulation;
import org.hibernate.dialect.function.CaseLeastGreatestEmulation;
@ -36,10 +35,13 @@ import org.hibernate.query.CastType;
import org.hibernate.query.IntervalType;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction;
import org.hibernate.query.sqm.mutation.internal.idtable.IdTable;
import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy;
import org.hibernate.query.sqm.mutation.internal.idtable.TempIdTableExporter;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.query.sqm.mutation.internal.temptable.BeforeUseAction;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableMutationStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableInsertStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableMutationStrategy;
import org.hibernate.dialect.temptable.TemporaryTableKind;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
@ -56,6 +58,7 @@ import org.hibernate.type.BasicType;
import org.hibernate.type.BasicTypeRegistry;
import org.hibernate.type.JavaObjectType;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.java.BigDecimalJavaTypeDescriptor;
import org.hibernate.type.descriptor.jdbc.DecimalJdbcType;
import org.hibernate.type.descriptor.jdbc.ObjectNullResolvingJdbcType;
import org.hibernate.type.descriptor.jdbc.SmallIntJdbcType;
@ -199,6 +202,8 @@ public class DerbyDialect extends Dialect {
"||",
getCastTypeName( stringType, null, null, null )
);
// AVG by default uses the input type, so we possibly need to cast the argument type, hence a special function
CommonFunctionFactory.avg_castingNonDoubleArguments( this, queryEngine, SqlAstNodeRenderingMode.DEFAULT );
CommonFunctionFactory.concat_pipeOperator( queryEngine );
CommonFunctionFactory.cot( queryEngine );
@ -220,13 +225,22 @@ public class DerbyDialect extends Dialect {
CommonFunctionFactory.leftRight_substrLength( queryEngine );
CommonFunctionFactory.characterLength_length( queryEngine, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
CommonFunctionFactory.power_expLn( queryEngine );
CommonFunctionFactory.bitLength_pattern( queryEngine, "length(?1)*8" );
queryEngine.getSqmFunctionRegistry().patternDescriptorBuilder( "round", "floor(?1*1e?2+0.5)/1e?2")
.setReturnTypeResolver( useArgType(1) )
.setExactArgumentCount( 2 )
.register();
queryEngine.getSqmFunctionRegistry().register( "concat", new DerbyConcatFunction( this, queryEngine.getTypeConfiguration() ) );
queryEngine.getSqmFunctionRegistry().register(
"concat",
new CastingConcatFunction(
this,
"||",
SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER,
queryEngine.getTypeConfiguration()
)
);
//no way I can see to pad with anything other than spaces
queryEngine.getSqmFunctionRegistry().register( "lpad", new DerbyLpadEmulation( queryEngine.getTypeConfiguration() ) );
@ -324,8 +338,9 @@ public class DerbyDialect extends Dialect {
case FLOAT:
case DOUBLE:
// Derby can't cast to char directly, but needs to be cast to decimal first...
return "cast(trim(cast(cast(?1 as decimal) as char(254))) as ?2)";
return "cast(trim(cast(cast(?1 as decimal(" + getDefaultDecimalPrecision() + "," + BigDecimalJavaTypeDescriptor.INSTANCE.getDefaultSqlScale( this, null ) + ")) as char(254))) as ?2)";
case INTEGER:
case LONG:
case FIXED:
return "cast(trim(cast(?1 as char(254))) as ?2)";
}
@ -773,7 +788,7 @@ public class DerbyDialect extends Dialect {
* The DECLARE GLOBAL TEMPORARY TABLE statement defines a temporary table for the current connection.
* </pre>
*
* {@link DB2Dialect} returns a {@link org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy} that
* {@link DB2Dialect} returns a {@link GlobalTemporaryTableMutationStrategy} that
* will make temporary tables created at startup and hence unavailable for subsequent connections.<br/>
* see HHH-10238.
*/
@ -781,32 +796,65 @@ public class DerbyDialect extends Dialect {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new LocalTemporaryTableStrategy(
new IdTable(
return new LocalTemporaryTableMutationStrategy(
TemporaryTable.createIdTable(
rootEntityDescriptor,
basename -> "session.HT_" + basename,
basename -> "session." + TemporaryTable.ID_TABLE_PREFIX + basename,
this,
runtimeModelCreationContext
),
() -> new TempIdTableExporter( true, this::getTypeName ) {
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
protected String getCreateCommand() {
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new LocalTemporaryTableInsertStrategy(
TemporaryTable.createEntityTable(
rootEntityDescriptor,
name -> "session." + TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public TemporaryTableKind getSupportedTemporaryTableKind() {
return TemporaryTableKind.LOCAL;
}
@Override
public String getTemporaryTableCreateOptions() {
return "not logged";
}
@Override
public boolean supportsTemporaryTablePrimaryKey() {
return false;
}
@Override
public String getTemporaryTableCreateCommand() {
return "declare global temporary table";
}
@Override
protected String getCreateOptions() {
return "not logged";
}
},
AfterUseAction.CLEAN,
TempTableDdlTransactionHandling.NONE,
runtimeModelCreationContext.getSessionFactory()
);
public BeforeUseAction getTemporaryTableBeforeUseAction() {
return BeforeUseAction.CREATE;
}
@Override
public boolean supportsPartitionBy() {
return false;
}
@Override
public boolean supportsWindowFunctions() {
// It seems at least the row_number function is supported as of 10.4
return getVersion() >= 1040;
}
}

View File

@ -10,7 +10,9 @@ import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.dialect.sequence.NoSequenceSupport;
import org.hibernate.dialect.temptable.TemporaryTableExporter;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.internal.util.MathHelper;
import org.hibernate.metamodel.mapping.JdbcMapping;
@ -60,10 +62,14 @@ import org.hibernate.query.TrimSpec;
import org.hibernate.query.hql.HqlTranslator;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction;
import org.hibernate.query.sqm.mutation.internal.idtable.IdTable;
import org.hibernate.query.sqm.mutation.internal.idtable.PersistentTableStrategy;
import org.hibernate.query.sqm.mutation.internal.idtable.PhysicalIdTableExporter;
import org.hibernate.query.sqm.mutation.internal.temptable.AfterUseAction;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.query.sqm.mutation.internal.temptable.BeforeUseAction;
import org.hibernate.query.sqm.mutation.internal.temptable.PersistentTableInsertStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.PersistentTableMutationStrategy;
import org.hibernate.dialect.temptable.TemporaryTableKind;
import org.hibernate.dialect.temptable.StandardTemporaryTableExporter;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
import org.hibernate.service.ServiceRegistry;
@ -711,6 +717,8 @@ public abstract class Dialect implements ConversionContext {
)
);
queryEngine.getSqmFunctionRegistry().registerAlternateKey( "current_instant", "instant" ); //deprecated legacy!
queryEngine.getSqmFunctionRegistry().register( "sql", new SqlFunction() );
}
/**
@ -1481,6 +1489,11 @@ public abstract class Dialect implements ConversionContext {
return true;
}
public boolean supportsTemporaryTablePrimaryKey() {
// Most databases do
return true;
}
// limit/offset support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
@ -1833,10 +1846,27 @@ public abstract class Dialect implements ConversionContext {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
EntityMappingType entityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new PersistentTableStrategy(
new IdTable( entityDescriptor, name -> name, this, runtimeModelCreationContext ),
AfterUseAction.CLEAN,
PhysicalIdTableExporter::new,
return new PersistentTableMutationStrategy(
TemporaryTable.createIdTable(
entityDescriptor,
basename -> TemporaryTable.ID_TABLE_PREFIX + basename,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType entityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new PersistentTableInsertStrategy(
TemporaryTable.createEntityTable(
entityDescriptor,
name -> TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@ -2122,6 +2152,15 @@ public abstract class Dialect implements ConversionContext {
return 10;
}
/**
* What is the maximum identifier length supported by the dialect?
*
* @return The maximum length.
*/
public int getMaxIdentifierLength() {
return Integer.MAX_VALUE;
}
/**
* The SQL literal value to which this database maps boolean values.
*
@ -2243,12 +2282,13 @@ public abstract class Dialect implements ConversionContext {
// DDL support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private StandardTableExporter tableExporter = new StandardTableExporter( this );
protected StandardSequenceExporter sequenceExporter = new StandardSequenceExporter( this );
private StandardIndexExporter indexExporter = new StandardIndexExporter( this );
private StandardForeignKeyExporter foreignKeyExporter = new StandardForeignKeyExporter( this );
private StandardUniqueKeyExporter uniqueKeyExporter = new StandardUniqueKeyExporter( this );
private StandardAuxiliaryDatabaseObjectExporter auxiliaryObjectExporter = new StandardAuxiliaryDatabaseObjectExporter( this );
private final StandardTableExporter tableExporter = new StandardTableExporter( this );
private final StandardSequenceExporter sequenceExporter = new StandardSequenceExporter( this );
private final StandardIndexExporter indexExporter = new StandardIndexExporter( this );
private final StandardForeignKeyExporter foreignKeyExporter = new StandardForeignKeyExporter( this );
private final StandardUniqueKeyExporter uniqueKeyExporter = new StandardUniqueKeyExporter( this );
private final StandardAuxiliaryDatabaseObjectExporter auxiliaryObjectExporter = new StandardAuxiliaryDatabaseObjectExporter( this );
private final StandardTemporaryTableExporter temporaryTableExporter = new StandardTemporaryTableExporter( this );
public Exporter<Table> getTableExporter() {
return tableExporter;
@ -2274,6 +2314,61 @@ public abstract class Dialect implements ConversionContext {
return auxiliaryObjectExporter;
}
public TemporaryTableExporter getTemporaryTableExporter() {
return temporaryTableExporter;
}
public TemporaryTableKind getSupportedTemporaryTableKind() {
return TemporaryTableKind.PERSISTENT;
}
public String getTemporaryTableCreateOptions() {
return null;
}
public String getTemporaryTableCreateCommand() {
final TemporaryTableKind kind = getSupportedTemporaryTableKind();
switch ( kind ) {
case PERSISTENT:
return "create table";
case LOCAL:
return "create local temporary table";
case GLOBAL:
return "create global temporary table";
}
throw new UnsupportedOperationException( "Unsupported kind: " + kind );
}
public String getTemporaryTableDropCommand() {
return "drop table";
}
public String getTemporaryTableTruncateCommand() {
return "delete from";
}
/**
* Annotation to be appended to the end of each COLUMN clause for temporary tables.
*
* @param sqlTypeCode The SQL type code
* @return The annotation to be appended (e.g. "COLLATE DATABASE_DEFAULT" in SQLServer SQL)
*/
public String getCreateTemporaryTableColumnAnnotation(int sqlTypeCode) {
return "";
}
public TempTableDdlTransactionHandling getTemporaryTableDdlTransactionHandling() {
return TempTableDdlTransactionHandling.NONE;
}
public AfterUseAction getTemporaryTableAfterUseAction() {
return AfterUseAction.CLEAN;
}
public BeforeUseAction getTemporaryTableBeforeUseAction() {
return BeforeUseAction.NONE;
}
/**
* Does this dialect support catalog creation?
*
@ -3781,14 +3876,4 @@ public abstract class Dialect implements ConversionContext {
public TimeZoneSupport getTimeZoneSupport() {
return TimeZoneSupport.NONE;
}
/**
* Annotation to be appended to the end of each COLUMN clause for temporary tables.
*
* @param sqlTypeCode The SQL type code
* @return The annotation to be appended (e.g. "COLLATE DATABASE_DEFAULT" in SQLServer SQL)
*/
public String getCreateTemporaryTableColumnAnnotation(int sqlTypeCode) {
return "";
}
}

View File

@ -15,7 +15,6 @@ import org.hibernate.query.FetchClauseType;
import org.hibernate.query.IntervalType;
import org.hibernate.query.NullOrdering;
import org.hibernate.PessimisticLockException;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.hint.IndexQueryHintHandler;
@ -39,9 +38,12 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction;
import org.hibernate.query.sqm.mutation.internal.idtable.IdTable;
import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.query.sqm.mutation.internal.temptable.BeforeUseAction;
import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableInsertStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableMutationStrategy;
import org.hibernate.dialect.temptable.TemporaryTableKind;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
@ -375,15 +377,42 @@ public class H2Dialect extends Dialect {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
EntityMappingType entityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new LocalTemporaryTableStrategy(
new IdTable( entityDescriptor, basename -> "HT_" + basename, this, runtimeModelCreationContext ),
this::getTypeName,
AfterUseAction.CLEAN,
TempTableDdlTransactionHandling.NONE,
return new LocalTemporaryTableMutationStrategy(
TemporaryTable.createIdTable(
entityDescriptor,
basename -> TemporaryTable.ID_TABLE_PREFIX + basename,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType entityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new LocalTemporaryTableInsertStrategy(
TemporaryTable.createEntityTable(
entityDescriptor,
name -> TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public TemporaryTableKind getSupportedTemporaryTableKind() {
return TemporaryTableKind.LOCAL;
}
@Override
public BeforeUseAction getTemporaryTableBeforeUseAction() {
return BeforeUseAction.CREATE;
}
@Override
public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() {
return EXTRACTOR;

View File

@ -12,10 +12,11 @@ import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction;
import org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy;
import org.hibernate.query.sqm.mutation.internal.idtable.IdTable;
import org.hibernate.query.sqm.mutation.internal.idtable.PhysicalIdTableExporter;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableInsertStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableMutationStrategy;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.dialect.temptable.TemporaryTableKind;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.type.StandardBasicTypes;
@ -127,29 +128,53 @@ public class HANAColumnStoreDialect extends AbstractHANADialect {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
EntityMappingType entityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new GlobalTemporaryTableStrategy(
new IdTable( entityDescriptor, basename -> "HT_" + basename, this, runtimeModelCreationContext ),
() -> new PhysicalIdTableExporter() {
@Override
protected String getCreateCommand() {
return "create global temporary column table";
}
@Override
protected String getTruncateIdTableCommand() {
return "truncate table";
}
@Override
public String getCreateOptions() {
return "on commit delete rows";
}
},
AfterUseAction.CLEAN,
return new GlobalTemporaryTableMutationStrategy(
TemporaryTable.createIdTable(
entityDescriptor,
basename -> TemporaryTable.ID_TABLE_PREFIX + basename,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType entityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new GlobalTemporaryTableInsertStrategy(
TemporaryTable.createEntityTable(
entityDescriptor,
name -> TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public TemporaryTableKind getSupportedTemporaryTableKind() {
return TemporaryTableKind.GLOBAL;
}
@Override
public String getTemporaryTableCreateOptions() {
return "on commit delete rows";
}
@Override
public String getTemporaryTableCreateCommand() {
// We use a row table for temporary tables here because HANA doesn't support UPDATE on temporary column tables
return "create global temporary row table";
}
@Override
public String getTemporaryTableTruncateCommand() {
return "truncate table";
}
@Override
protected boolean supportsAsciiStringTypes() {
if ( version >= 400 ) {

View File

@ -9,10 +9,11 @@ package org.hibernate.dialect;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction;
import org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy;
import org.hibernate.query.sqm.mutation.internal.idtable.IdTable;
import org.hibernate.query.sqm.mutation.internal.idtable.PhysicalIdTableExporter;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableInsertStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableMutationStrategy;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.dialect.temptable.TemporaryTableKind;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
/**
@ -49,22 +50,50 @@ public class HANARowStoreDialect extends AbstractHANADialect {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
EntityMappingType entityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new GlobalTemporaryTableStrategy(
new IdTable( entityDescriptor, basename -> "HT_" + basename, this, runtimeModelCreationContext ),
() -> new PhysicalIdTableExporter() {
return new GlobalTemporaryTableMutationStrategy(
TemporaryTable.createIdTable(
entityDescriptor,
basename -> TemporaryTable.ID_TABLE_PREFIX + basename,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
protected String getCreateCommand() {
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType entityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new GlobalTemporaryTableInsertStrategy(
TemporaryTable.createEntityTable(
entityDescriptor,
name -> TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public TemporaryTableKind getSupportedTemporaryTableKind() {
return TemporaryTableKind.GLOBAL;
}
@Override
public String getTemporaryTableCreateOptions() {
return "on commit delete rows";
}
@Override
public String getTemporaryTableCreateCommand() {
return "create global temporary row table";
}
@Override
public String getCreateOptions() {
return "on commit delete rows";
}
},
AfterUseAction.CLEAN,
runtimeModelCreationContext.getSessionFactory()
);
public String getTemporaryTableTruncateCommand() {
return "truncate table";
}
@Override

View File

@ -16,7 +16,6 @@ import org.hibernate.LockMode;
import org.hibernate.query.IntervalType;
import org.hibernate.query.NullOrdering;
import org.hibernate.StaleObjectStateException;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.identity.HSQLIdentityColumnSupport;
@ -49,11 +48,15 @@ import org.hibernate.persister.entity.Lockable;
import org.hibernate.query.CastType;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction;
import org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy;
import org.hibernate.query.sqm.mutation.internal.idtable.IdTable;
import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy;
import org.hibernate.query.sqm.mutation.internal.idtable.TempIdTableExporter;
import org.hibernate.query.sqm.mutation.internal.temptable.AfterUseAction;
import org.hibernate.query.sqm.mutation.internal.temptable.BeforeUseAction;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableInsertStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableMutationStrategy;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableInsertStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableMutationStrategy;
import org.hibernate.dialect.temptable.TemporaryTableKind;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.SqlAstTranslator;
@ -514,33 +517,93 @@ public class HSQLDialect extends Dialect {
// can happen in the middle of a transaction
if ( version < 200 ) {
return new GlobalTemporaryTableStrategy(
new IdTable( rootEntityDescriptor, name -> "HT_" + name, this, runtimeModelCreationContext ),
() -> new TempIdTableExporter( false, this::getTypeName ),
// Version 1.8 GLOBAL TEMPORARY table definitions persist beyond the end
// of the session (by default, data is cleared at commit).
AfterUseAction.CLEAN,
return new GlobalTemporaryTableMutationStrategy(
TemporaryTable.createIdTable(
rootEntityDescriptor,
basename -> TemporaryTable.ID_TABLE_PREFIX + basename,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
else {
return new LocalTemporaryTableStrategy(
return new LocalTemporaryTableMutationStrategy(
// With HSQLDB 2.0, the table name is qualified with MODULE to assist the drop
// statement (in-case there is a global name beginning with HT_)
new IdTable( rootEntityDescriptor, name -> "MODULE.HT_" + name, this, runtimeModelCreationContext ),
() -> new TempIdTableExporter( true, this::getTypeName ) {
@Override
protected String getCreateCommand() {
return "declare local temporary table";
}
},
AfterUseAction.DROP,
TempTableDdlTransactionHandling.NONE,
TemporaryTable.createIdTable(
rootEntityDescriptor,
basename -> "MODULE." + TemporaryTable.ID_TABLE_PREFIX + basename,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
}
@Override
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
// Hibernate uses this information for temporary tables that it uses for its own operations
// therefore the appropriate strategy is taken with different versions of HSQLDB
// All versions of HSQLDB support GLOBAL TEMPORARY tables where the table
// definition is shared by all users but data is private to the session
// HSQLDB 2.0 also supports session-based LOCAL TEMPORARY tables where
// the definition and data is private to the session and table declaration
// can happen in the middle of a transaction
if ( version < 200 ) {
return new GlobalTemporaryTableInsertStrategy(
TemporaryTable.createEntityTable(
rootEntityDescriptor,
name -> TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
else {
return new LocalTemporaryTableInsertStrategy(
// With HSQLDB 2.0, the table name is qualified with MODULE to assist the drop
// statement (in-case there is a global name beginning with HT_)
TemporaryTable.createEntityTable(
rootEntityDescriptor,
name -> "MODULE." + TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
}
@Override
public TemporaryTableKind getSupportedTemporaryTableKind() {
return version < 200 ? TemporaryTableKind.GLOBAL : TemporaryTableKind.LOCAL;
}
@Override
public String getTemporaryTableCreateCommand() {
return version < 200 ? super.getTemporaryTableCreateCommand() : "declare local temporary table";
}
@Override
public AfterUseAction getTemporaryTableAfterUseAction() {
// Version 1.8 GLOBAL TEMPORARY table definitions persist beyond the end
// of the session (by default, data is cleared at commit).
return version < 200 ? AfterUseAction.CLEAN : AfterUseAction.DROP;
}
@Override
public BeforeUseAction getTemporaryTableBeforeUseAction() {
return version < 200 ? BeforeUseAction.NONE : BeforeUseAction.CREATE;
}
// current timestamp support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**

View File

@ -18,7 +18,6 @@ import org.hibernate.query.CastType;
import org.hibernate.query.IntervalType;
import org.hibernate.query.NullOrdering;
import org.hibernate.PessimisticLockException;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.hint.IndexQueryHintHandler;
@ -44,10 +43,13 @@ import org.hibernate.metamodel.mapping.SqlExpressable;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction;
import org.hibernate.query.sqm.mutation.internal.idtable.IdTable;
import org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy;
import org.hibernate.query.sqm.mutation.internal.idtable.TempIdTableExporter;
import org.hibernate.query.sqm.mutation.internal.temptable.AfterUseAction;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.query.sqm.mutation.internal.temptable.BeforeUseAction;
import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableInsertStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableMutationStrategy;
import org.hibernate.dialect.temptable.TemporaryTableKind;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.sql.ast.SqlAstTranslator;
@ -696,23 +698,67 @@ public class MySQLDialect extends Dialect {
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new LocalTemporaryTableStrategy(
new IdTable( rootEntityDescriptor, basename -> "HT_" + basename, this, runtimeModelCreationContext ),
() -> new TempIdTableExporter( true, this::getTypeName ) {
return new LocalTemporaryTableMutationStrategy(
TemporaryTable.createIdTable(
rootEntityDescriptor,
basename -> TemporaryTable.ID_TABLE_PREFIX + basename,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
protected String getCreateCommand() {
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new LocalTemporaryTableInsertStrategy(
TemporaryTable.createEntityTable(
rootEntityDescriptor,
name -> TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public TemporaryTableKind getSupportedTemporaryTableKind() {
return TemporaryTableKind.LOCAL;
}
@Override
public String getTemporaryTableCreateCommand() {
return "create temporary table if not exists";
}
@Override
protected String getDropCommand() {
public String getTemporaryTableDropCommand() {
return "drop temporary table";
}
},
AfterUseAction.DROP,
TempTableDdlTransactionHandling.NONE,
runtimeModelCreationContext.getSessionFactory()
);
@Override
public AfterUseAction getTemporaryTableAfterUseAction() {
return AfterUseAction.DROP;
}
@Override
public BeforeUseAction getTemporaryTableBeforeUseAction() {
return BeforeUseAction.CREATE;
}
@Override
public int getMaxAliasLength() {
// Max alias length is 256, but Hibernate needs to add "uniqueing info" so we account for that
return 246;
}
@Override
public int getMaxIdentifierLength() {
return 64;
}
@Override

View File

@ -42,10 +42,11 @@ import org.hibernate.query.SemanticException;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction;
import org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy;
import org.hibernate.query.sqm.mutation.internal.idtable.IdTable;
import org.hibernate.query.sqm.mutation.internal.idtable.TempIdTableExporter;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableInsertStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableMutationStrategy;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.dialect.temptable.TemporaryTableKind;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
@ -902,24 +903,42 @@ public class OracleDialect extends Dialect {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new GlobalTemporaryTableStrategy(
new IdTable(
return new GlobalTemporaryTableMutationStrategy(
TemporaryTable.createIdTable(
rootEntityDescriptor,
name -> "HT_" + ( name.length() > 27 ? name.substring( 0, 27 ) : name ),
basename -> TemporaryTable.ID_TABLE_PREFIX + basename,
this,
runtimeModelCreationContext
),
() -> new TempIdTableExporter( false, this::getTypeName ) {
@Override
protected String getCreateOptions() {
return "on commit delete rows";
}
},
AfterUseAction.CLEAN,
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new GlobalTemporaryTableInsertStrategy(
TemporaryTable.createEntityTable(
rootEntityDescriptor,
name -> TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public TemporaryTableKind getSupportedTemporaryTableKind() {
return TemporaryTableKind.GLOBAL;
}
@Override
public String getTemporaryTableCreateOptions() {
return "on commit delete rows";
}
/**
* For Oracle, the FOR UPDATE clause cannot be applied when using ORDER BY, DISTINCT or views.
*
@ -969,10 +988,15 @@ public class OracleDialect extends Dialect {
@Override
public int getMaxAliasLength() {
// Oracle's max identifier length is 30, but Hibernate needs to add "uniqueing info" so we account for that,
// Max identifier length is 30, but Hibernate needs to add "uniqueing info" so we account for that
return 20;
}
@Override
public int getMaxIdentifierLength() {
return 30;
}
@Override
public CallableStatementSupport getCallableStatementSupport() {
// Oracle supports returning cursors

View File

@ -20,10 +20,13 @@ import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.FunctionExpression;
import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.expression.Over;
import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.from.UnionTableGroup;
import org.hibernate.sql.ast.tree.from.ValuesTableReference;
import org.hibernate.sql.ast.tree.insert.Values;
import org.hibernate.sql.ast.tree.select.QueryGroup;
import org.hibernate.sql.ast.tree.select.QueryPart;
@ -244,6 +247,52 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
}
}
@Override
protected void renderValuesTableReference(ValuesTableReference tableReference) {
final List<Values> valuesList = tableReference.getValuesList();
if ( valuesList.size() < 2 ) {
super.renderValuesTableReference( tableReference );
}
else {
append( '(' );
// Oracle doesn't support a multi-values insert
// So we render a select union emulation instead
final Stack<Clause> clauseStack = getClauseStack();
clauseStack.push( Clause.VALUES );
try {
// We render the first select statement with aliases
clauseStack.push( Clause.SELECT );
try {
appendSql( "select " );
renderCommaSeparatedSelectExpression(
valuesList.get( 0 ).getExpressions(),
tableReference.getColumnNames()
);
appendSql( getFromDualForSelectOnly() );
}
finally {
clauseStack.pop();
}
// The others, without the aliases
for ( int i = 1; i < valuesList.size(); i++ ) {
appendSql( " union all " );
renderExpressionsAsSubquery( valuesList.get( i ).getExpressions() );
}
}
finally {
clauseStack.pop();
}
append( ')' );
final String identificationVariable = tableReference.getIdentificationVariable();
if ( identificationVariable != null ) {
append( WHITESPACE );
append( tableReference.getIdentificationVariable() );
}
}
}
@Override
public void visitQueryGroup(QueryGroup queryGroup) {
if ( shouldEmulateFetchClause( queryGroup ) ) {
@ -287,6 +336,22 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
}
}
@Override
public void visitOver(Over over) {
final Expression expression = over.getExpression();
if ( expression instanceof FunctionExpression && "row_number".equals( ( (FunctionExpression) expression ).getFunctionName() ) ) {
if ( over.getPartitions().isEmpty() && over.getOrderList().isEmpty()
&& over.getStartKind() == Over.FrameKind.UNBOUNDED_PRECEDING
&& over.getEndKind() == Over.FrameKind.CURRENT_ROW
&& over.getExclusion() == Over.FrameExclusion.NO_OTHERS ) {
// Oracle doesn't allow an empty over clause for the row_number() function
append( "rownum" );
return;
}
}
super.visitOver( over );
}
@Override
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
renderComparisonEmulateDecode( lhs, operator, rhs );

View File

@ -57,7 +57,9 @@ import org.hibernate.query.IntervalType;
import org.hibernate.query.SemanticException;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.mutation.internal.cte.CteStrategy;
import org.hibernate.query.sqm.mutation.internal.cte.CteInsertStrategy;
import org.hibernate.query.sqm.mutation.internal.cte.CteMutationStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.sql.ast.SqlAstTranslator;
@ -617,7 +619,14 @@ public class PostgreSQLDialect extends Dialect {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new CteStrategy( rootEntityDescriptor, runtimeModelCreationContext );
return new CteMutationStrategy( rootEntityDescriptor, runtimeModelCreationContext );
}
@Override
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new CteInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext );
}
@Override
@ -746,6 +755,11 @@ public class PostgreSQLDialect extends Dialect {
return NationalizationSupport.IMPLICIT;
}
@Override
public int getMaxIdentifierLength() {
return 63;
}
@Override
public boolean supportsJdbcConnectionLobCreation(DatabaseMetaData databaseMetaData) {
return false;

View File

@ -164,6 +164,11 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
return 2_147_483_647;
}
@Override
public int getMaxIdentifierLength() {
return 128;
}
@Override
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
super.contributeTypes( typeContributions, serviceRegistry );
@ -288,7 +293,7 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
@Override
public String currentDate() {
return currentTimestamp();
return "convert(date,getdate())";
}
@Override

View File

@ -494,6 +494,11 @@ public class SybaseASEDialect extends SybaseDialect {
return 30;
}
@Override
public int getMaxIdentifierLength() {
return 255;
}
@Override
public boolean supportsValuesListForInsert() {
return false;

View File

@ -9,7 +9,6 @@ package org.hibernate.dialect;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.SybaseConcatFunction;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
@ -214,8 +213,6 @@ public class SybaseDialect extends AbstractTransactSQLDialect {
// AVG by default uses the input type, so we possibly need to cast the argument type, hence a special function
CommonFunctionFactory.avg_castingNonDoubleArguments( this, queryEngine, SqlAstNodeRenderingMode.DEFAULT );
queryEngine.getSqmFunctionRegistry().register( "concat", new SybaseConcatFunction( this, queryEngine.getTypeConfiguration() ) );
//this doesn't work 100% on earlier versions of Sybase
//which were missing the third parameter in charindex()
//TODO: we could emulate it with substring() like in Postgres
@ -242,6 +239,11 @@ public class SybaseDialect extends AbstractTransactSQLDialect {
return "select db_name()";
}
@Override
public int getMaxIdentifierLength() {
return 128;
}
@Override
public String castPattern(CastType from, CastType to) {
if ( to == CastType.STRING ) {

View File

@ -22,14 +22,22 @@ import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.spi.TypeConfiguration;
public class DerbyConcatFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
public class CastingConcatFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
private final Dialect dialect;
private final String concatOperator;
private final String concatArgumentCastType;
private final SqlAstNodeRenderingMode argumentRenderingMode;
public DerbyConcatFunction(Dialect dialect, TypeConfiguration typeConfiguration) {
public CastingConcatFunction(
Dialect dialect,
String concatOperator,
SqlAstNodeRenderingMode argumentRenderingMode,
TypeConfiguration typeConfiguration) {
super(
"concat",
StandardArgumentsValidators.min( 1 ),
@ -38,6 +46,18 @@ public class DerbyConcatFunction extends AbstractSqmSelfRenderingFunctionDescrip
)
);
this.dialect = dialect;
this.concatOperator = concatOperator;
this.argumentRenderingMode = argumentRenderingMode;
this.concatArgumentCastType = dialect.getTypeName(
SqlTypes.VARCHAR,
dialect.getSizeStrategy().resolveSize(
typeConfiguration.getJdbcTypeDescriptorRegistry().getDescriptor( SqlTypes.VARCHAR ),
typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( String.class ),
null,
null,
null
)
);
}
@Override
@ -45,7 +65,7 @@ public class DerbyConcatFunction extends AbstractSqmSelfRenderingFunctionDescrip
sqlAppender.appendSql( '(' );
renderAsString( sqlAppender, walker, (Expression) sqlAstArguments.get( 0 ) );
for ( int i = 1; i < sqlAstArguments.size(); i++ ) {
sqlAppender.appendSql( "||" );
sqlAppender.appendSql( concatOperator );
renderAsString( sqlAppender, walker, (Expression) sqlAstArguments.get( i ) );
}
sqlAppender.appendSql( ')' );
@ -55,11 +75,12 @@ public class DerbyConcatFunction extends AbstractSqmSelfRenderingFunctionDescrip
final JdbcMapping sourceMapping = expression.getExpressionType().getJdbcMappings().get( 0 );
// No need to cast if we already have a string
if ( sourceMapping.getCastType() == CastType.STRING ) {
translator.render( expression, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
translator.render( expression, argumentRenderingMode );
}
else {
final String cast = dialect.castPattern( sourceMapping.getCastType(), CastType.STRING );
new PatternRenderer( cast ).render( sqlAppender, Collections.singletonList( expression ), translator );
new PatternRenderer( cast.replace( "?2", concatArgumentCastType ), argumentRenderingMode )
.render( sqlAppender, Collections.singletonList( expression ), translator );
}
}
}

View File

@ -0,0 +1,67 @@
/*
* 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.dialect.function;
import java.util.List;
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
import org.hibernate.type.BasicType;
import org.hibernate.type.JavaObjectType;
/**
* A function to pass through a SQL fragment.
*
* @author Christian Beikov
*/
public class SqlFunction
extends AbstractSqmSelfRenderingFunctionDescriptor {
public SqlFunction() {
super(
"sql",
StandardArgumentsValidators.min( 1 ),
StandardFunctionReturnTypeResolvers.invariant( JavaObjectType.INSTANCE )
);
}
@Override
public void render(
SqlAppender sqlAppender,
List<SqlAstNode> arguments,
SqlAstTranslator<?> walker) {
final QueryLiteral<String> sqlFragmentLiteral = (QueryLiteral<String>) arguments.get( 0 );
final String sqlFragment = sqlFragmentLiteral.getLiteralValue();
if ( arguments.size() != 1 ) {
int index = 0;
for ( int i = 1; i < arguments.size(); i++ ) {
final SqlAstNode argument = arguments.get( i );
final int paramIndex = sqlFragment.indexOf( '?', index );
if ( paramIndex == -1 ) {
throw new IllegalArgumentException( "The SQL function passes an argument at index " + i + " but the fragment contains no placeholder for the argument: " + sqlFragment );
}
sqlAppender.append( sqlFragment, index, paramIndex );
argument.accept( walker );
index = paramIndex + 1;
}
}
else {
sqlAppender.appendSql( sqlFragment );
}
}
@Override
public String getArgumentListSignature() {
return "";
}
}

View File

@ -1,65 +0,0 @@
/*
* 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.dialect.function;
import java.util.Collections;
import java.util.List;
import org.hibernate.dialect.Dialect;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.query.CastType;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
import org.hibernate.query.sqm.produce.function.internal.PatternRenderer;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.spi.TypeConfiguration;
public class SybaseConcatFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
private final Dialect dialect;
public SybaseConcatFunction(Dialect dialect, TypeConfiguration typeConfiguration) {
super(
"concat",
StandardArgumentsValidators.min( 1 ),
StandardFunctionReturnTypeResolvers.invariant(
typeConfiguration.getBasicTypeRegistry().resolve( StandardBasicTypes.STRING )
)
);
this.dialect = dialect;
}
@Override
public void render(SqlAppender sqlAppender, List<SqlAstNode> sqlAstArguments, SqlAstTranslator<?> walker) {
sqlAppender.appendSql( '(' );
renderAsString( sqlAppender, walker, (Expression) sqlAstArguments.get( 0 ) );
for ( int i = 1; i < sqlAstArguments.size(); i++ ) {
sqlAppender.appendSql( '+' );
renderAsString( sqlAppender, walker, (Expression) sqlAstArguments.get( i ) );
}
sqlAppender.appendSql( ')' );
}
private void renderAsString(SqlAppender sqlAppender, SqlAstTranslator<?> translator, Expression expression) {
final JdbcMapping sourceMapping = expression.getExpressionType().getJdbcMappings().get( 0 );
// No need to cast if we already have a string
if ( sourceMapping.getCastType() == CastType.STRING ) {
translator.render( expression, SqlAstNodeRenderingMode.DEFAULT );
}
else {
final String cast = dialect.castPattern( sourceMapping.getCastType(), CastType.STRING );
new PatternRenderer( cast ).render( sqlAppender, Collections.singletonList( expression ), translator );
}
}
}

View File

@ -0,0 +1,114 @@
/*
* 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.dialect.temptable;
import java.util.function.Function;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
/**
* @author Steve Ebersole
*/
@SuppressWarnings("WeakerAccess")
public class StandardTemporaryTableExporter implements TemporaryTableExporter {
private final Dialect dialect;
public StandardTemporaryTableExporter(Dialect dialect) {
this.dialect = dialect;
}
protected String getCreateCommand() {
return dialect.getTemporaryTableCreateCommand();
}
protected String getCreateOptions() {
return dialect.getTemporaryTableCreateOptions();
}
protected String getDropCommand() {
return dialect.getTemporaryTableDropCommand();
}
protected String getTruncateTableCommand() {
return dialect.getTemporaryTableTruncateCommand();
}
@Override
public String getSqlCreateCommand(TemporaryTable temporaryTable) {
final StringBuilder buffer = new StringBuilder( getCreateCommand() ).append( ' ' );
buffer.append( temporaryTable.getQualifiedTableName() );
buffer.append( '(' );
for ( TemporaryTableColumn column : temporaryTable.getColumns() ) {
buffer.append( column.getColumnName() ).append( ' ' );
final int sqlTypeCode = column.getJdbcMapping().getJdbcTypeDescriptor().getDefaultSqlTypeCode();
final String databaseTypeName = column.getSqlTypeDefinition();
buffer.append( databaseTypeName );
final String columnAnnotation = dialect.getCreateTemporaryTableColumnAnnotation( sqlTypeCode );
if ( !columnAnnotation.isEmpty() ) {
buffer.append( ' ' ).append( columnAnnotation );
}
if ( column.isNullable() ) {
final String nullColumnString = dialect.getNullColumnString( databaseTypeName );
if ( !databaseTypeName.contains( nullColumnString ) ) {
buffer.append( nullColumnString );
}
}
else {
buffer.append( " not null" );
}
buffer.append( ", " );
}
if ( dialect.supportsTemporaryTablePrimaryKey() ) {
buffer.append( "primary key (" );
for ( TemporaryTableColumn column : temporaryTable.getColumns() ) {
if ( column.isPrimaryKey() ) {
buffer.append( column.getColumnName() );
buffer.append( ", " );
}
}
buffer.setLength( buffer.length() - 2 );
buffer.append( ')' );
}
else {
buffer.setLength( buffer.length() - 2 );
}
buffer.append( ')' );
final String createOptions = getCreateOptions();
if ( createOptions != null ) {
buffer.append( ' ' ).append( createOptions );
}
return buffer.toString();
}
@Override
public String getSqlDropCommand(TemporaryTable idTable) {
return getDropCommand() + " " + idTable.getQualifiedTableName();
}
@Override
public String getSqlTruncateCommand(
TemporaryTable idTable,
Function<SharedSessionContractImplementor, String> sessionUidAccess,
SharedSessionContractImplementor session) {
if ( idTable.getSessionUidColumn() != null ) {
assert sessionUidAccess != null;
final String uid = sessionUidAccess.apply( session );
return getTruncateTableCommand() + " " + idTable.getQualifiedTableName()
+ " where " + idTable.getSessionUidColumn().getColumnName() + " = " + uid;
}
else {
return getTruncateTableCommand() + " " + idTable.getQualifiedTableName();
}
}
}

View File

@ -0,0 +1,400 @@
/*
* 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.dialect.temptable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import java.util.function.Function;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.relational.Exportable;
import org.hibernate.dialect.Dialect;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.OptimizableGenerator;
import org.hibernate.id.PostInsertIdentifierGenerator;
import org.hibernate.id.enhanced.Optimizer;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Contributable;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Value;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.type.BasicType;
/**
* @author Steve Ebersole
* @author Christian Beikov
*/
public class TemporaryTable implements Exportable, Contributable {
public static final String ID_TABLE_PREFIX = "HT_";
public static final String ENTITY_TABLE_PREFIX = "HTE_";
public static final String DEFAULT_ALIAS = "temptable_";
public static final String ENTITY_TABLE_IDENTITY_COLUMN = "HTE_IDENTITY";
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( TemporaryTable.class );
private final EntityMappingType entityDescriptor;
private final String qualifiedTableName;
private final TemporaryTableSessionUidColumn sessionUidColumn;
private final List<TemporaryTableColumn> columns;
private final Dialect dialect;
private TemporaryTable(
EntityMappingType entityDescriptor,
Function<String, String> temporaryTableNameAdjuster,
Dialect dialect,
Function<TemporaryTable, List<TemporaryTableColumn>> columnInitializer) {
this.entityDescriptor = entityDescriptor;
// The table name might be a sub-query, which is inappropriate for a temporary table name
final String originalTableName = entityDescriptor.getEntityPersister().getSynchronizedQuerySpaces()[0];
final String name;
if ( Identifier.isQuoted( originalTableName ) ) {
name = dialect.quote( temporaryTableNameAdjuster.apply( Identifier.unQuote( originalTableName ) ) );
}
else {
name = temporaryTableNameAdjuster.apply( originalTableName );
}
if ( name.length() > dialect.getMaxIdentifierLength() ) {
this.qualifiedTableName = name.substring( 0, dialect.getMaxIdentifierLength() );
}
else {
this.qualifiedTableName = name;
}
this.dialect = dialect;
if ( dialect.getSupportedTemporaryTableKind() == TemporaryTableKind.PERSISTENT ) {
final BasicType<UUID> uuidType = entityDescriptor.getEntityPersister()
.getFactory()
.getTypeConfiguration()
.getBasicTypeForJavaType( UUID.class );
this.sessionUidColumn = new TemporaryTableSessionUidColumn(
this,
uuidType,
dialect.getTypeName(
uuidType.getJdbcTypeDescriptor(),
dialect.getSizeStrategy().resolveSize(
uuidType.getJdbcTypeDescriptor(),
uuidType.getJavaTypeDescriptor(),
null,
null,
null
)
)
);
}
else {
this.sessionUidColumn = null;
}
final List<TemporaryTableColumn> columns = columnInitializer.apply( this );
if ( sessionUidColumn != null ) {
columns.add( sessionUidColumn );
}
this.columns = columns;
}
public static TemporaryTable createIdTable(
EntityMappingType entityDescriptor,
Function<String, String> temporaryTableNameAdjuster,
Dialect dialect,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new TemporaryTable(
entityDescriptor,
temporaryTableNameAdjuster,
dialect,
temporaryTable -> {
final List<TemporaryTableColumn> columns = new ArrayList<>();
final PersistentClass entityBinding = runtimeModelCreationContext.getBootModel()
.getEntityBinding( entityDescriptor.getEntityName() );
final Iterator<Selectable> itr = entityBinding.getKey().getColumnIterator();
final Iterator<JdbcMapping> jdbcMappings = entityDescriptor.getIdentifierMapping()
.getJdbcMappings()
.iterator();
while ( itr.hasNext() ) {
final Column column = (Column) itr.next();
final JdbcMapping jdbcMapping = jdbcMappings.next();
columns.add(
new TemporaryTableColumn(
temporaryTable,
column.getText( dialect ),
jdbcMapping,
column.getSqlType( dialect, runtimeModelCreationContext.getMetadata() ),
column.isNullable(),
true
)
);
}
entityDescriptor.visitSubTypeAttributeMappings(
attribute -> {
if ( attribute instanceof PluralAttributeMapping ) {
final PluralAttributeMapping pluralAttribute = (PluralAttributeMapping) attribute;
if ( pluralAttribute.getSeparateCollectionTable() != null ) {
// Ensure that the FK target columns are available
final ModelPart fkTarget = pluralAttribute.getKeyDescriptor().getTargetPart();
if ( !( fkTarget instanceof EntityIdentifierMapping ) ) {
final Value value = entityBinding.getSubclassProperty( pluralAttribute.getAttributeName() )
.getValue();
final Iterator<Selectable> columnIterator = ( (Collection) value ).getKey()
.getColumnIterator();
fkTarget.forEachSelectable(
(columnIndex, selection) -> {
final Selectable selectable = columnIterator.next();
if ( selectable instanceof Column ) {
final Column column = (Column) selectable;
columns.add(
new TemporaryTableColumn(
temporaryTable,
selectable.getText( dialect ),
selection.getJdbcMapping(),
column.getSqlType(
dialect,
runtimeModelCreationContext.getMetadata()
),
column.isNullable()
)
);
}
}
);
}
}
}
}
);
return columns;
}
);
}
public static TemporaryTable createEntityTable(
EntityMappingType entityDescriptor,
Function<String, String> temporaryTableNameAdjuster,
Dialect dialect,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new TemporaryTable(
entityDescriptor,
temporaryTableNameAdjuster,
dialect,
temporaryTable -> {
final List<TemporaryTableColumn> columns = new ArrayList<>();
final PersistentClass entityBinding = runtimeModelCreationContext.getBootModel()
.getEntityBinding( entityDescriptor.getEntityName() );
final IdentifierGenerator identifierGenerator = entityDescriptor.getEntityPersister()
.getIdentifierGenerator();
final boolean identityColumn = identifierGenerator instanceof PostInsertIdentifierGenerator;
final boolean hasOptimizer;
if ( identityColumn ) {
hasOptimizer = false;
final Iterator<Selectable> itr = entityBinding.getKey().getColumnIterator();
final Iterator<JdbcMapping> jdbcMappings = entityDescriptor.getIdentifierMapping()
.getJdbcMappings()
.iterator();
while ( itr.hasNext() ) {
final Column column = (Column) itr.next();
final JdbcMapping jdbcMapping = jdbcMappings.next();
columns.add(
new TemporaryTableColumn(
temporaryTable,
ENTITY_TABLE_IDENTITY_COLUMN,
jdbcMapping,
column.getSqlType( dialect, runtimeModelCreationContext.getMetadata() ) + " " +
dialect.getIdentityColumnSupport().getIdentityColumnString( column.getSqlTypeCode( runtimeModelCreationContext.getMetadata() ) ),
// Always report as nullable as the identity column string usually includes the not null constraint
true, //column.isNullable()
true
)
);
}
}
else {
if ( identifierGenerator instanceof OptimizableGenerator ) {
final Optimizer optimizer = ( (OptimizableGenerator) identifierGenerator ).getOptimizer();
hasOptimizer = optimizer != null && optimizer.getIncrementSize() > 1;
}
else {
hasOptimizer = false;
}
}
final Iterator<Selectable> itr = entityBinding.getKey().getColumnIterator();
final Iterator<JdbcMapping> jdbcMappings = entityDescriptor.getIdentifierMapping()
.getJdbcMappings()
.iterator();
while ( itr.hasNext() ) {
final Column column = (Column) itr.next();
final JdbcMapping jdbcMapping = jdbcMappings.next();
columns.add(
new TemporaryTableColumn(
temporaryTable,
column.getText( dialect ),
jdbcMapping,
column.getSqlType( dialect, runtimeModelCreationContext.getMetadata() ),
// We have to set the identity column after the root table insert
column.isNullable() || identityColumn || hasOptimizer,
!identityColumn && !hasOptimizer
)
);
}
final EntityDiscriminatorMapping discriminatorMapping = entityDescriptor.getDiscriminatorMapping();
if ( entityBinding.getDiscriminator() != null && !discriminatorMapping.isFormula() ) {
final Column discriminator = (Column) entityBinding.getDiscriminator().getColumnIterator().next();
columns.add(
new TemporaryTableColumn(
temporaryTable,
discriminator.getText( dialect ),
discriminatorMapping.getJdbcMapping(),
discriminator.getSqlType( dialect, runtimeModelCreationContext.getMetadata() ),
// We have to set the identity column after the root table insert
discriminator.isNullable()
)
);
}
// Collect all columns for all entity subtype attributes
entityDescriptor.visitSubTypeAttributeMappings(
attribute -> {
if ( !( attribute instanceof PluralAttributeMapping ) ) {
final SimpleValue value = (SimpleValue) entityBinding.getSubclassProperty( attribute.getAttributeName() )
.getValue();
final Iterator<Selectable> columnIterator = value.getConstraintColumnIterator();
attribute.forEachSelectable(
(columnIndex, selection) -> {
final Selectable selectable = columnIterator.next();
if ( selectable instanceof Column ) {
final Column column = (Column) selectable;
columns.add(
new TemporaryTableColumn(
temporaryTable,
selectable.getText( dialect ),
selection.getJdbcMapping(),
column.getSqlType(
dialect,
runtimeModelCreationContext.getMetadata()
),
// Treat regular temporary table columns as nullable for simplicity
true
)
);
}
}
);
}
}
);
if ( hasOptimizer ) {
// We add a special row number column that we can use to identify and join rows
final BasicType<Integer> integerBasicType = entityDescriptor.getEntityPersister()
.getFactory()
.getTypeConfiguration()
.getBasicTypeForJavaType( Integer.class );
final String rowNumberType;
if ( dialect.supportsWindowFunctions() ) {
rowNumberType = dialect.getTypeName(
integerBasicType.getJdbcTypeDescriptor().getJdbcTypeCode(),
dialect.getSizeStrategy().resolveSize(
integerBasicType.getJdbcTypeDescriptor(),
integerBasicType.getJavaTypeDescriptor(),
null,
null,
null
)
);
}
else if ( dialect.getIdentityColumnSupport().supportsIdentityColumns() ) {
rowNumberType = dialect.getTypeName(
integerBasicType.getJdbcTypeDescriptor().getJdbcTypeCode(),
dialect.getSizeStrategy().resolveSize(
integerBasicType.getJdbcTypeDescriptor(),
integerBasicType.getJavaTypeDescriptor(),
null,
null,
null
)
) + " " +
dialect.getIdentityColumnSupport().getIdentityColumnString( integerBasicType.getJdbcTypeDescriptor().getJdbcTypeCode() );
}
else {
LOG.multiTableInsertNotAvailable( entityBinding.getEntityName() );
rowNumberType = dialect.getTypeName(
integerBasicType.getJdbcTypeDescriptor().getJdbcTypeCode(),
dialect.getSizeStrategy().resolveSize(
integerBasicType.getJdbcTypeDescriptor(),
integerBasicType.getJavaTypeDescriptor(),
null,
null,
null
)
);
}
columns.add(
new TemporaryTableColumn(
temporaryTable,
"rn_",
integerBasicType,
rowNumberType,
false,
true
)
);
}
return columns;
}
);
}
public EntityMappingType getEntityDescriptor() {
return entityDescriptor;
}
public String getQualifiedTableName() {
return qualifiedTableName;
}
public List<TemporaryTableColumn> getColumns() {
return columns;
}
public TemporaryTableSessionUidColumn getSessionUidColumn() {
return sessionUidColumn;
}
public String getTableExpression() {
return qualifiedTableName;
}
@Override
public String getContributor() {
return entityDescriptor.getContributor();
}
@Override
public String getExportIdentifier() {
return getQualifiedTableName();
}
public Dialect getDialect() {
return this.dialect;
}
}

View File

@ -4,7 +4,7 @@
* 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.query.sqm.mutation.internal.idtable;
package org.hibernate.dialect.temptable;
import org.hibernate.metamodel.mapping.JdbcMapping;
@ -14,24 +14,39 @@ import org.hibernate.metamodel.mapping.JdbcMapping;
*
* @author Steve Ebersole
*/
public class IdTableColumn {
private final IdTable containingTable;
public class TemporaryTableColumn {
private final TemporaryTable containingTable;
private final String columnName;
private final JdbcMapping jdbcMapping;
private final String sqlTypeName;
private final boolean nullable;
private final boolean primaryKey;
public IdTableColumn(
IdTable containingTable,
public TemporaryTableColumn(
TemporaryTable containingTable,
String columnName,
JdbcMapping jdbcMapping,
String sqlTypeName) {
String sqlTypeName,
boolean nullable) {
this( containingTable, columnName, jdbcMapping, sqlTypeName, nullable, false );
}
public TemporaryTableColumn(
TemporaryTable containingTable,
String columnName,
JdbcMapping jdbcMapping,
String sqlTypeName,
boolean nullable,
boolean primaryKey) {
this.containingTable = containingTable;
this.columnName = columnName;
this.jdbcMapping = jdbcMapping;
this.sqlTypeName = sqlTypeName;
this.nullable = nullable;
this.primaryKey = primaryKey;
}
public IdTable getContainingTable() {
public TemporaryTable getContainingTable() {
return containingTable;
}
@ -50,4 +65,12 @@ public class IdTableColumn {
public String getSqlTypeDefinition() {
return sqlTypeName;
}
public boolean isNullable() {
return nullable;
}
public boolean isPrimaryKey() {
return primaryKey;
}
}

View File

@ -4,7 +4,7 @@
* 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.query.sqm.mutation.internal.idtable;
package org.hibernate.dialect.temptable;
import java.util.function.Function;
@ -13,13 +13,13 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
/**
* @author Steve Ebersole
*/
public interface IdTableExporter {
String getSqlCreateCommand(IdTable idTable);
public interface TemporaryTableExporter {
String getSqlCreateCommand(TemporaryTable idTable);
String getSqlDropCommand(IdTable idTable);
String getSqlDropCommand(TemporaryTable idTable);
String getSqlTruncateCommand(
IdTable idTable,
TemporaryTable idTable,
Function<SharedSessionContractImplementor, String> sessionUidAccess,
SharedSessionContractImplementor session);
}

View File

@ -4,7 +4,7 @@
* 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.query.sqm.mutation.internal.idtable;
package org.hibernate.dialect.temptable;
import java.sql.Connection;
import java.sql.PreparedStatement;
@ -27,24 +27,34 @@ import org.hibernate.jdbc.AbstractWork;
* @author Steve Ebersole
*/
@SuppressWarnings("WeakerAccess")
public class IdTableHelper {
private final static CoreMessageLogger log = CoreLogging.messageLogger( IdTableHelper.class );
public class TemporaryTableHelper {
private final static CoreMessageLogger log = CoreLogging.messageLogger( TemporaryTableHelper.class );
public static final String SESSION_ID_COLUMN_NAME = "hib_sess_id";
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Creation
public static class IdTableCreationWork extends AbstractWork {
private final IdTable idTable;
private final IdTableExporter exporter;
public static class TemporaryTableCreationWork extends AbstractWork {
private final TemporaryTable temporaryTable;
private final TemporaryTableExporter exporter;
private final SessionFactoryImplementor sessionFactory;
public IdTableCreationWork(
IdTable idTable,
IdTableExporter exporter,
public TemporaryTableCreationWork(
TemporaryTable temporaryTable,
SessionFactoryImplementor sessionFactory) {
this.idTable = idTable;
this(
temporaryTable,
sessionFactory.getJdbcServices().getDialect().getTemporaryTableExporter(),
sessionFactory
);
}
public TemporaryTableCreationWork(
TemporaryTable temporaryTable,
TemporaryTableExporter exporter,
SessionFactoryImplementor sessionFactory) {
this.temporaryTable = temporaryTable;
this.exporter = exporter;
this.sessionFactory = sessionFactory;
}
@ -54,7 +64,7 @@ public class IdTableHelper {
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
try {
final String creationCommand = exporter.getSqlCreateCommand( idTable );
final String creationCommand = exporter.getSqlCreateCommand( temporaryTable );
logStatement( creationCommand, jdbcServices );
try (Statement statement = connection.createStatement()) {
@ -63,15 +73,15 @@ public class IdTableHelper {
}
catch (SQLException e) {
log.debugf(
"unable to create id table [%s]; `%s` failed : %s",
idTable.getQualifiedTableName(),
"unable to create temporary table [%s]; `%s` failed : %s",
temporaryTable.getQualifiedTableName(),
creationCommand,
e.getMessage()
);
}
}
catch( Exception e ) {
log.debugf( "Error creating id table(s) : %s", e.getMessage() );
log.debugf( "Error creating temporary table(s) : %s", e.getMessage() );
}
}
}
@ -80,16 +90,26 @@ public class IdTableHelper {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Drop
public static class IdTableDropWork extends AbstractWork {
private final IdTable idTable;
private final IdTableExporter exporter;
public static class TemporaryTableDropWork extends AbstractWork {
private final TemporaryTable temporaryTable;
private final TemporaryTableExporter exporter;
private final SessionFactoryImplementor sessionFactory;
IdTableDropWork(
IdTable idTable,
IdTableExporter exporter,
public TemporaryTableDropWork(
TemporaryTable temporaryTable,
SessionFactoryImplementor sessionFactory) {
this.idTable = idTable;
this(
temporaryTable,
sessionFactory.getJdbcServices().getDialect().getTemporaryTableExporter(),
sessionFactory
);
}
public TemporaryTableDropWork(
TemporaryTable temporaryTable,
TemporaryTableExporter exporter,
SessionFactoryImplementor sessionFactory) {
this.temporaryTable = temporaryTable;
this.exporter = exporter;
this.sessionFactory = sessionFactory;
}
@ -99,7 +119,7 @@ public class IdTableHelper {
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
try {
final String dropCommand = exporter.getSqlDropCommand( idTable );
final String dropCommand = exporter.getSqlDropCommand( temporaryTable );
logStatement( dropCommand, jdbcServices );
try (Statement statement = connection.createStatement()) {
@ -108,15 +128,15 @@ public class IdTableHelper {
}
catch (SQLException e) {
log.debugf(
"unable to drop id table [%s]; `%s` failed : %s",
idTable.getQualifiedTableName(),
"unable to drop temporary table [%s]; `%s` failed : %s",
temporaryTable.getQualifiedTableName(),
dropCommand,
e.getMessage()
);
}
}
catch( Exception e ) {
log.debugf( "Error dropping id table(s) : %s", e.getMessage() );
log.debugf( "Error dropping temporary table(s) : %s", e.getMessage() );
}
}
}
@ -125,18 +145,18 @@ public class IdTableHelper {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Clean
public static void cleanIdTableRows(
IdTable idTable,
IdTableExporter exporter,
public static void cleanTemporaryTableRows(
TemporaryTable temporaryTable,
TemporaryTableExporter exporter,
Function<SharedSessionContractImplementor,String> sessionUidAccess,
SharedSessionContractImplementor session) {
PreparedStatement ps = null;
try {
final String sql = exporter.getSqlTruncateCommand( idTable, sessionUidAccess, session );
final String sql = exporter.getSqlTruncateCommand( temporaryTable, sessionUidAccess, session );
ps = session.getJdbcCoordinator().getStatementPreparer().prepareStatement( sql, false );
if ( idTable.getSessionUidColumn() != null ) {
if ( temporaryTable.getSessionUidColumn() != null ) {
final String sessionUid = sessionUidAccess.apply( session );
ps.setString( 1, sessionUid );
}

View File

@ -0,0 +1,16 @@
/*
* 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.dialect.temptable;
/**
* @author Christian Beikov
*/
public enum TemporaryTableKind {
PERSISTENT,
LOCAL,
GLOBAL;
}

View File

@ -4,23 +4,25 @@
* 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.query.sqm.mutation.internal.idtable;
package org.hibernate.dialect.temptable;
import org.hibernate.metamodel.mapping.JdbcMapping;
/**
* @author Steve Ebersole
*/
public class IdTableSessionUidColumn extends IdTableColumn {
public IdTableSessionUidColumn(
IdTable containingTable,
public class TemporaryTableSessionUidColumn extends TemporaryTableColumn {
public TemporaryTableSessionUidColumn(
TemporaryTable containingTable,
JdbcMapping jdbcMapping,
String sqlTypeName) {
super(
containingTable,
IdTableHelper.SESSION_ID_COLUMN_NAME,
TemporaryTableHelper.SESSION_ID_COLUMN_NAME,
jdbcMapping,
sqlTypeName
sqlTypeName,
false,
true
);
}
}

View File

@ -73,6 +73,11 @@ public class IdentityGenerator extends AbstractPostInsertGenerator {
return insert;
}
@Override
public String prepareIdentifierGeneratingInsert(String insertSQL) {
return dialect.getIdentityColumnSupport().appendIdentitySelectToInsert( insertSQL );
}
@Override
protected PreparedStatement prepare(String insertSQL, SharedSessionContractImplementor session) throws SQLException {
return session

View File

@ -31,6 +31,7 @@ import org.hibernate.engine.spi.SessionEventListenerManager;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.enhanced.AccessCallback;
import org.hibernate.id.enhanced.LegacyHiLoAlgorithmOptimizer;
import org.hibernate.id.enhanced.Optimizer;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.log.DeprecationLogger;
@ -112,6 +113,12 @@ public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenera
private Class returnClass;
private int keySize;
@Override
public Optimizer getOptimizer() {
return hiloOptimizer;
}
@Override
public synchronized Object generate(final SharedSessionContractImplementor session, Object obj) {
DeprecationLogger.DEPRECATION_LOGGER.deprecatedTableGenerator( getClass().getName() );

View File

@ -46,4 +46,6 @@ public interface OptimizableGenerator extends IdentifierGenerator, ExportablePro
* NOTE : has precedence over {@link #INCREMENT_PARAM}
*/
String OPT_PARAM = "optimizer";
Optimizer getOptimizer();
}

View File

@ -21,6 +21,7 @@ import org.hibernate.boot.model.relational.Sequence;
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.enhanced.Optimizer;
import org.hibernate.internal.log.DeprecationLogger;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor;
@ -87,7 +88,11 @@ public class SequenceGenerator
}
@Override
@SuppressWarnings("StatementWithEmptyBody")
public Optimizer getOptimizer() {
return null;
}
@Override
public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {
DeprecationLogger.DEPRECATION_LOGGER.deprecatedSequenceGenerator( getClass().getName() );

View File

@ -12,6 +12,7 @@ import org.hibernate.MappingException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.enhanced.AccessCallback;
import org.hibernate.id.enhanced.LegacyHiLoAlgorithmOptimizer;
import org.hibernate.id.enhanced.Optimizer;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.Type;
@ -37,6 +38,11 @@ public class SequenceHiLoGenerator extends SequenceGenerator {
private LegacyHiLoAlgorithmOptimizer hiloOptimizer;
@Override
public Optimizer getOptimizer() {
return hiloOptimizer;
}
@Override
public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {
super.configure( type, params, serviceRegistry );

View File

@ -165,6 +165,7 @@ public class SequenceStyleGenerator
*
* @return Value for property 'optimizer'.
*/
@Override
public Optimizer getOptimizer() {
return optimizer;
}
@ -545,11 +546,8 @@ public class SequenceStyleGenerator
@Override
public boolean supportsBulkInsertionIdentifierGeneration() {
// it does, as long as
// 1) there is no (non-noop) optimizer in use
// 2) the underlying structure is a sequence
return NoopOptimizer.class.isInstance( getOptimizer() )
&& getDatabaseStructure().isPhysicalSequence();
// it does, as long as the underlying structure is a sequence
return getDatabaseStructure().isPhysicalSequence();
}
@Override

View File

@ -310,6 +310,7 @@ public class TableGenerator implements PersistentIdentifierGenerator {
*
* @return Out optimizer.
*/
@Override
public final Optimizer getOptimizer() {
return optimizer;
}

View File

@ -29,6 +29,16 @@ public interface InsertGeneratedIdentifierDelegate {
*/
IdentifierGeneratingInsert prepareIdentifierGeneratingInsert(SqlStringGenerationContext context);
/**
* Append SQL specific to the delegate's mode
* of handling generated key values.
*
* @return The insert SQL.
*/
default String prepareIdentifierGeneratingInsert(String insertSQL) {
return insertSQL;
}
/**
* Perform the indicated insert SQL statement and determine the identifier value
* generated.

View File

@ -1825,4 +1825,8 @@ public interface CoreMessageLogger extends BasicLogger {
@Message(value = "The Javassist based BytecodeProvider has been removed: remove the `hibernate.bytecode.provider` configuration property to switch to the default provider", id = 508)
HibernateException usingRemovedJavassistBytecodeProvider();
@LogMessage(level = WARN)
@Message(value = "Multi-table insert is not available due to missing identity and window function support for: %s", id = 509)
void multiTableInsertNotAvailable(String entityName);
}

View File

@ -8,6 +8,7 @@ package org.hibernate.internal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
@ -208,11 +209,28 @@ public class FilterHelper {
if ( ! (parameterType instanceof JdbcMapping ) ) {
throw new MappingException( String.format( "parameter [%s] for filter [%s] is not of JdbcMapping type", parameterName, filterName ) );
}
final JdbcMapping jdbcMapping = (JdbcMapping) parameterType;
final Object parameterValue = enabledFilter.getParameter( parameterName );
if ( parameterValue == null ) {
throw new MappingException( String.format( "unknown parameter [%s] for filter [%s]", parameterName, filterName ) );
}
parameters.add( new FilterJdbcParameter( (JdbcMapping) parameterType, parameterValue ) );
if ( parameterValue instanceof Iterable && !jdbcMapping.getJavaTypeDescriptor().isInstance( parameterValue ) ) {
final Iterator<?> iterator = ( (Iterable<?>) parameterValue ).iterator();
if ( iterator.hasNext() ) {
parameters.add( new FilterJdbcParameter( jdbcMapping, iterator.next() ) );
while ( iterator.hasNext() ) {
sb.append( ",?" );
parameters.add( new FilterJdbcParameter( jdbcMapping, iterator.next() ) );
}
}
else {
// We need a dummy value if the list is empty
parameters.add( new FilterJdbcParameter( jdbcMapping, null ) );
}
}
else {
parameters.add( new FilterJdbcParameter( jdbcMapping, parameterValue ) );
}
}
sb.append( filterFragment, pos, filterFragment.length() );
return new FilterPredicate( sb.toString(), parameters );

View File

@ -857,6 +857,12 @@ public class SessionFactoryImpl implements SessionFactoryImplementor {
jdbcConnectionAccess
);
}
if ( entityPersister.getSqmMultiTableInsertStrategy() != null ) {
entityPersister.getSqmMultiTableInsertStrategy().release(
this,
jdbcConnectionAccess
);
}
}
);
( (MappingMetamodelImpl) runtimeMetamodels.getMappingMetamodel() ).close();

View File

@ -109,6 +109,12 @@ public class OneToOne extends ToOne {
}
return list;
}
@Override
public Iterator<Selectable> getConstraintColumnIterator() {
return identifier.getColumnIterator();
}
/**
* Returns the constrained.
* @return boolean

View File

@ -217,6 +217,10 @@ public abstract class SimpleValue implements KeyValue {
return columns;
}
public Iterator<Selectable> getConstraintColumnIterator() {
return getColumnIterator();
}
public String getTypeName() {
return typeName;
}

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.metamodel.mapping;
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer;
import org.hibernate.sql.results.graph.Fetchable;
import org.hibernate.sql.results.graph.FetchableContainer;
@ -19,7 +20,7 @@ import org.hibernate.sql.results.graph.FetchableContainer;
*
* @author Steve Ebersole
*/
public interface DiscriminatedAssociationModelPart extends Fetchable, FetchableContainer {
public interface DiscriminatedAssociationModelPart extends Fetchable, FetchableContainer, TableGroupJoinProducer {
BasicValuedModelPart getDiscriminatorPart();
BasicValuedModelPart getKeyPart();

View File

@ -40,6 +40,8 @@ public interface EntityDiscriminatorMapping extends VirtualModelPart, BasicValue
String getConcreteEntityNameForDiscriminatorValue(Object value);
boolean isPhysical();
/**
* Create the appropriate SQL expression for this discriminator
*

View File

@ -25,6 +25,7 @@ import org.hibernate.metamodel.UnsupportedMappingException;
import org.hibernate.metamodel.spi.EntityRepresentationStrategy;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.spi.FromClauseAccess;
@ -232,6 +233,10 @@ public interface EntityMappingType extends ManagedMappingType, EntityValuedModel
return getEntityPersister().getSqmMultiTableMutationStrategy();
}
default SqmMultiTableInsertStrategy getSqmMultiTableInsertStrategy() {
return getEntityPersister().getSqmMultiTableInsertStrategy();
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Special model parts - identifier, discriminator, etc

View File

@ -24,7 +24,7 @@ import org.hibernate.sql.results.graph.DomainResultCreationState;
/**
* Descriptor for foreign-keys
*/
public interface ForeignKeyDescriptor extends VirtualModelPart {
public interface ForeignKeyDescriptor extends VirtualModelPart, ValueMapping {
enum Nature {
KEY,

View File

@ -78,6 +78,11 @@ public class CaseStatementDiscriminatorMappingImpl extends AbstractDiscriminator
}
}
@Override
public boolean isPhysical() {
return false;
}
@Override
public BasicFetch generateFetch(
FetchParent fetchParent,

View File

@ -61,7 +61,7 @@ import org.hibernate.type.descriptor.java.MutabilityPlan;
*/
public class DiscriminatedAssociationAttributeMapping
extends AbstractSingularAttributeMapping
implements DiscriminatedAssociationModelPart, TableGroupJoinProducer {
implements DiscriminatedAssociationModelPart {
private final NavigableRole navigableRole;
private final DiscriminatedAssociationMapping discriminatorMapping;
private final SessionFactoryImplementor sessionFactory;

View File

@ -22,7 +22,15 @@ import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.spi.FromClauseAccess;
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.from.StandardVirtualTableGroup;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetch;
@ -190,4 +198,62 @@ public class DiscriminatedCollectionPart implements DiscriminatedAssociationMode
int span = getDiscriminatorPart().forEachJdbcType( offset, action );
return span + getKeyPart().forEachJdbcType( offset + span, action );
}
@Override
public TableGroupJoin createTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAstJoinType sqlAstJoinType,
boolean fetched,
boolean addsPredicate,
SqlAliasBaseGenerator aliasBaseGenerator,
SqlExpressionResolver sqlExpressionResolver,
FromClauseAccess fromClauseAccess,
SqlAstCreationContext creationContext) {
final TableGroup tableGroup = createRootTableGroupJoin(
navigablePath,
lhs,
explicitSourceAlias,
sqlAstJoinType,
fetched,
null,
aliasBaseGenerator,
sqlExpressionResolver,
fromClauseAccess,
creationContext
);
return new TableGroupJoin( navigablePath, sqlAstJoinType, tableGroup );
}
@Override
public TableGroup createRootTableGroupJoin(
NavigablePath navigablePath,
TableGroup lhs,
String explicitSourceAlias,
SqlAstJoinType sqlAstJoinType,
boolean fetched,
Consumer<Predicate> predicateConsumer,
SqlAliasBaseGenerator aliasBaseGenerator,
SqlExpressionResolver sqlExpressionResolver,
FromClauseAccess fromClauseAccess,
SqlAstCreationContext creationContext) {
return new StandardVirtualTableGroup(
navigablePath,
this,
lhs,
fetched
);
}
@Override
public SqlAstJoinType getDefaultSqlAstJoinType(TableGroup parentTableGroup) {
return SqlAstJoinType.LEFT;
}
@Override
public String getSqlAliasStem() {
return collectionDescriptor.getAttributeMapping().getSqlAliasStem();
}
}

View File

@ -444,6 +444,11 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
return associationKey;
}
@Override
public MappingType getMappedType() {
return getPartMappingType();
}
@Override
public MappingType getPartMappingType() {
return targetSide.getModelPart().getPartMappingType();

View File

@ -27,6 +27,7 @@ public class ExplicitColumnDiscriminatorMappingImpl extends AbstractDiscriminato
private final String tableExpression;
private final String columnName;
private final String columnFormula;
private final boolean isPhysical;
public ExplicitColumnDiscriminatorMappingImpl(
EntityPersister entityDescriptor,
@ -34,9 +35,11 @@ public class ExplicitColumnDiscriminatorMappingImpl extends AbstractDiscriminato
String tableExpression,
String columnExpression,
boolean isFormula,
boolean isPhysical,
MappingModelCreationProcess creationProcess) {
super( discriminatorType.getJdbcMapping(), entityDescriptor, discriminatorType, creationProcess );
this.tableExpression = tableExpression;
this.isPhysical = isPhysical;
if ( isFormula ) {
columnName = null;
columnFormula = columnExpression;
@ -95,4 +98,9 @@ public class ExplicitColumnDiscriminatorMappingImpl extends AbstractDiscriminato
public boolean isFormula() {
return columnFormula != null;
}
@Override
public boolean isPhysical() {
return isPhysical;
}
}

View File

@ -64,6 +64,7 @@ import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.from.CorrelatedTableGroup;
import org.hibernate.sql.ast.tree.from.LazyTableGroup;
import org.hibernate.sql.ast.tree.from.MappedByTableGroup;
import org.hibernate.sql.ast.tree.from.PluralTableGroup;
@ -1349,8 +1350,6 @@ public class ToOneAttributeMapping
)
);
initializeIfNeeded( lhs, sqlAstJoinType, lazyTableGroup );
return join;
}
@ -1368,6 +1367,21 @@ public class ToOneAttributeMapping
SqlAstCreationContext creationContext) {
final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( sqlAliasStem );
final boolean canUseInnerJoin = sqlAstJoinType == SqlAstJoinType.INNER || lhs.canUseInnerJoins() && !isNullable;
TableGroup realParentTableGroup = lhs;
while ( realParentTableGroup.getModelPart() instanceof EmbeddableValuedModelPart ) {
realParentTableGroup = fromClauseAccess.findTableGroup( realParentTableGroup.getNavigablePath().getParent() );
}
final TableGroupProducer tableGroupProducer;
if ( realParentTableGroup instanceof CorrelatedTableGroup ) {
// If the parent is a correlated table group, we can't refer to columns of the table in the outer query,
// because the context in which a column is used could be an aggregate function.
// Using a parent column in such a case would lead to an error if the parent query lacks a proper group by
tableGroupProducer = entityMappingType;
}
else {
tableGroupProducer = this;
}
final LazyTableGroup lazyTableGroup = new LazyTableGroup(
canUseInnerJoin,
navigablePath,
@ -1402,7 +1416,7 @@ public class ToOneAttributeMapping
&& targetKeyPropertyNames.contains( sb.toString() )
&& identifyingColumnsTableExpression.equals( tableExpression );
},
this,
tableGroupProducer,
explicitSourceAlias,
sqlAliasBase,
creationContext.getSessionFactory(),
@ -1426,7 +1440,13 @@ public class ToOneAttributeMapping
)
)
);
}
if ( realParentTableGroup instanceof CorrelatedTableGroup ) {
// Force initialization of the underlying table group join to retain cardinality
lazyTableGroup.getPrimaryTableReference();
}
else {
initializeIfNeeded( lhs, sqlAstJoinType, lazyTableGroup );
}

View File

@ -55,10 +55,7 @@ public class VirtualIdRepresentationStrategy implements EmbeddableRepresentation
@Override
public PropertyAccess resolvePropertyAccess(Property bootAttributeDescriptor) {
return PropertyAccessStrategyMixedImpl.INSTANCE.buildPropertyAccess(
entityMappingType.getMappedJavaTypeDescriptor().getJavaTypeClass(),
bootAttributeDescriptor.getName()
);
return entityMappingType.getRepresentationStrategy().resolvePropertyAccess( bootAttributeDescriptor );
}
private static class InstantiatorAdapter implements StandardEmbeddableInstantiator {

View File

@ -6,15 +6,11 @@
*/
package org.hibernate.metamodel.mapping.ordering.ast;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.NullPrecedence;
import org.hibernate.query.SortOrder;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression;
import org.hibernate.sql.ast.tree.expression.SelfRenderingSqlFragmentExpression;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SortSpecification;
@ -27,28 +23,10 @@ import org.hibernate.sql.ast.tree.select.SortSpecification;
*
* @author Christian Beikov
*/
public class SelfRenderingOrderingExpression implements OrderingExpression, SelfRenderingExpression {
private final String expression;
public class SelfRenderingOrderingExpression extends SelfRenderingSqlFragmentExpression implements OrderingExpression {
public SelfRenderingOrderingExpression(String expression) {
this.expression = expression;
}
public String getExpression() {
return expression;
}
@Override
public JdbcMappingContainer getExpressionType() {
return null;
}
@Override
public void renderToSql(
SqlAppender sqlAppender,
SqlAstTranslator<?> walker,
SessionFactoryImplementor sessionFactory) {
sqlAppender.append( expression );
super( expression );
}
@Override

View File

@ -99,9 +99,12 @@ import org.hibernate.engine.spi.ValueInclusion;
import org.hibernate.event.spi.EventSource;
import org.hibernate.event.spi.LoadEvent;
import org.hibernate.id.Assigned;
import org.hibernate.id.BulkInsertionCapableIdentifierGenerator;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.OptimizableGenerator;
import org.hibernate.id.PostInsertIdentifierGenerator;
import org.hibernate.id.PostInsertIdentityPersister;
import org.hibernate.id.enhanced.Optimizer;
import org.hibernate.id.insert.Binder;
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
import org.hibernate.internal.CoreLogging;
@ -201,6 +204,7 @@ import org.hibernate.query.named.NamedQueryMemento;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.sql.internal.SQLQueryParser;
import org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.sql.Alias;
import org.hibernate.sql.Delete;
@ -285,6 +289,7 @@ public abstract class AbstractEntityPersister
private MultiNaturalIdLoader<?> multiNaturalIdLoader;
private SqmMultiTableMutationStrategy sqmMultiTableMutationStrategy;
private SqmMultiTableInsertStrategy sqmMultiTableInsertStrategy;
private final NavigableRole navigableRole;
@ -5561,7 +5566,8 @@ public abstract class AbstractEntityPersister
}
);
if ( isMultiTable() ) {
boolean needsMultiTableInsert;
if ( needsMultiTableInsert = isMultiTable() ) {
creationProcess.registerInitializationCallback(
"Entity(" + getEntityName() + ") `sqmMultiTableMutationStrategy` interpretation",
() -> {
@ -5591,6 +5597,44 @@ public abstract class AbstractEntityPersister
else {
sqmMultiTableMutationStrategy = null;
}
if ( !needsMultiTableInsert && getIdentifierGenerator() instanceof BulkInsertionCapableIdentifierGenerator ) {
if ( getIdentifierGenerator() instanceof OptimizableGenerator ) {
final Optimizer optimizer = ( (OptimizableGenerator) getIdentifierGenerator() ).getOptimizer();
needsMultiTableInsert = optimizer != null && optimizer.getIncrementSize() > 1;
}
}
if ( needsMultiTableInsert ) {
creationProcess.registerInitializationCallback(
"Entity(" + getEntityName() + ") `sqmMultiTableInsertStrategy` interpretation",
() -> {
try {
sqmMultiTableInsertStrategy = interpretSqmMultiTableInsertStrategy(
this,
creationProcess
);
}
catch (Exception ex) {
return false;
}
if ( sqmMultiTableInsertStrategy == null ) {
return false;
}
sqmMultiTableInsertStrategy.prepare(
creationProcess,
creationContext.getSessionFactory()
.getJdbcServices()
.getBootstrapJdbcConnectionAccess()
);
return true;
}
);
}
else {
sqmMultiTableInsertStrategy = null;
}
}
private void prepareMappingModel(MappingModelCreationProcess creationProcess, PersistentClass bootEntityDescriptor) {
@ -5709,7 +5753,21 @@ public abstract class AbstractEntityPersister
entityMappingDescriptor,
creationProcess
);
}
protected static SqmMultiTableInsertStrategy interpretSqmMultiTableInsertStrategy(
AbstractEntityPersister entityMappingDescriptor,
MappingModelCreationProcess creationProcess) {
// we need the boot model so we can have access to the Table
final RootClass entityBootDescriptor = (RootClass) creationProcess.getCreationContext()
.getBootModel()
.getEntityBinding( entityMappingDescriptor.getRootEntityName() );
return SqmMutationStrategyHelper.resolveInsertStrategy(
entityBootDescriptor,
entityMappingDescriptor,
creationProcess
);
}
@Override
@ -5717,6 +5775,10 @@ public abstract class AbstractEntityPersister
return sqmMultiTableMutationStrategy;
}
public SqmMultiTableInsertStrategy getSqmMultiTableInsertStrategy() {
return sqmMultiTableInsertStrategy;
}
protected int getStateArrayInitialPosition(MappingModelCreationProcess creationProcess) {
// todo (6.0) not sure this is correct in case of SingleTable Inheritance and for Table per class when the selection is the root
int stateArrayPosition;
@ -5730,6 +5792,10 @@ public abstract class AbstractEntityPersister
return stateArrayPosition;
}
protected boolean isPhysicalDiscriminator() {
return getDiscriminatorFormulaTemplate() == null;
}
protected EntityDiscriminatorMapping generateDiscriminatorMapping(MappingModelCreationProcess modelCreationProcess) {
if ( getDiscriminatorType() == null) {
return null;
@ -5748,6 +5814,7 @@ public abstract class AbstractEntityPersister
getTableName(),
discriminatorColumnExpression,
getDiscriminatorFormulaTemplate() != null,
isPhysicalDiscriminator(),
modelCreationProcess
);
}
@ -6236,6 +6303,13 @@ public abstract class AbstractEntityPersister
if ( superMappingType != null ) {
final ModelPart superDefinedAttribute = superMappingType.findSubPart( name, superMappingType );
if ( superDefinedAttribute != null ) {
// Prefer the identifier mapping of the concrete class
if ( superDefinedAttribute instanceof EntityIdentifierMapping ) {
final ModelPart identifierModelPart = getIdentifierModelPart( name, treatTargetType );
if ( identifierModelPart != null ) {
return identifierModelPart;
}
}
return superDefinedAttribute;
}
}

View File

@ -41,6 +41,7 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.internal.InFlightEntityMappingType;
import org.hibernate.metamodel.spi.EntityRepresentationStrategy;
import org.hibernate.persister.walking.spi.EntityDefinition;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.sql.ast.spi.SqlAliasStemHelper;
import org.hibernate.sql.ast.tree.from.RootTableGroupProducer;
@ -158,6 +159,10 @@ public interface EntityPersister
throw new NotYetImplementedFor6Exception( getClass() );
}
default SqmMultiTableInsertStrategy getSqmMultiTableInsertStrategy() {
throw new NotYetImplementedFor6Exception( getClass() );
}
/**
* Retrieve the underlying entity metamodel instance...
*

View File

@ -1213,6 +1213,11 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
);
}
@Override
protected boolean isPhysicalDiscriminator() {
return explicitDiscriminatorColumnName != null;
}
@Override
protected EntityDiscriminatorMapping generateDiscriminatorMapping(MappingModelCreationProcess modelCreationProcess) {
EntityMappingType superMappingType = getSuperMappingType();

View File

@ -419,6 +419,11 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
}
}
@Override
protected boolean isPhysicalDiscriminator() {
return false;
}
@Override
protected EntityDiscriminatorMapping generateDiscriminatorMapping(MappingModelCreationProcess modelCreationProcess) {
if ( hasSubclasses() ) {

View File

@ -338,7 +338,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
final ParseTree parseTree = ctx.getChild( 0 );
if ( parseTree instanceof HqlParser.SelectStatementContext ) {
final SqmSelectStatement<R> selectStatement = visitSelectStatement( (HqlParser.SelectStatementContext) parseTree );
selectStatement.getQueryPart().validateFetchStructureAndOwners();
selectStatement.getQueryPart().validateQueryStructureAndFetchOwners();
return selectStatement;
}
else if ( parseTree instanceof HqlParser.InsertStatementContext ) {

View File

@ -11,6 +11,7 @@ import java.util.Map;
import org.hibernate.query.hql.HqlTranslator;
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
@ -60,4 +61,10 @@ public interface QueryEngineOptions {
* target of the mutation is a multi-table entity.
*/
SqmMultiTableMutationStrategy getCustomSqmMultiTableMutationStrategy();
/**
* Contract for handling SQM trees representing insertion (INSERT) queries where the
* target of the mutation is a multi-table entity.
*/
SqmMultiTableInsertStrategy getCustomSqmMultiTableInsertStrategy();
}

View File

@ -0,0 +1,37 @@
/*
* 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.query.sqm.internal;
import org.hibernate.action.internal.BulkOperationCleanupAction;
import org.hibernate.query.spi.DomainQueryExecutionContext;
import org.hibernate.query.spi.NonSelectQueryPlan;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.tree.insert.SqmInsertStatement;
/**
* @author Christian Beikov
*/
public class MultiTableInsertQueryPlan implements NonSelectQueryPlan {
private final SqmInsertStatement<?> sqmInsert;
private final DomainParameterXref domainParameterXref;
private final SqmMultiTableInsertStrategy mutationStrategy;
public MultiTableInsertQueryPlan(
SqmInsertStatement<?> sqmInsert,
DomainParameterXref domainParameterXref,
SqmMultiTableInsertStrategy mutationStrategy) {
this.sqmInsert = sqmInsert;
this.domainParameterXref = domainParameterXref;
this.mutationStrategy = mutationStrategy;
}
@Override
public int executeUpdate(DomainQueryExecutionContext executionContext) {
BulkOperationCleanupAction.schedule( executionContext.getSession(), sqmInsert );
return mutationStrategy.executeInsert( sqmInsert, domainParameterXref, executionContext );
}
}

View File

@ -36,6 +36,7 @@ import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.ImmutableEntityUpdateQueryHandlingMode;
import org.hibernate.query.Query;
import org.hibernate.query.QueryTypeMismatchException;
import org.hibernate.query.SemanticException;
import org.hibernate.query.hql.internal.NamedHqlQueryMementoImpl;
import org.hibernate.query.hql.internal.QuerySplitter;
import org.hibernate.query.hql.spi.HqlQueryImplementor;
@ -59,15 +60,21 @@ import org.hibernate.query.spi.ScrollableResultsImplementor;
import org.hibernate.query.spi.SelectQueryPlan;
import org.hibernate.query.sqm.SqmExpressable;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.tree.SqmDmlStatement;
import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement;
import org.hibernate.query.sqm.tree.insert.SqmValues;
import org.hibernate.query.sqm.tree.select.SqmQueryGroup;
import org.hibernate.query.sqm.tree.select.SqmQueryPart;
import org.hibernate.query.sqm.tree.select.SqmQuerySpec;
@ -142,6 +149,9 @@ public class QuerySqmImpl<R>
else if ( sqmStatement instanceof SqmUpdateStatement<?> ) {
verifyImmutableEntityUpdate( hqlString, (SqmUpdateStatement<R>) sqmStatement, producer.getFactory() );
}
else if ( sqmStatement instanceof SqmInsertStatement<?> ) {
verifyInsertTypesMatch( hqlString, (SqmInsertStatement<R>) sqmStatement );
}
this.resultType = resultType;
this.domainParameterXref = hqlInterpretation.getDomainParameterXref();
this.parameterMetadata = hqlInterpretation.getParameterMetadata();
@ -203,6 +213,9 @@ public class QuerySqmImpl<R>
else if ( sqmStatement instanceof SqmUpdateStatement<?> ) {
verifyImmutableEntityUpdate( hqlString, (SqmUpdateStatement<R>) sqmStatement, producer.getFactory() );
}
else if ( sqmStatement instanceof SqmInsertStatement<?> ) {
verifyInsertTypesMatch( hqlString, (SqmInsertStatement<R>) sqmStatement );
}
this.parameterMetadata = hqlInterpretation.getParameterMetadata();
this.domainParameterXref = hqlInterpretation.getDomainParameterXref();
@ -227,7 +240,7 @@ public class QuerySqmImpl<R>
SqmUtil.verifyIsSelectStatement( sqmStatement, null );
final SqmQueryPart<R> queryPart = ( (SqmSelectStatement<R>) sqmStatement ).getQueryPart();
// For criteria queries, we have to validate the fetch structure here
queryPart.validateFetchStructureAndOwners();
queryPart.validateQueryStructureAndFetchOwners();
visitQueryReturnType(
queryPart,
resultType,
@ -241,6 +254,9 @@ public class QuerySqmImpl<R>
throw new IllegalArgumentException( "No assignments specified as part of UPDATE criteria" );
}
}
else if ( sqmStatement instanceof SqmInsertStatement<?> ) {
verifyInsertTypesMatch( CRITERIA_HQL_STRING, (SqmInsertStatement<R>) sqmStatement );
}
this.hqlString = CRITERIA_HQL_STRING;
this.sqmStatement = sqmStatement;
@ -422,7 +438,7 @@ public class QuerySqmImpl<R>
.getSessionFactoryOptions()
.getImmutableEntityUpdateQueryHandlingMode();
String querySpaces = Arrays.toString( entityDescriptor.getQuerySpaces() );
final String querySpaces = Arrays.toString( entityDescriptor.getQuerySpaces() );
switch ( immutableEntityUpdateQueryHandlingMode ) {
case WARNING:
@ -436,9 +452,65 @@ public class QuerySqmImpl<R>
throw new UnsupportedOperationException(
"The " + immutableEntityUpdateQueryHandlingMode + " is not supported!"
);
}
}
private void verifyInsertTypesMatch(String hqlString, SqmInsertStatement<R> sqmStatement) {
final List<SqmPath<?>> insertionTargetPaths = sqmStatement.getInsertionTargetPaths();
if ( sqmStatement instanceof SqmInsertValuesStatement<?> ) {
final SqmInsertValuesStatement<R> statement = (SqmInsertValuesStatement<R>) sqmStatement;
for ( SqmValues sqmValues : statement.getValuesList() ) {
verifyInsertTypesMatch( hqlString, insertionTargetPaths, sqmValues.getExpressions() );
}
}
else {
final SqmInsertSelectStatement<R> statement = (SqmInsertSelectStatement<R>) sqmStatement;
final List<SqmSelection<?>> selections = statement.getSelectQueryPart()
.getFirstQuerySpec()
.getSelectClause()
.getSelections();
verifyInsertTypesMatch( hqlString, insertionTargetPaths, selections );
statement.getSelectQueryPart().validateQueryStructureAndFetchOwners();
}
}
private void verifyInsertTypesMatch(
String hqlString,
List<SqmPath<?>> insertionTargetPaths,
List<? extends SqmTypedNode<?>> expressions) {
final int size = insertionTargetPaths.size();
final int expressionsSize = expressions.size();
if ( size != expressionsSize ) {
throw new SemanticException(
String.format(
"Expected insert attribute count [%d] did not match Query selection count [%d]",
size,
expressionsSize
),
hqlString,
null
);
}
for ( int i = 0; i < expressionsSize; i++ ) {
final SqmTypedNode<?> expression = expressions.get( i );
if ( expression.getNodeJavaTypeDescriptor() == null ) {
continue;
}
if ( insertionTargetPaths.get( i ).getJavaTypeDescriptor() != expression.getNodeJavaTypeDescriptor() ) {
throw new SemanticException(
String.format(
"Expected insert attribute type [%s] did not match Query selection type [%s] at selection index [%d]",
insertionTargetPaths.get( i ).getJavaTypeDescriptor().getJavaType().getTypeName(),
expression.getNodeJavaTypeDescriptor().getJavaType().getTypeName(),
i
),
hqlString,
null
);
}
}
}
public SessionFactoryImplementor getSessionFactory() {
return getSession().getFactory();
@ -852,17 +924,16 @@ public class QuerySqmImpl<R>
private NonSelectQueryPlan buildInsertQueryPlan() {
final SqmInsertStatement<R> sqmInsert = (SqmInsertStatement<R>) getSqmStatement();
// final String entityNameToUpdate = sqmInsert.getTarget().getReferencedPathSource().getHibernateEntityName();
// final EntityPersister entityDescriptor = getSessionFactory().getDomainModel().findEntityDescriptor( entityNameToUpdate );
final String entityNameToInsert = sqmInsert.getTarget().getReferencedPathSource().getHibernateEntityName();
final EntityPersister entityDescriptor = getSessionFactory().getDomainModel().findEntityDescriptor( entityNameToInsert );
// final SqmMultiTableMutationStrategy multiTableStrategy = entityDescriptor.getSqmMultiTableMutationStrategy();
// if ( multiTableStrategy == null ) {
final SqmMultiTableInsertStrategy multiTableStrategy = entityDescriptor.getSqmMultiTableInsertStrategy();
if ( multiTableStrategy == null ) {
return new SimpleInsertQueryPlan( sqmInsert, domainParameterXref );
// }
// else {
//TODO:
// return new MultiTableUpdateQueryPlan( sqmInsert, domainParameterXref, multiTableStrategy );
// }
}
else {
return new MultiTableInsertQueryPlan( sqmInsert, domainParameterXref, multiTableStrategy );
}
}
@Override

View File

@ -444,7 +444,7 @@ public class SqmUtil {
}
@Override
public Map<JpaCriteriaParameter<?>, Supplier<SqmJpaCriteriaParameterWrapper<?>>> getJpaCriteriaParamResolutions() {
public Map<JpaCriteriaParameter<?>, SqmJpaCriteriaParameterWrapper<?>> getJpaCriteriaParamResolutions() {
return Collections.emptyMap();
}
};
@ -513,7 +513,7 @@ public class SqmUtil {
private static class ParameterResolutionsImpl implements SqmStatement.ParameterResolutions {
private final Set<SqmParameter<?>> sqmParameters;
private final Map<JpaCriteriaParameter<?>, Supplier<SqmJpaCriteriaParameterWrapper<?>>> jpaCriteriaParamResolutions;
private final Map<JpaCriteriaParameter<?>, SqmJpaCriteriaParameterWrapper<?>> jpaCriteriaParamResolutions;
public ParameterResolutionsImpl(
Set<SqmParameter<?>> sqmParameters,
@ -527,16 +527,11 @@ public class SqmUtil {
this.jpaCriteriaParamResolutions = new IdentityHashMap<>( CollectionHelper.determineProperSizing( jpaCriteriaParamResolutions ) );
for ( Map.Entry<JpaCriteriaParameter<?>, List<SqmJpaCriteriaParameterWrapper<?>>> entry : jpaCriteriaParamResolutions.entrySet() ) {
final Iterator<SqmJpaCriteriaParameterWrapper<?>> itr = entry.getValue().iterator();
this.jpaCriteriaParamResolutions.put(
entry.getKey(),
() -> {
if ( itr.hasNext() ) {
return itr.next();
if ( !itr.hasNext() ) {
throw new IllegalStateException(
"SqmJpaCriteriaParameterWrapper references for JpaCriteriaParameter [" + entry.getKey() + "] already exhausted" );
}
throw new IllegalStateException( "SqmJpaCriteriaParameterWrapper references for JpaCriteriaParameter [" + entry.getKey() + "] already exhausted" );
}
);
this.jpaCriteriaParamResolutions.put( entry.getKey(), itr.next() );
}
}
}
@ -547,7 +542,7 @@ public class SqmUtil {
}
@Override
public Map<JpaCriteriaParameter<?>, Supplier<SqmJpaCriteriaParameterWrapper<?>>> getJpaCriteriaParamResolutions() {
public Map<JpaCriteriaParameter<?>, SqmJpaCriteriaParameterWrapper<?>> getJpaCriteriaParamResolutions() {
return jpaCriteriaParamResolutions;
}
}

View File

@ -0,0 +1,13 @@
/*
* 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.query.sqm.mutation.internal;
/**
* Handler for dealing with multi-table SQM INSERT queries.
*/
public interface InsertHandler extends Handler {
}

View File

@ -7,6 +7,7 @@
package org.hibernate.query.sqm.mutation.internal;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
@ -14,6 +15,7 @@ import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.MappingModelExpressable;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.sqm.internal.DomainParameterXref;
@ -24,6 +26,7 @@ import org.hibernate.query.sqm.sql.internal.SqlAstQueryPartProcessingStateImpl;
import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.insert.SqmInsertStatement;
import org.hibernate.query.sqm.tree.predicate.SqmWhereClause;
import org.hibernate.query.sqm.tree.select.SqmSelectClause;
import org.hibernate.query.sqm.tree.update.SqmAssignment;
@ -163,6 +166,19 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
(Expression) sqmAssignment.getValue().accept( this )
);
}
/**
* Specialized hook to visit the assignments defined by the update SQM allow
* "listening" for each SQL assignment.
*/
public AdditionalInsertValues visitInsertionTargetPaths(
BiConsumer<Assignable, List<ColumnReference>> targetColumnReferenceConsumer,
SqmInsertStatement<?> sqmStatement,
EntityPersister entityDescriptor,
TableGroup tableGroup,
SqmParameterResolutionConsumer parameterResolutionConsumer) {
this.parameterResolutionConsumer = parameterResolutionConsumer;
return visitInsertionTargetPaths( targetColumnReferenceConsumer, sqmStatement, entityDescriptor, tableGroup );
}
public Predicate visitWhereClause(
SqmWhereClause sqmWhereClause,

View File

@ -17,6 +17,7 @@ import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.query.spi.SqlOmittingQueryOptions;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.from.TableReference;
@ -61,6 +62,31 @@ public class SqmMutationStrategyHelper {
.getFallbackSqmMutationStrategy( rootEntityDescriptor, creationContext );
}
/**
* Standard resolution of SqmInsertStrategy to use for a given
* entity hierarchy.
*/
public static SqmMultiTableInsertStrategy resolveInsertStrategy(
RootClass entityBootDescriptor,
EntityMappingType rootEntityDescriptor,
MappingModelCreationProcess creationProcess) {
final RuntimeModelCreationContext creationContext = creationProcess.getCreationContext();
final SessionFactoryImplementor sessionFactory = creationContext.getSessionFactory();
final SessionFactoryOptions options = sessionFactory.getSessionFactoryOptions();
final SqmMultiTableInsertStrategy specifiedStrategy = options.getCustomSqmMultiTableInsertStrategy();
if ( specifiedStrategy != null ) {
return specifiedStrategy;
}
// todo (6.0) : add capability define strategy per-hierarchy
return sessionFactory.getServiceRegistry().getService( JdbcServices.class )
.getJdbcEnvironment()
.getDialect()
.getFallbackSqmInsertStrategy( rootEntityDescriptor, creationContext );
}
public static void cleanUpCollectionTables(
EntityMappingType entityDescriptor,
BiFunction<TableReference, PluralAttributeMapping, Predicate> restrictionProducer,

View File

@ -45,9 +45,8 @@ import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.UnionTableGroup;
import org.hibernate.sql.ast.tree.from.UnionTableReference;
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
import org.hibernate.sql.ast.tree.predicate.Junction;
import org.hibernate.sql.ast.tree.predicate.Predicate;
@ -74,13 +73,13 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler
private final SqmCteTable cteTable;
private final DomainParameterXref domainParameterXref;
private final CteStrategy strategy;
private final CteMutationStrategy strategy;
public AbstractCteMutationHandler(
SqmCteTable cteTable,
SqmDeleteOrUpdateStatement sqmStatement,
SqmDeleteOrUpdateStatement<?> sqmStatement,
DomainParameterXref domainParameterXref,
CteStrategy strategy,
CteMutationStrategy strategy,
SessionFactoryImplementor sessionFactory) {
super( sqmStatement, sessionFactory );
this.cteTable = cteTable;
@ -97,7 +96,7 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler
return domainParameterXref;
}
public CteStrategy getStrategy() {
public CteMutationStrategy getStrategy() {
return strategy;
}
@ -315,18 +314,18 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler
protected TableReference resolveUnionTableReference(
TableGroup tableGroup,
TableReference tableReference,
String tableExpression) {
if ( tableGroup instanceof UnionTableGroup ) {
if ( tableReference instanceof UnionTableReference ) {
return new TableReference(
tableExpression,
tableGroup.getPrimaryTableReference().getIdentificationVariable(),
false,
tableReference.getIdentificationVariable(),
tableReference.isOptional(),
getSessionFactory()
);
}
else {
return tableGroup.getTableReference( tableGroup.getNavigablePath(), tableExpression, true, true );
return tableReference;
}
}

View File

@ -44,7 +44,7 @@ public class CteDeleteHandler extends AbstractCteMutationHandler implements Dele
SqmCteTable cteTable,
SqmDeleteStatement<?> sqmDeleteStatement,
DomainParameterXref domainParameterXref,
CteStrategy strategy,
CteMutationStrategy strategy,
SessionFactoryImplementor sessionFactory) {
super( cteTable, sqmDeleteStatement, domainParameterXref, strategy, sessionFactory );
}
@ -141,8 +141,14 @@ public class CteDeleteHandler extends AbstractCteMutationHandler implements Dele
idSelectCte.getCteTable().getCteColumns(),
factory
);
final TableReference updatingTableReference = updatingTableGroup.getTableReference(
updatingTableGroup.getNavigablePath(),
tableExpression,
true,
true
);
final TableReference dmlTableReference = resolveUnionTableReference(
updatingTableGroup,
updatingTableReference,
tableExpression
);
final List<ColumnReference> columnReferences = new ArrayList<>( idSelectCte.getCteTable().getCteColumns().size() );

View File

@ -0,0 +1,161 @@
/*
* 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.query.sqm.mutation.internal.cte;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.spi.DomainQueryExecutionContext;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.tree.cte.SqmCteTable;
import org.hibernate.query.sqm.tree.insert.SqmInsertStatement;
/**
* @asciidoc
*
* {@link SqmMultiTableInsertStrategy} implementation using SQL's modifiable CTE (Common Table Expression)
* approach to perform the update/delete. E.g. (using delete):
*
* This strategy will create a query like this:
*
* ```
* with hte_entity as (
* select *, next value for sequence from ...
* ),
* dml_cte_1 as (
* insert into base_table select e.id, e.base from hte_entity e
* returning id
* ),
* dml_cte_2 as (
* insert into sub_table select e.id, e.sub1 from hte_entity e
* returning id
* )
* select count(*) from dml_cte_1
* ```
*
* if the sequence generator has an optimizer, the optimizer is implemented in SQL like this:
*
* ```
* with hte_entity_raw as (
* select *, row_number() over() from ...
* ),
* rows_with_next_val(rn, val) as (
* -- then, fetch a sequence value for every row number that needs it
* select rn, next value for sequence FROM rows_needing_next_val
* where (e.rn-1) % [incrementSize] = 0
* ),
* hte_entity as (
* select e.*, t.val + (e.rn - t.rn) as id
* from hte_entity_raw e
* -- join the data against the generated sequence value, based on the row number group they belong to
* -- i.e. where the row number is within the increment size
* left join rows_with_next_val t ON e.rn - ((e.rn-1) % 10) = t.rn
* ),
* dml_cte_1 as (
* insert into base_table select e.id, e.base from hte_entity e
* returning id
* ),
* dml_cte_2 as (
* insert into sub_table select e.id, e.sub1 from hte_entity e
* returning id
* )
* select count(*) from dml_cte_1
* ```
*
* in case the id generator uses identity generation, a row number will be created which should ensure insert ordering
*
* ```
* with hte_entity_raw as (
* select *, row_number() over() from ...
* ),
* dml_cte_1 as (
* insert into base_table select e.id, e.base from hte_entity_raw e
* returning id, e.row_number
* ),
* with hte_entity as (
* select * from hte_entity e join dml_cte_1 c on e.row_number = c.row_number
* ),
* dml_cte_2 as (
* insert into sub_table select c.id, e.sub1 from hte_entity e
* returning id
* )
* select count(*) from dml_cte_1
* ```
*
* @author Christian Beikov
*/
public class CteInsertStrategy implements SqmMultiTableInsertStrategy {
public static final String SHORT_NAME = "cte";
private final EntityPersister rootDescriptor;
private final SessionFactoryImplementor sessionFactory;
private final SqmCteTable entityCteTable;
public CteInsertStrategy(
EntityMappingType rootEntityType,
RuntimeModelCreationContext runtimeModelCreationContext) {
this( rootEntityType.getEntityPersister(), runtimeModelCreationContext );
}
public CteInsertStrategy(
EntityPersister rootDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
this.rootDescriptor = rootDescriptor;
this.sessionFactory = runtimeModelCreationContext.getSessionFactory();
final Dialect dialect = sessionFactory.getServiceRegistry()
.getService( JdbcServices.class )
.getJdbcEnvironment()
.getDialect();
if ( !dialect.supportsNonQueryWithCTE() ) {
throw new UnsupportedOperationException(
getClass().getSimpleName() +
" can only be used with Dialects that support CTE that can take UPDATE or DELETE statements as well"
);
}
if ( !dialect.supportsValuesList() ) {
throw new UnsupportedOperationException(
getClass().getSimpleName() +
" can only be used with Dialects that support VALUES lists"
);
}
// The table name might be a sub-query, which is inappropriate for a temporary table name
final String originalTableName = rootDescriptor.getEntityPersister().getSynchronizedQuerySpaces()[0];
final String name;
if ( Identifier.isQuoted( originalTableName ) ) {
name = dialect.quote( TemporaryTable.ENTITY_TABLE_PREFIX + Identifier.unQuote( originalTableName ) );
}
else {
name = TemporaryTable.ENTITY_TABLE_PREFIX + originalTableName;
}
final String qualifiedTableName;
if ( name.length() > dialect.getMaxIdentifierLength() ) {
qualifiedTableName = name.substring( 0, dialect.getMaxIdentifierLength() );
}
else {
qualifiedTableName = name;
}
this.entityCteTable = SqmCteTable.createEntityTable( qualifiedTableName, rootDescriptor );
}
@Override
public int executeInsert(
SqmInsertStatement<?> sqmInsertStatement,
DomainParameterXref domainParameterXref,
DomainQueryExecutionContext context) {
return new CteInsertHandler( entityCteTable, sqmInsertStatement, domainParameterXref, sessionFactory ).execute( context );
}
}

View File

@ -50,21 +50,21 @@ import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
*
* @author Christian Beikov
*/
public class CteStrategy implements SqmMultiTableMutationStrategy {
public class CteMutationStrategy implements SqmMultiTableMutationStrategy {
public static final String SHORT_NAME = "cte";
public static final String TABLE_NAME = "id_cte";
public static final String ID_TABLE_NAME = "id_cte";
private final EntityPersister rootDescriptor;
private final SessionFactoryImplementor sessionFactory;
private final SqmCteTable cteTable;
private final SqmCteTable idCteTable;
public CteStrategy(
public CteMutationStrategy(
EntityMappingType rootEntityType,
RuntimeModelCreationContext runtimeModelCreationContext) {
this( rootEntityType.getEntityPersister(), runtimeModelCreationContext );
}
public CteStrategy(
public CteMutationStrategy(
EntityPersister rootDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
this.rootDescriptor = rootDescriptor;
@ -89,28 +89,28 @@ public class CteStrategy implements SqmMultiTableMutationStrategy {
);
}
this.cteTable = new SqmCteTable( TABLE_NAME, rootDescriptor );
this.idCteTable = SqmCteTable.createIdTable( ID_TABLE_NAME, rootDescriptor );
}
@Override
public int executeDelete(
SqmDeleteStatement sqmDelete,
SqmDeleteStatement<?> sqmDelete,
DomainParameterXref domainParameterXref,
DomainQueryExecutionContext context) {
checkMatch( sqmDelete );
return new CteDeleteHandler( cteTable, sqmDelete, domainParameterXref, this, sessionFactory ).execute( context );
return new CteDeleteHandler( idCteTable, sqmDelete, domainParameterXref, this, sessionFactory ).execute( context );
}
@Override
public int executeUpdate(
SqmUpdateStatement sqmUpdate,
SqmUpdateStatement<?> sqmUpdate,
DomainParameterXref domainParameterXref,
DomainQueryExecutionContext context) {
checkMatch( sqmUpdate );
return new CteUpdateHandler( cteTable, sqmUpdate, domainParameterXref, this, sessionFactory ).execute( context );
return new CteUpdateHandler( idCteTable, sqmUpdate, domainParameterXref, this, sessionFactory ).execute( context );
}
private void checkMatch(SqmDeleteOrUpdateStatement sqmStatement) {
private void checkMatch(SqmDeleteOrUpdateStatement<?> sqmStatement) {
final String targetEntityName = sqmStatement.getTarget().getEntityName();
final EntityPersister targetEntityDescriptor = sessionFactory.getDomainModel().getEntityDescriptor( targetEntityName );

View File

@ -18,6 +18,7 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.MappingModelExpressable;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.query.SemanticException;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter;
import org.hibernate.query.sqm.mutation.internal.UpdateHandler;
@ -34,7 +35,6 @@ import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
import org.hibernate.sql.ast.tree.from.UnionTableGroup;
import org.hibernate.sql.ast.tree.update.Assignment;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
@ -49,7 +49,7 @@ public class CteUpdateHandler extends AbstractCteMutationHandler implements Upda
SqmCteTable cteTable,
SqmUpdateStatement sqmStatement,
DomainParameterXref domainParameterXref,
CteStrategy strategy,
CteMutationStrategy strategy,
SessionFactoryImplementor sessionFactory) {
super( cteTable, sqmStatement, domainParameterXref, strategy, sessionFactory );
}
@ -103,7 +103,7 @@ public class CteUpdateHandler extends AbstractCteMutationHandler implements Upda
collectTableReference( updatingTableGroup.getTableReferenceJoins().get( i ), tableReferenceByAlias::put );
}
final Map<String, List<Assignment>> assignmentsByTable = CollectionHelper.mapOfSize(
final Map<TableReference, List<Assignment>> assignmentsByTable = CollectionHelper.mapOfSize(
updatingTableGroup.getTableReferenceJoins().size() + 1
);
@ -111,11 +111,11 @@ public class CteUpdateHandler extends AbstractCteMutationHandler implements Upda
final Assignment assignment = assignments.get( i );
final List<ColumnReference> assignmentColumnRefs = assignment.getAssignable().getColumnReferences();
String assignmentTableReference = null;
TableReference assignmentTableReference = null;
for ( int c = 0; c < assignmentColumnRefs.size(); c++ ) {
final ColumnReference columnReference = assignmentColumnRefs.get( c );
final String tableReference = resolveTableReference(
final TableReference tableReference = resolveTableReference(
columnReference,
tableReferenceByAlias
);
@ -144,18 +144,18 @@ public class CteUpdateHandler extends AbstractCteMutationHandler implements Upda
idSelectCte.getCteTable().getCteColumns(),
factory
);
final List<Assignment> assignmentList;
if ( updatingTableGroup instanceof UnionTableGroup ) {
assignmentList = assignmentsByTable.get( updatingTableGroup.getPrimaryTableReference().getTableExpression() );
}
else {
assignmentList = assignmentsByTable.get( tableExpression );
final TableReference updatingTableReference = updatingTableGroup.getTableReference(
updatingTableGroup.getNavigablePath(),
tableExpression,
true,
true
);
final List<Assignment> assignmentList = assignmentsByTable.get( updatingTableReference );
if ( assignmentList == null ) {
return;
}
}
final TableReference dmlTableReference = resolveUnionTableReference(
updatingTableGroup,
updatingTableReference,
tableExpression
);
final List<ColumnReference> columnReferences = new ArrayList<>( idSelectCte.getCteTable().getCteColumns().size() );
@ -191,15 +191,14 @@ public class CteUpdateHandler extends AbstractCteMutationHandler implements Upda
collectTableReference( tableReferenceJoin.getJoinedTableReference(), consumer );
}
private String resolveTableReference(
private TableReference resolveTableReference(
ColumnReference columnReference,
Map<String, TableReference> tableReferenceByAlias) {
final String qualifier = columnReference.getQualifier();
final TableReference tableReferenceByQualifier = tableReferenceByAlias.get( qualifier );
final TableReference tableReferenceByQualifier = tableReferenceByAlias.get( columnReference.getQualifier() );
if ( tableReferenceByQualifier != null ) {
return tableReferenceByQualifier.getTableExpression();
return tableReferenceByQualifier;
}
return qualifier;
throw new SemanticException( "Assignment referred to column of a joined association: " + columnReference );
}
}

View File

@ -1,159 +0,0 @@
/*
* 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.query.sqm.mutation.internal.idtable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.relational.Exportable;
import org.hibernate.dialect.Dialect;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Contributable;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.Value;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
/**
* @author Steve Ebersole
*/
public class IdTable implements Exportable, Contributable {
public static final String DEFAULT_ALIAS = "idtable_";
private final EntityMappingType entityDescriptor;
private final String qualifiedTableName;
private IdTableSessionUidColumn sessionUidColumn;
private final List<IdTableColumn> columns = new ArrayList<>();
private final Dialect dialect;
public IdTable(
EntityMappingType entityDescriptor,
Function<String, String> idTableNameAdjuster,
Dialect dialect,
RuntimeModelCreationContext runtimeModelCreationContext) {
this.entityDescriptor = entityDescriptor;
// The table name might be a sub-query, which is inappropriate for an id table name
final String originalTableName = entityDescriptor.getEntityPersister().getSynchronizedQuerySpaces()[0];
if ( Identifier.isQuoted( originalTableName ) ) {
this.qualifiedTableName = dialect.quote( idTableNameAdjuster.apply( Identifier.unQuote( originalTableName ) ) );
}
else {
this.qualifiedTableName = idTableNameAdjuster.apply( originalTableName );
}
final PersistentClass entityBinding = runtimeModelCreationContext.getBootModel()
.getEntityBinding( entityDescriptor.getEntityName() );
final Iterator<Column> itr = entityBinding.getTable().getPrimaryKey().getColumnIterator();
final Iterator<JdbcMapping> jdbcMappings = entityDescriptor.getIdentifierMapping().getJdbcMappings().iterator();
while ( itr.hasNext() ) {
final Column column = itr.next();
final JdbcMapping jdbcMapping = jdbcMappings.next();
columns.add(
new IdTableColumn(
this,
column.getText( dialect ),
jdbcMapping,
column.getSqlType( dialect, runtimeModelCreationContext.getMetadata() )
)
);
}
entityDescriptor.visitSubTypeAttributeMappings(
attribute -> {
if ( attribute instanceof PluralAttributeMapping ) {
final PluralAttributeMapping pluralAttribute = (PluralAttributeMapping) attribute;
if ( pluralAttribute.getSeparateCollectionTable() != null ) {
// Ensure that the FK target columns are available
final ModelPart fkTarget = pluralAttribute.getKeyDescriptor().getTargetPart();
if ( !( fkTarget instanceof EntityIdentifierMapping ) ) {
final Value value = entityBinding.getSubclassProperty( pluralAttribute.getAttributeName() )
.getValue();
final Iterator<Selectable> columnIterator = ( (Collection) value ).getKey()
.getColumnIterator();
fkTarget.forEachSelectable(
(columnIndex, selection) -> {
final Selectable selectable = columnIterator.next();
if ( selectable instanceof Column ) {
columns.add(
new IdTableColumn(
this,
selectable.getText( dialect ),
selection.getJdbcMapping(),
( (Column) selectable ).getSqlType(
dialect,
runtimeModelCreationContext.getMetadata()
)
)
);
}
}
);
}
}
}
}
);
this.dialect = dialect;
}
public EntityMappingType getEntityDescriptor() {
return entityDescriptor;
}
public String getQualifiedTableName() {
return qualifiedTableName;
}
public List<IdTableColumn> getIdTableColumns() {
return columns;
}
public IdTableSessionUidColumn getSessionUidColumn() {
return sessionUidColumn;
}
public String getTableExpression() {
return qualifiedTableName;
}
public void addColumn(IdTableColumn column) {
columns.add( column );
if ( column instanceof IdTableSessionUidColumn ) {
this.sessionUidColumn = (IdTableSessionUidColumn) column;
}
}
@Override
public String getContributor() {
return entityDescriptor.getContributor();
}
@Override
public String getExportIdentifier() {
return getQualifiedTableName();
}
public Dialect getDialect() {
return this.dialect;
}
}

View File

@ -1,171 +0,0 @@
/*
* 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.query.sqm.mutation.internal.idtable;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.function.Function;
import java.util.function.Supplier;
import org.hibernate.boot.TempTableDdlTransactionHandling;
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;
import org.hibernate.query.spi.DomainQueryExecutionContext;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
import org.jboss.logging.Logger;
/**
* Strategy based on ANSI SQL's definition of a "local temporary table" (local to each db session).
*
* @author Steve Ebersole
*/
public class LocalTemporaryTableStrategy implements SqmMultiTableMutationStrategy {
private static final Logger log = Logger.getLogger( LocalTemporaryTableStrategy.class );
public static final String SHORT_NAME = "local_temporary";
public static final String DROP_ID_TABLES = "hibernate.hql.bulk_id_strategy.local_temporary.drop_tables";
private final IdTable idTable;
private final Supplier<IdTableExporter> idTableExporterAccess;
private final AfterUseAction afterUseAction;
private final TempTableDdlTransactionHandling ddlTransactionHandling;
private final SessionFactoryImplementor sessionFactory;
private boolean dropIdTables;
public LocalTemporaryTableStrategy(
IdTable idTable,
Supplier<IdTableExporter> idTableExporterAccess,
AfterUseAction afterUseAction,
TempTableDdlTransactionHandling ddlTransactionHandling,
SessionFactoryImplementor sessionFactory) {
this.idTable = idTable;
this.idTableExporterAccess = idTableExporterAccess;
this.afterUseAction = afterUseAction;
this.ddlTransactionHandling = ddlTransactionHandling;
this.sessionFactory = sessionFactory;
}
public LocalTemporaryTableStrategy(
IdTable idTable,
Function<Integer, String> databaseTypeNameResolver,
AfterUseAction afterUseAction,
TempTableDdlTransactionHandling ddlTransactionHandling,
SessionFactoryImplementor sessionFactory) {
this(
idTable,
() -> new TempIdTableExporter( true, databaseTypeNameResolver ),
afterUseAction,
ddlTransactionHandling,
sessionFactory
);
}
@Override
public int executeUpdate(
SqmUpdateStatement sqmUpdate,
DomainParameterXref domainParameterXref,
DomainQueryExecutionContext context) {
return new TableBasedUpdateHandler(
sqmUpdate,
domainParameterXref,
idTable,
session -> {
throw new UnsupportedOperationException( "Unexpected call to access Session uid" );
},
idTableExporterAccess,
BeforeUseAction.CREATE,
afterUseAction,
ddlTransactionHandling,
sessionFactory
).execute( context );
}
@Override
public int executeDelete(
SqmDeleteStatement sqmDelete,
DomainParameterXref domainParameterXref,
DomainQueryExecutionContext context) {
return new TableBasedDeleteHandler(
sqmDelete,
domainParameterXref,
idTable,
session -> {
throw new UnsupportedOperationException( "Unexpected call to access Session uid" );
},
idTableExporterAccess,
BeforeUseAction.CREATE,
afterUseAction,
ddlTransactionHandling,
sessionFactory
).execute( context );
}
@Override
public void prepare(
MappingModelCreationProcess mappingModelCreationProcess,
JdbcConnectionAccess connectionAccess) {
final ConfigurationService configService = mappingModelCreationProcess.getCreationContext()
.getBootstrapContext()
.getServiceRegistry().getService( ConfigurationService.class );
this.dropIdTables = configService.getSetting(
DROP_ID_TABLES,
StandardConverters.BOOLEAN,
false
);
}
@Override
public void release(
SessionFactoryImplementor sessionFactory,
JdbcConnectionAccess connectionAccess) {
if ( !dropIdTables ) {
return;
}
dropIdTables = false;
log.debugf( "Dropping local-temp ID table : %s", idTable.getTableExpression() );
final IdTableHelper.IdTableDropWork idTableDropWork = new IdTableHelper.IdTableDropWork(
idTable,
idTableExporterAccess.get(),
sessionFactory
);
Connection connection;
try {
connection = connectionAccess.obtainConnection();
}
catch (UnsupportedOperationException e) {
// assume this comes from org.hibernate.engine.jdbc.connections.internal.UserSuppliedConnectionProviderImpl
log.debugf( "Unable to obtain JDBC connection; unable to drop local-temp ID table : %s", idTable.getTableExpression() );
return;
}
catch (SQLException e) {
log.error( "Unable obtain JDBC Connection", e );
return;
}
try {
idTableDropWork.execute( connection );
}
finally {
try {
connectionAccess.releaseConnection( connection );
}
catch (SQLException ignore) {
}
}
}
}

View File

@ -1,86 +0,0 @@
/*
* 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.query.sqm.mutation.internal.idtable;
import java.util.function.Function;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
/**
* @author Steve Ebersole
*/
@SuppressWarnings("WeakerAccess")
public class PhysicalIdTableExporter implements IdTableExporter {
protected String getCreateCommand() {
return "create table";
}
protected String getCreateOptions() {
return null;
}
protected String getDropCommand() {
return "drop table";
}
protected String getTruncateIdTableCommand(){
return "delete from";
}
@Override
public String getSqlCreateCommand(IdTable idTable) {
final StringBuilder buffer = new StringBuilder( getCreateCommand() ).append( ' ' );
buffer.append( idTable.getQualifiedTableName() );
buffer.append( '(' );
boolean firstPass = true;
for ( IdTableColumn column : idTable.getIdTableColumns() ) {
if ( firstPass ) {
firstPass = false;
}
else {
buffer.append( ", " );
}
buffer.append( column.getColumnName() ).append( ' ' );
buffer.append( column.getSqlTypeDefinition() );
// id values cannot be null
buffer.append( " not null" );
}
buffer.append( ") " );
final String createOptions = getCreateOptions();
if ( createOptions != null ) {
buffer.append( createOptions );
}
return buffer.toString();
}
@Override
public String getSqlDropCommand(IdTable idTable) {
return getDropCommand() + ' ' + idTable.getQualifiedTableName();
}
@Override
public String getSqlTruncateCommand(
IdTable idTable,
Function<SharedSessionContractImplementor, String> sessionUidAccess,
SharedSessionContractImplementor session) {
if ( idTable.getSessionUidColumn() != null ) {
assert sessionUidAccess != null;
final String uid = sessionUidAccess.apply( session );
return getTruncateIdTableCommand() + " " + idTable.getQualifiedTableName()
+ " where " + idTable.getSessionUidColumn().getColumnName() + " = " + uid;
}
else {
return getTruncateIdTableCommand() + " " + idTable.getQualifiedTableName();
}
}
}

View File

@ -1,104 +0,0 @@
/*
* 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.query.sqm.mutation.internal.idtable;
import java.util.function.Function;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
/**
* @author Steve Ebersole
*/
@SuppressWarnings("WeakerAccess")
public class TempIdTableExporter implements IdTableExporter {
private final boolean isLocal;
private final Function<Integer, String> databaseTypeNameResolver;
public TempIdTableExporter(
boolean isLocal,
Function<Integer, String> databaseTypeNameResolver) {
this.isLocal = isLocal;
this.databaseTypeNameResolver = databaseTypeNameResolver;
}
protected String getCreateCommand() {
return "create " + (isLocal ? "local" : "global") + " temporary table";
}
protected String getCreateOptions() {
return null;
}
protected String getDropCommand() {
return "drop table";
}
protected String getTruncateIdTableCommand(){
return "delete from";
}
@Override
public String getSqlCreateCommand(IdTable idTable) {
final StringBuilder buffer = new StringBuilder( getCreateCommand() ).append( ' ' );
buffer.append( idTable.getQualifiedTableName() );
buffer.append( '(' );
boolean firstPass = true;
for ( IdTableColumn column : idTable.getIdTableColumns() ) {
if ( firstPass ) {
firstPass = false;
}
else {
buffer.append( ", " );
}
buffer.append( column.getColumnName() ).append( ' ' );
final int sqlTypeCode = column.getJdbcMapping().getJdbcTypeDescriptor().getDefaultSqlTypeCode();
final String databaseTypeName = column.getSqlTypeDefinition();
buffer.append( " " ).append( databaseTypeName ).append( " " );
final String columnAnnotation = idTable.getDialect().getCreateTemporaryTableColumnAnnotation( sqlTypeCode );
if ( !columnAnnotation.isEmpty() ) {
buffer.append(" ").append( columnAnnotation );
}
// id values cannot be null
buffer.append( " not null" );
}
buffer.append( ") " );
final String createOptions = getCreateOptions();
if ( createOptions != null ) {
buffer.append( createOptions );
}
return buffer.toString();
}
@Override
public String getSqlDropCommand(IdTable idTable) {
return getDropCommand() + " " + idTable.getQualifiedTableName();
}
@Override
public String getSqlTruncateCommand(
IdTable idTable,
Function<SharedSessionContractImplementor, String> sessionUidAccess,
SharedSessionContractImplementor session) {
if ( idTable.getSessionUidColumn() != null ) {
assert sessionUidAccess != null;
final String uid = sessionUidAccess.apply( session );
return getTruncateIdTableCommand() + " " + idTable.getQualifiedTableName()
+ " where " + idTable.getSessionUidColumn().getColumnName() + " = " + uid;
}
else {
return getTruncateIdTableCommand() + " " + idTable.getQualifiedTableName();
}
}
}

View File

@ -25,10 +25,10 @@ import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
* @author Steve Ebersole
*/
@SuppressWarnings("unused")
public class InlineStrategy implements SqmMultiTableMutationStrategy {
public class InlineMutationStrategy implements SqmMultiTableMutationStrategy {
private final Function<SqmDeleteOrUpdateStatement,MatchingIdRestrictionProducer> matchingIdsStrategy;
public InlineStrategy(Dialect dialect) {
public InlineMutationStrategy(Dialect dialect) {
this( determinePredicateProducer( dialect ) );
}
@ -36,13 +36,13 @@ public class InlineStrategy implements SqmMultiTableMutationStrategy {
return statement -> new InPredicateRestrictionProducer();
}
public InlineStrategy(Function<SqmDeleteOrUpdateStatement,MatchingIdRestrictionProducer> matchingIdsStrategy) {
public InlineMutationStrategy(Function<SqmDeleteOrUpdateStatement,MatchingIdRestrictionProducer> matchingIdsStrategy) {
this.matchingIdsStrategy = matchingIdsStrategy;
}
@Override
public int executeUpdate(
SqmUpdateStatement sqmUpdate,
SqmUpdateStatement<?> sqmUpdate,
DomainParameterXref domainParameterXref,
DomainQueryExecutionContext context) {
final InlineUpdateHandler handler = new InlineUpdateHandler(
@ -56,7 +56,7 @@ public class InlineStrategy implements SqmMultiTableMutationStrategy {
@Override
public int executeDelete(
SqmDeleteStatement sqmDelete,
SqmDeleteStatement<?> sqmDelete,
DomainParameterXref domainParameterXref,
DomainQueryExecutionContext context) {
final InlineDeleteHandler deleteHandler = new InlineDeleteHandler(

View File

@ -4,10 +4,10 @@
* 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.query.sqm.mutation.internal.idtable;
package org.hibernate.query.sqm.mutation.internal.temptable;
/**
* Actions to perform in regards to an id-table after each use.
* Actions to perform in regards to an temporary table after each use.
*
* @author Steve Ebersole
*/

View File

@ -4,10 +4,10 @@
* 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.query.sqm.mutation.internal.idtable;
package org.hibernate.query.sqm.mutation.internal.temptable;
/**
* Actions to perform in regards to an id-table prior to each use.
* Actions to perform in regards to a temporary table prior to each use.
*
* @author Steve Ebersole
*/

View File

@ -4,14 +4,17 @@
* 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.query.sqm.mutation.internal.idtable;
package org.hibernate.query.sqm.mutation.internal.temptable;
import java.util.function.Function;
import java.util.function.Supplier;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.boot.TempTableDdlTransactionHandling;
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.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -44,14 +47,14 @@ import org.hibernate.sql.results.internal.SqlSelectionImpl;
* @author Steve Ebersole
*/
@SuppressWarnings("WeakerAccess")
public final class ExecuteWithIdTableHelper {
private ExecuteWithIdTableHelper() {
public final class ExecuteWithTemporaryTableHelper {
private ExecuteWithTemporaryTableHelper() {
}
public static int saveMatchingIdsIntoIdTable(
MultiTableSqmMutationConverter sqmConverter,
Predicate suppliedPredicate,
IdTable idTable,
TemporaryTable idTable,
Function<SharedSessionContractImplementor, String> sessionUidAccess,
JdbcParameterBindings jdbcParameterBindings,
ExecutionContext executionContext) {
@ -62,11 +65,16 @@ public final class ExecuteWithIdTableHelper {
assert mutatingTableGroup.getModelPart() instanceof EntityMappingType;
final EntityMappingType mutatingEntityDescriptor = (EntityMappingType) mutatingTableGroup.getModelPart();
final TableReference idTableReference = new TableReference( idTable.getTableExpression(), null, false, factory );
final TableReference idTableReference = new TableReference(
idTable.getTableExpression(),
null,
false,
factory
);
final InsertStatement idTableInsert = new InsertStatement( idTableReference );
for ( int i = 0; i < idTable.getIdTableColumns().size(); i++ ) {
final IdTableColumn column = idTable.getIdTableColumns().get( i );
for ( int i = 0; i < idTable.getColumns().size(); i++ ) {
final TemporaryTableColumn column = idTable.getColumns().get( i );
idTableInsert.addTargetColumnReferences(
new ColumnReference(
idTableReference,
@ -97,7 +105,10 @@ public final class ExecuteWithIdTableHelper {
jdbcPosition,
jdbcPosition + 1,
sqmConverter.getSqlExpressionResolver().resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey( tableReference, selection.getSelectionExpression() ),
SqlExpressionResolver.createColumnReferenceKey(
tableReference,
selection.getSelectionExpression()
),
sqlAstProcessingState -> new ColumnReference(
tableReference,
selection,
@ -124,7 +135,14 @@ public final class ExecuteWithIdTableHelper {
}
matchingIdSelection.applyPredicate( suppliedPredicate );
return saveIntoTemporaryTable( idTableInsert, jdbcParameterBindings, executionContext );
}
public static int saveIntoTemporaryTable(
InsertStatement temporaryTableInsert,
JdbcParameterBindings jdbcParameterBindings,
ExecutionContext executionContext) {
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
final JdbcServices jdbcServices = factory.getJdbcServices();
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
@ -133,8 +151,11 @@ public final class ExecuteWithIdTableHelper {
// 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
if ( !jdbcEnvironment.getDialect().supportsOuterJoinForUpdate() ) {
matchingIdSelection.getFromClause().visitTableJoins(
if ( temporaryTableInsert.getSourceSelectStatement() != null
&& !jdbcEnvironment.getDialect().supportsOuterJoinForUpdate() ) {
temporaryTableInsert.getSourceSelectStatement().forEachQuerySpec(
querySpec -> {
querySpec.getFromClause().visitTableJoins(
tableJoin -> {
if ( tableJoin.getJoinType() != SqlAstJoinType.INNER ) {
lockOptions.setLockMode( lockMode );
@ -142,7 +163,9 @@ public final class ExecuteWithIdTableHelper {
}
);
}
final JdbcInsert jdbcInsert = sqlAstTranslatorFactory.buildInsertTranslator( factory, idTableInsert )
);
}
final JdbcInsert jdbcInsert = sqlAstTranslatorFactory.buildInsertTranslator( factory, temporaryTableInsert )
.translate( jdbcParameterBindings, executionContext.getQueryOptions() );
lockOptions.setLockMode( lockMode );
@ -159,7 +182,7 @@ public final class ExecuteWithIdTableHelper {
}
public static QuerySpec createIdTableSelectQuerySpec(
IdTable idTable,
TemporaryTable idTable,
Function<SharedSessionContractImplementor, String> sessionUidAccess,
EntityMappingType entityDescriptor,
ExecutionContext executionContext) {
@ -167,7 +190,7 @@ public final class ExecuteWithIdTableHelper {
}
public static QuerySpec createIdTableSelectQuerySpec(
IdTable idTable,
TemporaryTable idTable,
ModelPart fkModelPart,
Function<SharedSessionContractImplementor, String> sessionUidAccess,
EntityMappingType entityDescriptor,
@ -176,7 +199,7 @@ public final class ExecuteWithIdTableHelper {
final TableReference idTableReference = new TableReference(
idTable.getTableExpression(),
IdTable.DEFAULT_ALIAS,
TemporaryTable.DEFAULT_ALIAS,
true,
executionContext.getSession().getFactory()
);
@ -201,25 +224,25 @@ public final class ExecuteWithIdTableHelper {
private static void applyIdTableSelections(
QuerySpec querySpec,
TableReference tableReference,
IdTable idTable,
TemporaryTable idTable,
ModelPart fkModelPart,
ExecutionContext executionContext) {
if ( fkModelPart == null ) {
final int size = idTable.getEntityDescriptor().getIdentifierMapping().getJdbcTypeCount();
for ( int i = 0; i < size; i++ ) {
final IdTableColumn idTableColumn = idTable.getIdTableColumns().get( i );
if ( idTableColumn != idTable.getSessionUidColumn() ) {
final TemporaryTableColumn temporaryTableColumn = idTable.getColumns().get( i );
if ( temporaryTableColumn != idTable.getSessionUidColumn() ) {
querySpec.getSelectClause().addSqlSelection(
new SqlSelectionImpl(
i + 1,
i,
new ColumnReference(
tableReference,
idTableColumn.getColumnName(),
temporaryTableColumn.getColumnName(),
false,
null,
null,
idTableColumn.getJdbcMapping(),
temporaryTableColumn.getJdbcMapping(),
executionContext.getSession().getFactory()
)
)
@ -253,7 +276,7 @@ public final class ExecuteWithIdTableHelper {
private static void applyIdTableRestrictions(
QuerySpec querySpec,
TableReference idTableReference,
IdTable idTable,
TemporaryTable idTable,
Function<SharedSessionContractImplementor, String> sessionUidAccess,
ExecutionContext executionContext) {
if ( idTable.getSessionUidColumn() != null ) {
@ -278,21 +301,20 @@ public final class ExecuteWithIdTableHelper {
}
}
public static void performBeforeIdTableUseActions(
BeforeUseAction beforeUseAction,
IdTable idTable,
Supplier<IdTableExporter> idTableExporterAccess,
TempTableDdlTransactionHandling ddlTransactionHandling,
public static void performBeforeTemporaryTableUseActions(
TemporaryTable temporaryTable,
ExecutionContext executionContext) {
if ( beforeUseAction == BeforeUseAction.CREATE ) {
final IdTableHelper.IdTableCreationWork idTableCreationWork = new IdTableHelper.IdTableCreationWork(
idTable,
idTableExporterAccess.get(),
executionContext.getSession().getFactory()
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
final Dialect dialect = factory.getJdbcServices().getDialect();
if ( dialect.getTemporaryTableBeforeUseAction() == BeforeUseAction.CREATE ) {
final TemporaryTableHelper.TemporaryTableCreationWork temporaryTableCreationWork = new TemporaryTableHelper.TemporaryTableCreationWork(
temporaryTable,
factory
);
final TempTableDdlTransactionHandling ddlTransactionHandling = dialect.getTemporaryTableDdlTransactionHandling();
if ( ddlTransactionHandling == TempTableDdlTransactionHandling.NONE ) {
executionContext.getSession().doWork( idTableCreationWork );
executionContext.getSession().doWork( temporaryTableCreationWork );
}
else {
final IsolationDelegate isolationDelegate = executionContext.getSession()
@ -300,35 +322,35 @@ public final class ExecuteWithIdTableHelper {
.getJdbcSessionOwner()
.getTransactionCoordinator()
.createIsolationDelegate();
isolationDelegate.delegateWork( idTableCreationWork, ddlTransactionHandling == TempTableDdlTransactionHandling.ISOLATE_AND_TRANSACT );
isolationDelegate.delegateWork( temporaryTableCreationWork, ddlTransactionHandling == TempTableDdlTransactionHandling.ISOLATE_AND_TRANSACT );
}
}
}
public static void performAfterIdTableUseActions(
AfterUseAction afterUseAction,
IdTable idTable,
Supplier<IdTableExporter> idTableExporterAccess,
TempTableDdlTransactionHandling ddlTransactionHandling,
public static void performAfterTemporaryTableUseActions(
TemporaryTable temporaryTable,
Function<SharedSessionContractImplementor, String> sessionUidAccess,
ExecutionContext executionContext) {
if ( afterUseAction == AfterUseAction.CLEAN ) {
IdTableHelper.cleanIdTableRows(
idTable,
idTableExporterAccess.get(),
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
final Dialect dialect = factory.getJdbcServices().getDialect();
switch ( dialect.getTemporaryTableAfterUseAction() ) {
case CLEAN:
TemporaryTableHelper.cleanTemporaryTableRows(
temporaryTable,
dialect.getTemporaryTableExporter(),
sessionUidAccess,
executionContext.getSession()
);
}
else if ( afterUseAction == AfterUseAction.DROP ) {
final IdTableHelper.IdTableDropWork idTableDropWork = new IdTableHelper.IdTableDropWork(
idTable,
idTableExporterAccess.get(),
executionContext.getSession().getFactory()
break;
case DROP:
final TemporaryTableHelper.TemporaryTableDropWork temporaryTableDropWork = new TemporaryTableHelper.TemporaryTableDropWork(
temporaryTable,
factory
);
final TempTableDdlTransactionHandling ddlTransactionHandling = dialect.getTemporaryTableDdlTransactionHandling();
if ( ddlTransactionHandling == TempTableDdlTransactionHandling.NONE ) {
executionContext.getSession().doWork( idTableDropWork );
executionContext.getSession().doWork( temporaryTableDropWork );
}
else {
final IsolationDelegate isolationDelegate = executionContext.getSession()
@ -336,7 +358,10 @@ public final class ExecuteWithIdTableHelper {
.getJdbcSessionOwner()
.getTransactionCoordinator()
.createIsolationDelegate();
isolationDelegate.delegateWork( idTableDropWork, ddlTransactionHandling == TempTableDdlTransactionHandling.ISOLATE_AND_TRANSACT );
isolationDelegate.delegateWork(
temporaryTableDropWork,
ddlTransactionHandling == TempTableDdlTransactionHandling.ISOLATE_AND_TRANSACT
);
}
}
}

View File

@ -4,9 +4,8 @@
* 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.query.sqm.mutation.internal.idtable;
package org.hibernate.query.sqm.mutation.internal.temptable;
import org.hibernate.LockMode;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.NavigablePath;

View File

@ -0,0 +1,45 @@
/*
* 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.query.sqm.mutation.internal.temptable;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.spi.DomainQueryExecutionContext;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.tree.insert.SqmInsertStatement;
/**
* Strategy based on ANSI SQL's definition of a "global temporary table".
*
* @author Steve Ebersole
*/
public class GlobalTemporaryTableInsertStrategy extends GlobalTemporaryTableStrategy implements SqmMultiTableInsertStrategy {
public GlobalTemporaryTableInsertStrategy(
TemporaryTable entityTable,
SessionFactoryImplementor sessionFactory) {
super( entityTable, sessionFactory );
}
@Override
public int executeInsert(
SqmInsertStatement<?> sqmInsertStatement,
DomainParameterXref domainParameterXref,
DomainQueryExecutionContext context) {
return new TableBasedInsertHandler(
sqmInsertStatement,
domainParameterXref,
getTemporaryTable(),
// generally a global temp table should already track a Connection-specific uid,
// but just in case a particular env needs it...
session -> session.getSessionIdentifier().toString(),
getSessionFactory()
).execute( context );
}
}

View File

@ -0,0 +1,61 @@
/*
* 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.query.sqm.mutation.internal.temptable;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.spi.DomainQueryExecutionContext;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
/**
* Strategy based on ANSI SQL's definition of a "global temporary table".
*
* @author Steve Ebersole
*/
public class GlobalTemporaryTableMutationStrategy extends GlobalTemporaryTableStrategy implements SqmMultiTableMutationStrategy {
public GlobalTemporaryTableMutationStrategy(
TemporaryTable idTable,
SessionFactoryImplementor sessionFactory) {
super( idTable, sessionFactory );
}
@Override
public int executeUpdate(
SqmUpdateStatement<?> sqmUpdate,
DomainParameterXref domainParameterXref,
DomainQueryExecutionContext context) {
return new TableBasedUpdateHandler(
sqmUpdate,
domainParameterXref,
getTemporaryTable(),
// generally a global temp table should already track a Connection-specific uid,
// but just in case a particular env needs it...
session -> session.getSessionIdentifier().toString(),
getSessionFactory()
).execute( context );
}
@Override
public int executeDelete(
SqmDeleteStatement<?> sqmDelete,
DomainParameterXref domainParameterXref,
DomainQueryExecutionContext context) {
return new TableBasedDeleteHandler(
sqmDelete,
domainParameterXref,
getTemporaryTable(),
// generally a global temp table should already track a Connection-specific uid,
// but just in case a particular env needs it...
session -> session.getSessionIdentifier().toString(),
getSessionFactory()
).execute( context );
}
}

View File

@ -4,23 +4,18 @@
* 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.query.sqm.mutation.internal.idtable;
package org.hibernate.query.sqm.mutation.internal.temptable;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.function.Supplier;
import org.hibernate.boot.TempTableDdlTransactionHandling;
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;
import org.hibernate.query.spi.DomainQueryExecutionContext;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
import org.jboss.logging.Logger;
@ -29,15 +24,13 @@ import org.jboss.logging.Logger;
*
* @author Steve Ebersole
*/
public class GlobalTemporaryTableStrategy implements SqmMultiTableMutationStrategy {
public class GlobalTemporaryTableStrategy {
private static final Logger log = Logger.getLogger( GlobalTemporaryTableStrategy.class );
public static final String SHORT_NAME = "global_temporary";
public static final String DROP_ID_TABLES = "hibernate.hql.bulk_id_strategy.global_temporary.drop_tables";
private final IdTable idTable;
private final AfterUseAction afterUseAction;
private final Supplier<IdTableExporter> idTableExporterAccess;
private final TemporaryTable temporaryTable;
private final SessionFactoryImplementor sessionFactory;
@ -45,61 +38,16 @@ public class GlobalTemporaryTableStrategy implements SqmMultiTableMutationStrate
private boolean dropIdTables;
public GlobalTemporaryTableStrategy(
IdTable idTable,
Supplier<IdTableExporter> idTableExporterAccess,
AfterUseAction afterUseAction,
TemporaryTable temporaryTable,
SessionFactoryImplementor sessionFactory) {
this.idTable = idTable;
this.idTableExporterAccess = idTableExporterAccess;
this.afterUseAction = afterUseAction;
this.temporaryTable = temporaryTable;
this.sessionFactory = sessionFactory;
if ( afterUseAction == AfterUseAction.DROP ) {
throw new IllegalArgumentException( "Global-temp ID tables cannot use AfterUseAction.DROP : " + idTable.getTableExpression() );
if ( sessionFactory.getJdbcServices().getDialect().getTemporaryTableAfterUseAction() == AfterUseAction.DROP ) {
throw new IllegalArgumentException( "Global-temp ID tables cannot use AfterUseAction.DROP : " + temporaryTable.getTableExpression() );
}
}
@Override
public int executeUpdate(
SqmUpdateStatement sqmUpdate,
DomainParameterXref domainParameterXref,
DomainQueryExecutionContext context) {
return new TableBasedUpdateHandler(
sqmUpdate,
domainParameterXref,
idTable,
// generally a global temp table should already track a Connection-specific uid,
// but just in case a particular env needs it...
session -> session.getSessionIdentifier().toString(),
idTableExporterAccess,
BeforeUseAction.CREATE,
afterUseAction,
TempTableDdlTransactionHandling.NONE,
sessionFactory
).execute( context );
}
@Override
public int executeDelete(
SqmDeleteStatement sqmDelete,
DomainParameterXref domainParameterXref,
DomainQueryExecutionContext context) {
return new TableBasedDeleteHandler(
sqmDelete,
domainParameterXref,
idTable,
// generally a global temp table should already track a Connection-specific uid,
// but just in case a particular env needs it...
session -> session.getSessionIdentifier().toString(),
idTableExporterAccess,
BeforeUseAction.CREATE,
afterUseAction,
TempTableDdlTransactionHandling.NONE,
sessionFactory
).execute( context );
}
@Override
public void prepare(
MappingModelCreationProcess mappingModelCreationProcess,
JdbcConnectionAccess connectionAccess) {
@ -109,11 +57,10 @@ public class GlobalTemporaryTableStrategy implements SqmMultiTableMutationStrate
prepared = true;
log.debugf( "Creating global-temp ID table : %s", idTable.getTableExpression() );
log.debugf( "Creating global-temp ID table : %s", getTemporaryTable().getTableExpression() );
final IdTableHelper.IdTableCreationWork idTableCreationWork = new IdTableHelper.IdTableCreationWork(
idTable,
idTableExporterAccess.get(),
final TemporaryTableHelper.TemporaryTableCreationWork temporaryTableCreationWork = new TemporaryTableHelper.TemporaryTableCreationWork(
getTemporaryTable(),
sessionFactory
);
Connection connection;
@ -131,7 +78,7 @@ public class GlobalTemporaryTableStrategy implements SqmMultiTableMutationStrate
}
try {
idTableCreationWork.execute( connection );
temporaryTableCreationWork.execute( connection );
final ConfigurationService configService = mappingModelCreationProcess.getCreationContext()
.getBootstrapContext()
.getServiceRegistry().getService( ConfigurationService.class );
@ -150,7 +97,6 @@ public class GlobalTemporaryTableStrategy implements SqmMultiTableMutationStrate
}
}
@Override
public void release(
SessionFactoryImplementor sessionFactory,
JdbcConnectionAccess connectionAccess) {
@ -160,11 +106,10 @@ public class GlobalTemporaryTableStrategy implements SqmMultiTableMutationStrate
dropIdTables = false;
log.debugf( "Dropping global-temp ID table : %s", idTable.getTableExpression() );
log.debugf( "Dropping global-temp ID table : %s", getTemporaryTable().getTableExpression() );
final IdTableHelper.IdTableDropWork idTableDropWork = new IdTableHelper.IdTableDropWork(
idTable,
idTableExporterAccess.get(),
final TemporaryTableHelper.TemporaryTableDropWork temporaryTableDropWork = new TemporaryTableHelper.TemporaryTableDropWork(
getTemporaryTable(),
sessionFactory
);
Connection connection;
@ -173,7 +118,7 @@ public class GlobalTemporaryTableStrategy implements SqmMultiTableMutationStrate
}
catch (UnsupportedOperationException e) {
// assume this comes from org.hibernate.engine.jdbc.connections.internal.UserSuppliedConnectionProviderImpl
log.debugf( "Unable to obtain JDBC connection; unable to drop global-temp ID table : %s", idTable.getTableExpression() );
log.debugf( "Unable to obtain JDBC connection; unable to drop global-temp ID table : %s", getTemporaryTable().getTableExpression() );
return;
}
catch (SQLException e) {
@ -182,7 +127,7 @@ public class GlobalTemporaryTableStrategy implements SqmMultiTableMutationStrate
}
try {
idTableDropWork.execute( connection );
temporaryTableDropWork.execute( connection );
}
finally {
try {
@ -192,4 +137,12 @@ public class GlobalTemporaryTableStrategy implements SqmMultiTableMutationStrate
}
}
}
public TemporaryTable getTemporaryTable() {
return temporaryTable;
}
public SessionFactoryImplementor getSessionFactory() {
return sessionFactory;
}
}

View File

@ -0,0 +1,694 @@
/*
* 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.query.sqm.mutation.internal.temptable;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.dialect.temptable.TemporaryTableColumn;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.IdentityGenerator;
import org.hibernate.id.OptimizableGenerator;
import org.hibernate.id.PostInsertIdentifierGenerator;
import org.hibernate.id.PostInsertIdentityPersister;
import org.hibernate.id.enhanced.Optimizer;
import org.hibernate.id.insert.Binder;
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.MappingModelExpressable;
import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.query.ComparisonOperator;
import org.hibernate.query.SemanticException;
import org.hibernate.query.SortOrder;
import org.hibernate.query.results.TableGroupImpl;
import org.hibernate.query.spi.DomainQueryExecutionContext;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.internal.SqmUtil;
import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.insert.SqmInsertStatement;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.UnionTableReference;
import org.hibernate.sql.ast.tree.insert.InsertStatement;
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.ast.tree.select.SortSpecification;
import org.hibernate.sql.ast.tree.update.Assignable;
import org.hibernate.sql.ast.tree.update.Assignment;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl;
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
import org.hibernate.sql.exec.internal.JdbcParameterImpl;
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcInsert;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.exec.spi.JdbcSelect;
import org.hibernate.sql.exec.spi.JdbcUpdate;
import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.sql.results.internal.SqlSelectionImpl;
import org.hibernate.sql.results.spi.ListResultsConsumer;
import org.hibernate.type.descriptor.ValueBinder;
/**
* @author Steve Ebersole
*/
public class InsertExecutionDelegate implements TableBasedInsertHandler.ExecutionDelegate {
private final SqmInsertStatement<?> sqmInsert;
private final MultiTableSqmMutationConverter sqmConverter;
private final TemporaryTable entityTable;
private final Function<SharedSessionContractImplementor, String> sessionUidAccess;
private final DomainParameterXref domainParameterXref;
private final TableGroup updatingTableGroup;
private final InsertStatement insertStatement;
private final EntityMappingType entityDescriptor;
private final JdbcParameterBindings jdbcParameterBindings;
private final Map<TableReference, List<Assignment>> assignmentsByTable;
private final Map<SqmParameter, MappingModelExpressable> paramTypeResolutions;
private final SessionFactoryImplementor sessionFactory;
public InsertExecutionDelegate(
SqmInsertStatement<?> sqmInsert,
MultiTableSqmMutationConverter sqmConverter,
TemporaryTable entityTable,
Function<SharedSessionContractImplementor, String> sessionUidAccess,
DomainParameterXref domainParameterXref,
TableGroup insertingTableGroup,
Map<String, TableReference> tableReferenceByAlias,
List<Assignment> assignments,
InsertStatement insertStatement,
Map<SqmParameter, List<List<JdbcParameter>>> parameterResolutions,
Map<SqmParameter, MappingModelExpressable> paramTypeResolutions,
DomainQueryExecutionContext executionContext) {
this.sqmInsert = sqmInsert;
this.sqmConverter = sqmConverter;
this.entityTable = entityTable;
this.sessionUidAccess = sessionUidAccess;
this.domainParameterXref = domainParameterXref;
this.updatingTableGroup = insertingTableGroup;
this.paramTypeResolutions = paramTypeResolutions;
this.insertStatement = insertStatement;
this.sessionFactory = executionContext.getSession().getFactory();
final ModelPartContainer updatingModelPart = insertingTableGroup.getModelPart();
assert updatingModelPart instanceof EntityMappingType;
this.entityDescriptor = (EntityMappingType) updatingModelPart;
this.assignmentsByTable = CollectionHelper.mapOfSize( insertingTableGroup.getTableReferenceJoins().size() + 1 );
jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(
executionContext.getQueryParameterBindings(),
domainParameterXref,
SqmUtil.generateJdbcParamsXref(
domainParameterXref,
() -> parameterResolutions
),
sessionFactory.getDomainModel(),
navigablePath -> insertingTableGroup,
paramTypeResolutions::get,
executionContext.getSession()
);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// segment the assignments by table-reference
for ( int i = 0; i < assignments.size(); i++ ) {
final Assignment assignment = assignments.get( i );
final Assignable assignable = assignment.getAssignable();
final List<ColumnReference> assignmentColumnRefs = assignable.getColumnReferences();
TableReference assignmentTableReference = null;
for ( int c = 0; c < assignmentColumnRefs.size(); c++ ) {
final ColumnReference columnReference = assignmentColumnRefs.get( c );
final TableReference tableReference = resolveTableReference(
columnReference,
insertingTableGroup,
tableReferenceByAlias
);
if ( assignmentTableReference != null && assignmentTableReference != tableReference ) {
throw new SemanticException( "Assignment referred to columns from multiple tables: " + i );
}
assignmentTableReference = tableReference;
}
assignmentsByTable.computeIfAbsent( assignmentTableReference, k -> new ArrayList<>() )
.add( assignment );
}
}
@Override
public int execute(ExecutionContext executionContext) {
ExecuteWithTemporaryTableHelper.performBeforeTemporaryTableUseActions(
entityTable,
executionContext
);
try {
final int rows = ExecuteWithTemporaryTableHelper.saveIntoTemporaryTable(
insertStatement,
jdbcParameterBindings,
executionContext
);
if ( rows != 0 ) {
final AbstractEntityPersister persister = (AbstractEntityPersister) entityDescriptor.getEntityPersister();
final int tableSpan = persister.getTableSpan();
insertRootTable( persister.getTableName( 0 ), rows, persister.getKeyColumns( 0 ), executionContext );
for ( int i = 1; i < tableSpan; i++ ) {
insertTable(
persister.getTableName( i ),
persister.getKeyColumns( i ),
persister.isNullableTable( i ),
executionContext
);
}
}
return rows;
}
finally {
ExecuteWithTemporaryTableHelper.performAfterTemporaryTableUseActions(
entityTable,
sessionUidAccess,
executionContext
);
}
}
private TableReference resolveTableReference(
ColumnReference columnReference,
TableGroup updatingTableGroup,
Map<String, TableReference> tableReferenceByAlias) {
if ( columnReference.getQualifier() == null ) {
// This happens only for the special row_number column
return null;
}
final TableReference tableReferenceByQualifier = tableReferenceByAlias.get( columnReference.getQualifier() );
if ( tableReferenceByQualifier != null ) {
return tableReferenceByQualifier;
}
throw new SemanticException( "Assignment referred to column of a joined association: " + columnReference );
}
private TableReference resolveUnionTableReference(TableReference tableReference, String tableExpression) {
if ( tableReference instanceof UnionTableReference ) {
return new TableReference(
tableExpression,
tableReference.getIdentificationVariable(),
tableReference.isOptional(),
sessionFactory
);
}
else {
return tableReference;
}
}
private void insertRootTable(
String tableExpression,
int rows,
String[] keyColumns,
ExecutionContext executionContext) {
final TableReference updatingTableReference = updatingTableGroup.getTableReference(
updatingTableGroup.getNavigablePath(),
tableExpression,
true,
true
);
final IdentifierGenerator identifierGenerator = entityDescriptor.getEntityPersister().getIdentifierGenerator();
final List<Assignment> assignments = assignmentsByTable.get( updatingTableReference );
if ( ( assignments == null || assignments.isEmpty() ) && !( identifierGenerator instanceof PostInsertIdentifierGenerator ) ) {
throw new IllegalStateException( "There must be at least a single root table assignment" );
}
final TableReference dmlTableReference = resolveUnionTableReference( updatingTableReference, tableExpression );
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Create the SQL AST and convert it into a JdbcOperation
final QuerySpec querySpec = new QuerySpec( true );
final TableGroupImpl temporaryTableGroup = new TableGroupImpl(
updatingTableGroup.getNavigablePath(),
null,
new TableReference(
insertStatement.getTargetTable().getTableExpression(),
updatingTableReference.getIdentificationVariable(),
false,
sessionFactory
),
entityDescriptor,
null
);
querySpec.getFromClause().addRoot( temporaryTableGroup );
final InsertStatement insertStatement = new InsertStatement( dmlTableReference );
insertStatement.setSourceSelectStatement( querySpec );
for ( Assignment assignment : assignments ) {
insertStatement.addTargetColumnReferences( assignment.getAssignable().getColumnReferences() );
for ( ColumnReference columnReference : assignment.getAssignable().getColumnReferences() ) {
querySpec.getSelectClause().addSqlSelection(
new SqlSelectionImpl(
1,
0,
new ColumnReference(
updatingTableReference.getIdentificationVariable(),
columnReference.getColumnExpression(),
false,
null,
null,
columnReference.getJdbcMapping(),
sessionFactory
)
)
);
}
}
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
final Map<Object, Object> entityTableToRootIdentity;
if ( identifierGenerator instanceof PostInsertIdentifierGenerator ) {
final BasicEntityIdentifierMapping identifierMapping = (BasicEntityIdentifierMapping) entityDescriptor.getIdentifierMapping();
final QuerySpec idSelectQuerySpec = new QuerySpec( true );
idSelectQuerySpec.getFromClause().addRoot( temporaryTableGroup );
final ColumnReference columnReference = new ColumnReference(
(String) null,
TemporaryTable.ENTITY_TABLE_IDENTITY_COLUMN,
false,
null,
null,
identifierMapping.getJdbcMapping(),
sessionFactory
);
idSelectQuerySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( 1, 0, columnReference ) );
idSelectQuerySpec.addSortSpecification(
new SortSpecification(
columnReference,
null,
SortOrder.ASCENDING
)
);
final SelectStatement selectStatement = new SelectStatement(
idSelectQuerySpec,
Collections.singletonList(
new BasicFetch<>(
0,
null,
null,
identifierMapping,
null,
FetchTiming.IMMEDIATE,
null
)
)
);
final JdbcSelect jdbcSelect = jdbcServices.getJdbcEnvironment()
.getSqlAstTranslatorFactory()
.buildSelectTranslator( sessionFactory, selectStatement )
.translate( null, executionContext.getQueryOptions() );
final List<Object> list = jdbcServices.getJdbcSelectExecutor().list(
jdbcSelect,
JdbcParameterBindings.NO_BINDINGS,
executionContext,
null,
ListResultsConsumer.UniqueSemantic.NONE
);
entityTableToRootIdentity = new LinkedHashMap<>( list.size() );
for ( Object o : list ) {
entityTableToRootIdentity.put( o, null );
}
querySpec.applyPredicate(
new ComparisonPredicate(
columnReference,
ComparisonOperator.EQUAL,
new JdbcParameterImpl( identifierMapping.getJdbcMapping() )
)
);
}
else {
entityTableToRootIdentity = null;
if ( identifierGenerator instanceof OptimizableGenerator ) {
final Optimizer optimizer = ( (OptimizableGenerator) identifierGenerator ).getOptimizer();
// If the generator uses an optimizer, we have to generate the identifiers for the new rows
if ( optimizer != null && optimizer.getIncrementSize() > 1 ) {
final BasicEntityIdentifierMapping identifierMapping = (BasicEntityIdentifierMapping) entityDescriptor.getIdentifierMapping();
final JdbcParameter rowNumber = new JdbcParameterImpl( identifierMapping.getJdbcMapping() );
final JdbcParameter rootIdentity = new JdbcParameterImpl( identifierMapping.getJdbcMapping() );
final List<Assignment> temporaryTableAssignments = new ArrayList<>( 1 );
final ColumnReference idColumnReference = new ColumnReference(
(String) null,
identifierMapping,
sessionFactory
);
temporaryTableAssignments.add(
new Assignment(
idColumnReference,
rootIdentity
)
);
final TemporaryTableColumn rowNumberColumn = entityTable.getColumns().get(
entityTable.getColumns().size() - 1
);
final UpdateStatement updateStatement = new UpdateStatement(
temporaryTableGroup.getPrimaryTableReference(),
temporaryTableAssignments,
new ComparisonPredicate(
new ColumnReference(
(String) null,
rowNumberColumn.getColumnName(),
false,
null,
null,
rowNumberColumn.getJdbcMapping(),
sessionFactory
),
ComparisonOperator.EQUAL,
rowNumber
)
);
final JdbcUpdate jdbcUpdate = jdbcServices.getJdbcEnvironment()
.getSqlAstTranslatorFactory()
.buildUpdateTranslator( sessionFactory, updateStatement )
.translate( null, executionContext.getQueryOptions() );
final JdbcParameterBindings updateBindings = new JdbcParameterBindingsImpl( 2 );
for ( int i = 0; i < rows; i++ ) {
updateBindings.addBinding(
rowNumber,
new JdbcParameterBindingImpl(
rowNumberColumn.getJdbcMapping(),
i + 1
)
);
updateBindings.addBinding(
rootIdentity,
new JdbcParameterBindingImpl(
identifierMapping.getJdbcMapping(),
identifierGenerator.generate(
executionContext.getSession(),
null
)
)
);
jdbcServices.getJdbcMutationExecutor().execute(
jdbcUpdate,
updateBindings,
sql -> executionContext.getSession()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sql ),
(integer, preparedStatement) -> {
},
executionContext
);
}
// Finally, we also have to add the id to the insertion targets if it's not already there
if ( insertStatement.getTargetColumnReferences()
.stream()
.noneMatch( c -> keyColumns[0].equals( c.getColumnExpression() ) ) ) {
insertStatement.addTargetColumnReferences(
new ColumnReference(
(String) null,
keyColumns[0],
false,
null,
null,
identifierMapping.getJdbcMapping(),
sessionFactory
)
);
querySpec.getSelectClause().addSqlSelection(
new SqlSelectionImpl(
1,
0,
new ColumnReference(
updatingTableReference.getIdentificationVariable(),
idColumnReference.getColumnExpression(),
false,
null,
null,
idColumnReference.getJdbcMapping(),
sessionFactory
)
)
);
}
}
}
}
final JdbcInsert jdbcInsert = jdbcServices.getJdbcEnvironment()
.getSqlAstTranslatorFactory()
.buildInsertTranslator( sessionFactory, insertStatement )
.translate( null, executionContext.getQueryOptions() );
if ( identifierGenerator instanceof PostInsertIdentifierGenerator ) {
final PostInsertIdentifierGenerator generator = (PostInsertIdentifierGenerator) identifierGenerator;
final boolean generatedKeysEnabled = sessionFactory.getSessionFactoryOptions().isGetGeneratedKeysEnabled();
final InsertGeneratedIdentifierDelegate identifierDelegate = generator.getInsertGeneratedIdentifierDelegate(
(PostInsertIdentityPersister) entityDescriptor.getEntityPersister(),
jdbcServices.getDialect(),
generatedKeysEnabled
);
final String finalSql = identifierDelegate.prepareIdentifierGeneratingInsert( jdbcInsert.getSql() );
final BasicEntityIdentifierMapping identifierMapping = (BasicEntityIdentifierMapping) entityDescriptor.getIdentifierMapping();
final ValueBinder jdbcValueBinder = identifierMapping.getJdbcMapping().getJdbcValueBinder();
for ( Map.Entry<Object, Object> entry : entityTableToRootIdentity.entrySet() ) {
final Object rootIdentity = identifierDelegate.performInsert(
finalSql,
executionContext.getSession(),
new Binder() {
@Override
public void bindValues(PreparedStatement ps) throws SQLException {
jdbcValueBinder.bind( ps, entry.getKey(), 1, executionContext.getSession() );
}
@Override
public Object getEntity() {
return null;
}
}
);
entry.setValue( rootIdentity );
}
final JdbcParameter entityIdentity = new JdbcParameterImpl( identifierMapping.getJdbcMapping() );
final JdbcParameter rootIdentity = new JdbcParameterImpl( identifierMapping.getJdbcMapping() );
final List<Assignment> temporaryTableAssignments = new ArrayList<>( 1 );
temporaryTableAssignments.add(
new Assignment(
new ColumnReference(
(String) null,
identifierMapping,
sessionFactory
),
rootIdentity
)
);
final UpdateStatement updateStatement = new UpdateStatement(
temporaryTableGroup.getPrimaryTableReference(),
temporaryTableAssignments,
new ComparisonPredicate(
new ColumnReference(
(String) null,
TemporaryTable.ENTITY_TABLE_IDENTITY_COLUMN,
false,
null,
null,
identifierMapping.getJdbcMapping(),
sessionFactory
),
ComparisonOperator.EQUAL,
entityIdentity
)
);
final JdbcUpdate jdbcUpdate = jdbcServices.getJdbcEnvironment()
.getSqlAstTranslatorFactory()
.buildUpdateTranslator( sessionFactory, updateStatement )
.translate( null, executionContext.getQueryOptions() );
final JdbcParameterBindings updateBindings = new JdbcParameterBindingsImpl( 2 );
for ( Map.Entry<Object, Object> entry : entityTableToRootIdentity.entrySet() ) {
updateBindings.addBinding( entityIdentity, new JdbcParameterBindingImpl( identifierMapping.getJdbcMapping(), entry.getKey() ) );
updateBindings.addBinding( rootIdentity, new JdbcParameterBindingImpl( identifierMapping.getJdbcMapping(), entry.getValue() ) );
jdbcServices.getJdbcMutationExecutor().execute(
jdbcUpdate,
updateBindings,
sql -> executionContext.getSession()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sql ),
(integer, preparedStatement) -> {
},
executionContext
);
}
}
else {
jdbcServices.getJdbcMutationExecutor().execute(
jdbcInsert,
JdbcParameterBindings.NO_BINDINGS,
sql -> executionContext.getSession()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sql ),
(integer, preparedStatement) -> {
},
executionContext
);
}
}
private void insertTable(
String tableExpression,
String[] keyColumns,
boolean nullableTable,
ExecutionContext executionContext) {
final TableReference updatingTableReference = updatingTableGroup.getTableReference(
updatingTableGroup.getNavigablePath(),
tableExpression,
true,
true
);
final List<Assignment> assignments = assignmentsByTable.get( updatingTableReference );
if ( nullableTable && ( assignments == null || assignments.isEmpty() ) ) {
// no assignments for this table - skip it
return;
}
final TableReference dmlTargetTableReference = resolveUnionTableReference( updatingTableReference, tableExpression );
final QuerySpec querySpec = new QuerySpec( true );
final TableGroupImpl temporaryTableGroup = new TableGroupImpl(
updatingTableGroup.getNavigablePath(),
null,
new TableReference(
insertStatement.getTargetTable().getTableExpression(),
updatingTableReference.getIdentificationVariable(),
false,
sessionFactory
),
entityDescriptor,
null
);
querySpec.getFromClause().addRoot( temporaryTableGroup );
final InsertStatement insertStatement = new InsertStatement( dmlTargetTableReference );
insertStatement.setSourceSelectStatement( querySpec );
if ( assignments != null && !assignments.isEmpty() ) {
for ( Assignment assignment : assignments ) {
insertStatement.addTargetColumnReferences( assignment.getAssignable().getColumnReferences() );
for ( ColumnReference columnReference : assignment.getAssignable().getColumnReferences() ) {
querySpec.getSelectClause().addSqlSelection(
new SqlSelectionImpl(
1,
0,
new ColumnReference(
updatingTableReference.getIdentificationVariable(),
columnReference.getColumnExpression(),
false,
null,
null,
columnReference.getJdbcMapping(),
sessionFactory
)
)
);
}
}
}
final String targetKeyColumnName = keyColumns[0];
final AbstractEntityPersister entityPersister = (AbstractEntityPersister) entityDescriptor.getEntityPersister();
final IdentifierGenerator identifierGenerator = entityPersister.getIdentifierGenerator();
final boolean needsKeyInsert;
if ( identifierGenerator instanceof PostInsertIdentifierGenerator ) {
needsKeyInsert = true;
}
else if ( identifierGenerator instanceof OptimizableGenerator ) {
final Optimizer optimizer = ( (OptimizableGenerator) identifierGenerator ).getOptimizer();
// If the generator uses an optimizer, we have to generate the identifiers for the new rows
needsKeyInsert = optimizer != null && optimizer.getIncrementSize() > 1;
}
else {
needsKeyInsert = false;
}
if ( needsKeyInsert && insertStatement.getTargetColumnReferences()
.stream()
.noneMatch( c -> targetKeyColumnName.equals( c.getColumnExpression() ) ) ) {
final BasicEntityIdentifierMapping identifierMapping = (BasicEntityIdentifierMapping) entityDescriptor.getIdentifierMapping();
insertStatement.addTargetColumnReferences(
new ColumnReference(
dmlTargetTableReference.getIdentificationVariable(),
targetKeyColumnName,
false,
null,
null,
identifierMapping.getJdbcMapping(),
sessionFactory
)
);
querySpec.getSelectClause().addSqlSelection(
new SqlSelectionImpl(
1,
0,
new ColumnReference(
updatingTableReference.getIdentificationVariable(),
identifierMapping,
sessionFactory
)
)
);
}
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
final JdbcInsert jdbcInsert = jdbcServices.getJdbcEnvironment()
.getSqlAstTranslatorFactory()
.buildInsertTranslator( sessionFactory, insertStatement )
.translate( null, executionContext.getQueryOptions() );
jdbcServices.getJdbcMutationExecutor().execute(
jdbcInsert,
JdbcParameterBindings.NO_BINDINGS,
sql -> executionContext.getSession()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sql ),
(integer, preparedStatement) -> {
},
executionContext
);
}
}

View File

@ -0,0 +1,44 @@
/*
* 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.query.sqm.mutation.internal.temptable;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.spi.DomainQueryExecutionContext;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.tree.insert.SqmInsertStatement;
/**
* Strategy based on ANSI SQL's definition of a "local temporary table" (local to each db session).
*
* @author Steve Ebersole
*/
public class LocalTemporaryTableInsertStrategy extends LocalTemporaryTableStrategy implements SqmMultiTableInsertStrategy {
public LocalTemporaryTableInsertStrategy(
TemporaryTable entityTable,
SessionFactoryImplementor sessionFactory) {
super( entityTable, sessionFactory );
}
@Override
public int executeInsert(
SqmInsertStatement<?> sqmInsertStatement,
DomainParameterXref domainParameterXref,
DomainQueryExecutionContext context) {
return new TableBasedInsertHandler(
sqmInsertStatement,
domainParameterXref,
getTemporaryTable(),
session -> {
throw new UnsupportedOperationException( "Unexpected call to access Session uid" );
},
getSessionFactory()
).execute( context );
}
}

View File

@ -0,0 +1,62 @@
/*
* 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.query.sqm.mutation.internal.temptable;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.spi.DomainQueryExecutionContext;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
/**
* Strategy based on ANSI SQL's definition of a "local temporary table" (local to each db session).
*
* @author Steve Ebersole
*/
public class LocalTemporaryTableMutationStrategy extends LocalTemporaryTableStrategy implements SqmMultiTableMutationStrategy {
public LocalTemporaryTableMutationStrategy(
TemporaryTable idTable,
SessionFactoryImplementor sessionFactory) {
super( idTable, sessionFactory );
}
@Override
public int executeUpdate(
SqmUpdateStatement<?> sqmUpdate,
DomainParameterXref domainParameterXref,
DomainQueryExecutionContext context) {
return new TableBasedUpdateHandler(
sqmUpdate,
domainParameterXref,
getTemporaryTable(),
session -> {
throw new UnsupportedOperationException( "Unexpected call to access Session uid" );
},
getSessionFactory()
).execute( context );
}
@Override
public int executeDelete(
SqmDeleteStatement<?> sqmDelete,
DomainParameterXref domainParameterXref,
DomainQueryExecutionContext context) {
return new TableBasedDeleteHandler(
sqmDelete,
domainParameterXref,
getTemporaryTable(),
session -> {
throw new UnsupportedOperationException( "Unexpected call to access Session uid" );
},
getSessionFactory()
).execute( context );
}
}

View File

@ -0,0 +1,107 @@
/*
* 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.query.sqm.mutation.internal.temptable;
import java.sql.Connection;
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;
import org.jboss.logging.Logger;
/**
* Strategy based on ANSI SQL's definition of a "local temporary table" (local to each db session).
*
* @author Steve Ebersole
*/
public class LocalTemporaryTableStrategy {
private static final Logger log = Logger.getLogger( LocalTemporaryTableStrategy.class );
public static final String SHORT_NAME = "local_temporary";
public static final String DROP_ID_TABLES = "hibernate.hql.bulk_id_strategy.local_temporary.drop_tables";
private final TemporaryTable temporaryTable;
private final SessionFactoryImplementor sessionFactory;
private boolean dropIdTables;
public LocalTemporaryTableStrategy(
TemporaryTable temporaryTable,
SessionFactoryImplementor sessionFactory) {
this.temporaryTable = temporaryTable;
this.sessionFactory = sessionFactory;
}
public void prepare(
MappingModelCreationProcess mappingModelCreationProcess,
JdbcConnectionAccess connectionAccess) {
final ConfigurationService configService = mappingModelCreationProcess.getCreationContext()
.getBootstrapContext()
.getServiceRegistry().getService( ConfigurationService.class );
this.dropIdTables = configService.getSetting(
DROP_ID_TABLES,
StandardConverters.BOOLEAN,
false
);
}
public void release(
SessionFactoryImplementor sessionFactory,
JdbcConnectionAccess connectionAccess) {
if ( !dropIdTables ) {
return;
}
dropIdTables = false;
final TemporaryTable temporaryTable = getTemporaryTable();
log.debugf( "Dropping local-temp ID table : %s", temporaryTable.getTableExpression() );
final TemporaryTableHelper.TemporaryTableDropWork temporaryTableDropWork = new TemporaryTableHelper.TemporaryTableDropWork(
temporaryTable,
sessionFactory
);
Connection connection;
try {
connection = connectionAccess.obtainConnection();
}
catch (UnsupportedOperationException e) {
// assume this comes from org.hibernate.engine.jdbc.connections.internal.UserSuppliedConnectionProviderImpl
log.debugf( "Unable to obtain JDBC connection; unable to drop local-temp ID table : %s", temporaryTable.getTableExpression() );
return;
}
catch (SQLException e) {
log.error( "Unable obtain JDBC Connection", e );
return;
}
try {
temporaryTableDropWork.execute( connection );
}
finally {
try {
connectionAccess.releaseConnection( connection );
}
catch (SQLException ignore) {
}
}
}
public TemporaryTable getTemporaryTable() {
return temporaryTable;
}
public SessionFactoryImplementor getSessionFactory() {
return sessionFactory;
}
}

View File

@ -0,0 +1,51 @@
/*
* 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.query.sqm.mutation.internal.temptable;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.spi.DomainQueryExecutionContext;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.tree.insert.SqmInsertStatement;
/**
* This is a strategy that mimics temporary tables for databases which do not support
* temporary tables. It follows a pattern similar to the ANSI SQL definition of global
* temporary table using a "session id" column to segment rows from the various sessions.
*
* @author Steve Ebersole
*/
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) {
super( entityTable, sessionFactory );
}
@Override
public int executeInsert(
SqmInsertStatement<?> sqmInsertStatement,
DomainParameterXref domainParameterXref,
DomainQueryExecutionContext context) {
return new TableBasedInsertHandler(
sqmInsertStatement,
domainParameterXref,
getTemporaryTable(),
session -> session.getSessionIdentifier().toString(),
getSessionFactory()
).execute( context );
}
}

View File

@ -0,0 +1,59 @@
/*
* 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.query.sqm.mutation.internal.temptable;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.spi.DomainQueryExecutionContext;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
/**
* This is a strategy that mimics temporary tables for databases which do not support
* temporary tables. It follows a pattern similar to the ANSI SQL definition of global
* temporary table using a "session id" column to segment rows from the various sessions.
*
* @author Steve Ebersole
*/
public class PersistentTableMutationStrategy extends PersistentTableStrategy implements SqmMultiTableMutationStrategy {
public PersistentTableMutationStrategy(
TemporaryTable idTable,
SessionFactoryImplementor sessionFactory) {
super( idTable, sessionFactory );
}
@Override
public int executeUpdate(
SqmUpdateStatement<?> sqmUpdate,
DomainParameterXref domainParameterXref,
DomainQueryExecutionContext context) {
return new TableBasedUpdateHandler(
sqmUpdate,
domainParameterXref,
getTemporaryTable(),
session -> session.getSessionIdentifier().toString(),
getSessionFactory()
).execute( context );
}
@Override
public int executeDelete(
SqmDeleteStatement<?> sqmDelete,
DomainParameterXref domainParameterXref,
DomainQueryExecutionContext context) {
return new TableBasedDeleteHandler(
sqmDelete,
domainParameterXref,
getTemporaryTable(),
session -> session.getSessionIdentifier().toString(),
getSessionFactory()
).execute( context );
}
}

View File

@ -4,21 +4,16 @@
* 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.query.sqm.mutation.internal.idtable;
package org.hibernate.query.sqm.mutation.internal.temptable;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.function.Supplier;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.dialect.temptable.TemporaryTableHelper;
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.query.spi.DomainQueryExecutionContext;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
import org.jboss.logging.Logger;
@ -29,7 +24,7 @@ import org.jboss.logging.Logger;
*
* @author Steve Ebersole
*/
public class PersistentTableStrategy implements SqmMultiTableMutationStrategy {
public abstract class PersistentTableStrategy {
private static final Logger log = Logger.getLogger( PersistentTableStrategy.class );
public static final String SHORT_NAME = "persistent";
@ -39,9 +34,7 @@ public class PersistentTableStrategy implements SqmMultiTableMutationStrategy {
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 IdTable idTable;
private final AfterUseAction afterUseAction;
private final Supplier<IdTableExporter> idTableExporterAccess;
private final TemporaryTable temporaryTable;
private final SessionFactoryImplementor sessionFactory;
@ -50,57 +43,16 @@ public class PersistentTableStrategy implements SqmMultiTableMutationStrategy {
private boolean released;
public PersistentTableStrategy(
IdTable idTable,
AfterUseAction afterUseAction,
Supplier<IdTableExporter> idTableExporterAccess,
TemporaryTable temporaryTable,
SessionFactoryImplementor sessionFactory) {
this.idTable = idTable;
this.afterUseAction = afterUseAction;
this.idTableExporterAccess = idTableExporterAccess;
this.temporaryTable = temporaryTable;
this.sessionFactory = sessionFactory;
if ( afterUseAction == AfterUseAction.DROP ) {
throw new IllegalArgumentException( "Persistent ID tables cannot use AfterUseAction.DROP : " + idTable.getTableExpression() );
if ( sessionFactory.getJdbcServices().getDialect().getTemporaryTableAfterUseAction() == AfterUseAction.DROP ) {
throw new IllegalArgumentException( "Persistent ID tables cannot use AfterUseAction.DROP : " + temporaryTable.getTableExpression() );
}
}
@Override
public int executeUpdate(
SqmUpdateStatement sqmUpdate,
DomainParameterXref domainParameterXref,
DomainQueryExecutionContext context) {
return new TableBasedUpdateHandler(
sqmUpdate,
domainParameterXref,
idTable,
session -> session.getSessionIdentifier().toString(),
idTableExporterAccess,
BeforeUseAction.CREATE,
afterUseAction,
TempTableDdlTransactionHandling.NONE,
sessionFactory
).execute( context );
}
@Override
public int executeDelete(
SqmDeleteStatement sqmDelete,
DomainParameterXref domainParameterXref,
DomainQueryExecutionContext context) {
return new TableBasedDeleteHandler(
sqmDelete,
domainParameterXref,
idTable,
session -> session.getSessionIdentifier().toString(),
idTableExporterAccess,
BeforeUseAction.CREATE,
afterUseAction,
TempTableDdlTransactionHandling.NONE,
sessionFactory
).execute( context );
}
@Override
public void prepare(
MappingModelCreationProcess mappingModelCreationProcess,
JdbcConnectionAccess connectionAccess) {
@ -110,11 +62,10 @@ public class PersistentTableStrategy implements SqmMultiTableMutationStrategy {
prepared = true;
log.debugf( "Creating persistent ID table : %s", idTable.getTableExpression() );
log.debugf( "Creating persistent ID table : %s", getTemporaryTable().getTableExpression() );
final IdTableHelper.IdTableCreationWork idTableCreationWork = new IdTableHelper.IdTableCreationWork(
idTable,
idTableExporterAccess.get(),
final TemporaryTableHelper.TemporaryTableCreationWork temporaryTableCreationWork = new TemporaryTableHelper.TemporaryTableCreationWork(
getTemporaryTable(),
sessionFactory
);
Connection connection;
@ -132,7 +83,7 @@ public class PersistentTableStrategy implements SqmMultiTableMutationStrategy {
}
try {
idTableCreationWork.execute( connection );
temporaryTableCreationWork.execute( connection );
created = true;
}
finally {
@ -148,7 +99,6 @@ public class PersistentTableStrategy implements SqmMultiTableMutationStrategy {
}
}
@Override
public void release(
SessionFactoryImplementor sessionFactory,
JdbcConnectionAccess connectionAccess) {
@ -162,12 +112,11 @@ public class PersistentTableStrategy implements SqmMultiTableMutationStrategy {
return;
}
final TemporaryTable temporaryTable = getTemporaryTable();
log.debugf( "Dropping persistent ID table : %s", temporaryTable.getTableExpression() );
log.debugf( "Dropping persistent ID table : %s", idTable.getTableExpression() );
final IdTableHelper.IdTableDropWork idTableDropWork = new IdTableHelper.IdTableDropWork(
idTable,
idTableExporterAccess.get(),
final TemporaryTableHelper.TemporaryTableDropWork temporaryTableDropWork = new TemporaryTableHelper.TemporaryTableDropWork(
temporaryTable,
sessionFactory
);
Connection connection;
@ -176,7 +125,7 @@ public class PersistentTableStrategy implements SqmMultiTableMutationStrategy {
}
catch (UnsupportedOperationException e) {
// assume this comes from org.hibernate.engine.jdbc.connections.internal.UserSuppliedConnectionProviderImpl
log.debugf( "Unable to obtain JDBC connection; unable to drop persistent ID table : %s", idTable.getTableExpression() );
log.debugf( "Unable to obtain JDBC connection; unable to drop persistent ID table : %s", temporaryTable.getTableExpression() );
return;
}
catch (SQLException e) {
@ -185,7 +134,7 @@ public class PersistentTableStrategy implements SqmMultiTableMutationStrategy {
}
try {
idTableDropWork.execute( connection );
temporaryTableDropWork.execute( connection );
}
finally {
try {
@ -195,4 +144,12 @@ public class PersistentTableStrategy implements SqmMultiTableMutationStrategy {
}
}
}
public TemporaryTable getTemporaryTable() {
return temporaryTable;
}
public SessionFactoryImplementor getSessionFactory() {
return sessionFactory;
}
}

View File

@ -4,7 +4,7 @@
* 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.query.sqm.mutation.internal.idtable;
package org.hibernate.query.sqm.mutation.internal.temptable;
import java.util.ArrayList;
import java.util.Collections;
@ -16,7 +16,7 @@ import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -69,29 +69,20 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
private static final Logger log = Logger.getLogger( RestrictedDeleteExecutionDelegate.class );
private final EntityMappingType entityDescriptor;
private final IdTable idTable;
private final TemporaryTable idTable;
private final SqmDeleteStatement<?> sqmDelete;
private final DomainParameterXref domainParameterXref;
private final SessionFactoryImplementor sessionFactory;
private final BeforeUseAction beforeUseAction;
private final AfterUseAction afterUseAction;
private final TempTableDdlTransactionHandling ddlTransactionHandling;
private final Supplier<IdTableExporter> idTableExporterAccess;
private final Function<SharedSessionContractImplementor,String> sessionUidAccess;
private final MultiTableSqmMutationConverter converter;
@SuppressWarnings("WeakerAccess")
public RestrictedDeleteExecutionDelegate(
EntityMappingType entityDescriptor,
IdTable idTable,
TemporaryTable idTable,
SqmDeleteStatement<?> sqmDelete,
DomainParameterXref domainParameterXref,
BeforeUseAction beforeUseAction,
AfterUseAction afterUseAction,
TempTableDdlTransactionHandling ddlTransactionHandling,
Supplier<IdTableExporter> idTableExporterAccess,
Function<SharedSessionContractImplementor, String> sessionUidAccess,
QueryOptions queryOptions,
LoadQueryInfluencers loadQueryInfluencers,
@ -101,10 +92,6 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
this.idTable = idTable;
this.sqmDelete = sqmDelete;
this.domainParameterXref = domainParameterXref;
this.beforeUseAction = beforeUseAction;
this.afterUseAction = afterUseAction;
this.ddlTransactionHandling = ddlTransactionHandling;
this.idTableExporterAccess = idTableExporterAccess;
this.sessionUidAccess = sessionUidAccess;
this.sessionFactory = sessionFactory;
this.converter = new MultiTableSqmMutationConverter(
@ -489,11 +476,8 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
executionContext.getSession()
);
ExecuteWithIdTableHelper.performBeforeIdTableUseActions(
beforeUseAction,
ExecuteWithTemporaryTableHelper.performBeforeTemporaryTableUseActions(
idTable,
idTableExporterAccess,
ddlTransactionHandling,
executionContext
);
@ -501,11 +485,8 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
return executeUsingIdTable( predicate, executionContext, jdbcParameterBindings );
}
finally {
ExecuteWithIdTableHelper.performAfterIdTableUseActions(
afterUseAction,
ExecuteWithTemporaryTableHelper.performAfterTemporaryTableUseActions(
idTable,
idTableExporterAccess,
ddlTransactionHandling,
sessionUidAccess,
executionContext
);
@ -516,7 +497,7 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
Predicate predicate,
ExecutionContext executionContext,
JdbcParameterBindings jdbcParameterBindings) {
final int rows = ExecuteWithIdTableHelper.saveMatchingIdsIntoIdTable(
final int rows = ExecuteWithTemporaryTableHelper.saveMatchingIdsIntoIdTable(
converter,
predicate,
idTable,
@ -525,7 +506,7 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
executionContext
);
final QuerySpec idTableIdentifierSubQuery = ExecuteWithIdTableHelper.createIdTableSelectQuerySpec(
final QuerySpec idTableIdentifierSubQuery = ExecuteWithTemporaryTableHelper.createIdTableSelectQuerySpec(
idTable,
sessionUidAccess,
entityDescriptor,
@ -541,7 +522,7 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
idTableFkSubQuery = idTableIdentifierSubQuery;
}
else {
idTableFkSubQuery = ExecuteWithIdTableHelper.createIdTableSelectQuerySpec(
idTableFkSubQuery = ExecuteWithTemporaryTableHelper.createIdTableSelectQuerySpec(
idTable,
fkDescriptor.getTargetPart(),
sessionUidAccess,

View File

@ -4,12 +4,11 @@
* 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.query.sqm.mutation.internal.idtable;
package org.hibernate.query.sqm.mutation.internal.temptable;
import java.util.function.Function;
import java.util.function.Supplier;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.query.spi.DomainQueryExecutionContext;
@ -32,37 +31,24 @@ public class TableBasedDeleteHandler
int execute(DomainQueryExecutionContext executionContext);
}
private final IdTable idTable;
private final TempTableDdlTransactionHandling ddlTransactionHandling;
private final BeforeUseAction beforeUseAction;
private final AfterUseAction afterUseAction;
private final TemporaryTable idTable;
private final Function<SharedSessionContractImplementor,String> sessionUidAccess;
private final Supplier<IdTableExporter> exporterSupplier;
private final DomainParameterXref domainParameterXref;
public TableBasedDeleteHandler(
SqmDeleteStatement sqmDeleteStatement,
SqmDeleteStatement<?> sqmDeleteStatement,
DomainParameterXref domainParameterXref,
IdTable idTable,
Function<SharedSessionContractImplementor,
String> sessionUidAccess,
Supplier<IdTableExporter> exporterSupplier,
BeforeUseAction beforeUseAction,
AfterUseAction afterUseAction,
TempTableDdlTransactionHandling ddlTransactionHandling,
TemporaryTable idTable,
Function<SharedSessionContractImplementor, String> sessionUidAccess,
SessionFactoryImplementor sessionFactory) {
super( sqmDeleteStatement, sessionFactory );
this.idTable = idTable;
this.ddlTransactionHandling = ddlTransactionHandling;
this.beforeUseAction = beforeUseAction;
this.afterUseAction = afterUseAction;
this.domainParameterXref = domainParameterXref;
this.sessionUidAccess = sessionUidAccess;
this.exporterSupplier = exporterSupplier;
}
@Override
@ -82,10 +68,6 @@ public class TableBasedDeleteHandler
idTable,
getSqmDeleteOrUpdateStatement(),
domainParameterXref,
beforeUseAction,
afterUseAction,
ddlTransactionHandling,
exporterSupplier,
sessionUidAccess,
executionContext.getQueryOptions(),
executionContext.getSession().getLoadQueryInfluencers(),

Some files were not shown because too many files have changed in this diff Show More