HHH-13715 - working support for "multi-table" HQL/Criteria UPDATE and DELETE queries;

local-temp-table support works tested on H2.  I believe cte support works as well - need to set up and environment with a database supporting it.

There is a problem saving entities with secondary tables atm so for now i cannot create the data to assert that the correct rows were deleted.  But looking at the executed SQL it works
This commit is contained in:
Steve Ebersole 2019-11-11 16:09:11 -06:00
parent 186f4b37c0
commit ba5ef1b149
101 changed files with 3455 additions and 295 deletions

View File

@ -19,5 +19,5 @@ public interface Exportable {
*
* @return The exporting identifier.
*/
public String getExportIdentifier();
String getExportIdentifier();
}

View File

@ -15,7 +15,7 @@ import java.util.Map;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.CharIndexFunction;
import org.hibernate.dialect.function.NoArgSQLFunction;
@ -24,7 +24,10 @@ import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.dialect.function.VarArgsSQLFunction;
import org.hibernate.dialect.identity.AbstractTransactSQLIdentityColumnSupport;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.metamodel.mapping.EntityMappingType;
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.spi.SqmMultiTableMutationStrategy;
import org.hibernate.type.StandardBasicTypes;
@ -211,20 +214,13 @@ abstract class AbstractTransactSQLDialect extends Dialect {
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) {
throw new NotYetImplementedFor6Exception( getClass() );
// return new LocalTemporaryTableBulkIdStrategy(
// new IdTableSupportStandardImpl() {
// @Override
// public String generateIdTableName(String baseName) {
// return "#" + baseName;
// }
// },
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType entityDescriptor) {
return new LocalTemporaryTableStrategy(
new IdTable( entityDescriptor, basename -> "#" + basename ),
// // sql-server, at least needed this dropped after use; strange!
// AfterUseAction.DROP,
// TempTableDdlTransactionHandling.NONE
// );
AfterUseAction.DROP,
TempTableDdlTransactionHandling.NONE
);
}
@Override

View File

@ -38,7 +38,7 @@ import org.hibernate.exception.internal.CacheSQLExceptionConversionDelegate;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter;
import org.hibernate.exception.spi.ViolatedConstraintNameExtracter;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.persister.entity.Lockable;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.sql.CacheJoinFragment;
@ -444,7 +444,7 @@ public class Cache71Dialect extends Dialect {
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) {
throw new NotYetImplementedFor6Exception( getClass() );
// return new GlobalTemporaryTableBulkIdStrategy(
// new IdTableSupportStandardImpl() {

View File

@ -10,7 +10,7 @@ import java.sql.Types;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.dialect.function.DB2SubstringFunction;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.type.descriptor.sql.CharTypeDescriptor;
import org.hibernate.type.descriptor.sql.ClobTypeDescriptor;
@ -36,7 +36,7 @@ public class DB297Dialect extends DB2Dialect {
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) {
throw new NotYetImplementedFor6Exception( getClass() );
// // Starting in DB2 9.7, "real" global temporary tables that can be shared between sessions

View File

@ -33,7 +33,7 @@ import org.hibernate.engine.spi.RowSelection;
import org.hibernate.exception.LockTimeoutException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorDB2DatabaseImpl;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
@ -393,7 +393,7 @@ public class DB2Dialect extends Dialect {
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) {
throw new NotYetImplementedFor6Exception( getClass() );
// // Prior to DB2 9.7, "real" global temporary tables that can be shared between sessions

View File

@ -13,7 +13,7 @@ import java.sql.Types;
import java.util.Locale;
import org.hibernate.MappingException;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.dialect.function.AnsiTrimFunction;
import org.hibernate.dialect.function.DerbyConcatFunction;
import org.hibernate.dialect.pagination.AbstractLimitHandler;
@ -24,7 +24,11 @@ import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
import org.hibernate.engine.spi.RowSelection;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.metamodel.mapping.EntityMappingType;
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.spi.SqmMultiTableMutationStrategy;
import org.hibernate.sql.CaseFragment;
import org.hibernate.sql.DerbyCaseFragment;
@ -590,31 +594,27 @@ public class DerbyDialect extends DB2Dialect {
* The DECLARE GLOBAL TEMPORARY TABLE statement defines a temporary table for the current connection.
* </pre>
*
* {@link DB2Dialect} returns a {@link org.hibernate.hql.spi.id.global.GlobalTemporaryTableBulkIdStrategy} that
* {@link DB2Dialect} returns a {@link org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy} that
* will make temporary tables created at startup and hence unavailable for subsequent connections.<br/>
* see HHH-10238.
* </p>
* @return
* @param runtimeRootEntityDescriptor
*/
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) {
throw new NotYetImplementedFor6Exception( getClass() );
// return new LocalTemporaryTableBulkIdStrategy(new IdTableSupportStandardImpl() {
// @Override
// public String generateIdTableName(String baseName) {
// return "session." + super.generateIdTableName( baseName );
// }
//
// @Override
// public String getCreateIdTableCommand() {
// return "declare global temporary table";
// }
//
// @Override
// public String getCreateIdTableStatementOptions() {
// return "not logged";
// }
// }, AfterUseAction.CLEAN, null);
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) {
return new LocalTemporaryTableStrategy(
new IdTable( rootEntityDescriptor, basename -> "HT_" + basename ),
() -> new TempIdTableExporter() {
@Override
protected String getCreateCommand() {
return "declare global temporary table";
}
@Override
protected String getCreateOptions() {
return "not logged";
}
},
AfterUseAction.CLEAN,
TempTableDdlTransactionHandling.NONE
);
}
}

View File

@ -92,7 +92,7 @@ import org.hibernate.mapping.Constraint;
import org.hibernate.mapping.ForeignKey;
import org.hibernate.mapping.Index;
import org.hibernate.mapping.Table;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.persister.entity.Lockable;
import org.hibernate.procedure.internal.StandardCallableStatementSupport;
import org.hibernate.procedure.spi.CallableStatementSupport;
@ -101,6 +101,7 @@ import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
import org.hibernate.query.sqm.sql.internal.StandardSqmSelectTranslator;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.sql.ANSICaseFragment;
import org.hibernate.sql.ANSIJoinFragment;
@ -110,10 +111,7 @@ import org.hibernate.sql.JoinFragment;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.spi.ANSICaseExpressionWalker;
import org.hibernate.sql.ast.spi.CaseExpressionWalker;
import org.hibernate.sql.ast.spi.SqlAstWalker;
import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.tool.hbm2ddl.SchemaUpdate;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
@ -1517,7 +1515,7 @@ public abstract class Dialect implements ConversionContext {
return getCreateTableString();
}
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType entityDescriptor) {
throw new NotYetImplementedFor6Exception( getClass() );
}
@ -3114,7 +3112,7 @@ public abstract class Dialect implements ConversionContext {
* Note that {@link SessionFactoryOptions#getSqmTranslatorFactory()} has higher
* precedence as it comes directly from the user config
*
* @see org.hibernate.query.sqm.sql.internal.StandardSqmSelectToSqlAstConverter
* @see StandardSqmSelectTranslator
* @see QueryEngine#getSqmTranslatorFactory()
*/
public SqmTranslatorFactory getSqmTranslatorFactory() {

View File

@ -10,8 +10,8 @@ import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.JDBCException;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.PessimisticLockException;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.function.AvgWithArgumentCastFunction;
import org.hibernate.dialect.function.NoArgSQLFunction;
@ -32,7 +32,11 @@ import org.hibernate.exception.spi.ViolatedConstraintNameExtracter;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.persister.entity.Queryable;
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.spi.SqmMultiTableMutationStrategy;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorH2DatabaseImpl;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
@ -368,26 +372,12 @@ public class H2Dialect extends Dialect {
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) {
throw new NotYetImplementedFor6Exception( getClass() );
// return new LocalTemporaryTableBulkIdStrategy(
// new IdTableSupportStandardImpl() {
// @Override
// public String getCreateIdTableCommand() {
// return "create cached local temporary table if not exists";
// }
//
// @Override
// public String getCreateIdTableStatementOptions() {
// // actually 2 different options are specified here:
// // 1) [on commit drop] - says to drop the table on transaction commit
// // 2) [transactional] - says to not perform an implicit commit of any current transaction
// return "on commit drop transactional"; }
// },
// AfterUseAction.CLEAN,
// TempTableDdlTransactionHandling.NONE
// );
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType entityDescriptor) {
return new LocalTemporaryTableStrategy(
new IdTable( entityDescriptor, basename -> "HT_" + basename ),
AfterUseAction.NONE,
TempTableDdlTransactionHandling.NONE
);
}
@Override

View File

@ -10,7 +10,7 @@ import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.dialect.function.SQLFunctionTemplate;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.dialect.function.VarArgsSQLFunction;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.type.StandardBasicTypes;
@ -48,7 +48,7 @@ public class HANAColumnStoreDialect extends AbstractHANADialect {
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) {
throw new NotYetImplementedFor6Exception( getClass() );
// return new GlobalTemporaryTableBulkIdStrategy( new IdTableSupportStandardImpl() {

View File

@ -7,7 +7,7 @@
package org.hibernate.dialect;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
/**
@ -36,7 +36,7 @@ public class HANARowStoreDialect extends AbstractHANADialect {
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) {
throw new NotYetImplementedFor6Exception( getClass() );
// return new GlobalTemporaryTableBulkIdStrategy( new IdTableSupportStandardImpl() {

View File

@ -42,6 +42,7 @@ import org.hibernate.exception.spi.ViolatedConstraintNameExtracter;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Lockable;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
@ -503,7 +504,7 @@ public class HSQLDialect extends Dialect {
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) {
throw new NotYetImplementedFor6Exception( getClass() );
// // Hibernate uses this information for temporary tables that it uses for its own operations

View File

@ -11,6 +11,7 @@ import java.sql.Types;
import java.util.Locale;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.dialect.function.NoArgSQLFunction;
import org.hibernate.dialect.function.NvlFunction;
import org.hibernate.dialect.function.SQLFunctionTemplate;
@ -25,7 +26,11 @@ import org.hibernate.dialect.unique.UniqueDelegate;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter;
import org.hibernate.exception.spi.ViolatedConstraintNameExtracter;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.metamodel.mapping.EntityMappingType;
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.spi.SqmMultiTableMutationStrategy;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorInformixDatabaseImpl;
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
@ -279,24 +284,23 @@ public class InformixDialect extends Dialect {
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) {
throw new NotYetImplementedFor6Exception( getClass() );
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) {
return new LocalTemporaryTableStrategy(
new IdTable( rootEntityDescriptor, basename -> "HT_" + basename ),
() -> new TempIdTableExporter() {
@Override
protected String getCreateCommand() {
return "create temp table";
}
// return new LocalTemporaryTableBulkIdStrategy(
// new IdTableSupportStandardImpl() {
// @Override
// public String getCreateIdTableCommand() {
// return "create temp table";
// }
//
// @Override
// public String getCreateIdTableStatementOptions() {
// return "with no log";
// }
// },
// AfterUseAction.CLEAN,
// null
// );
@Override
protected String getCreateOptions() {
return "with no log";
}
},
AfterUseAction.NONE,
TempTableDdlTransactionHandling.NONE
);
}
@Override

View File

@ -17,6 +17,7 @@ import org.hibernate.dialect.function.VarArgsSQLFunction;
import org.hibernate.dialect.pagination.FirstLimitHandler;
import org.hibernate.dialect.pagination.LegacyFirstLimitHandler;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.tool.schema.extract.internal.SequenceNameExtractorImpl;
@ -270,7 +271,7 @@ public class IngresDialect extends Dialect {
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) {
throw new NotYetImplementedFor6Exception( getClass() );
// return new GlobalTemporaryTableBulkIdStrategy(

View File

@ -17,6 +17,7 @@ import org.hibernate.JDBCException;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.NullPrecedence;
import org.hibernate.PessimisticLockException;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.NoArgSQLFunction;
import org.hibernate.dialect.function.StandardSQLFunction;
@ -33,7 +34,11 @@ import org.hibernate.exception.LockTimeoutException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.mapping.Column;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.metamodel.mapping.EntityMappingType;
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.spi.SqmMultiTableMutationStrategy;
import org.hibernate.type.StandardBasicTypes;
@ -343,24 +348,24 @@ public class MySQLDialect extends Dialect {
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) {
throw new NotYetImplementedFor6Exception( getClass() );
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) {
// return new LocalTemporaryTableBulkIdStrategy(
// new IdTableSupportStandardImpl() {
// @Override
// public String getCreateIdTableCommand() {
// return "create temporary table if not exists";
// }
//
// @Override
// public String getDropIdTableCommand() {
// return "drop temporary table";
// }
// },
// AfterUseAction.DROP,
// TempTableDdlTransactionHandling.NONE
// );
return new LocalTemporaryTableStrategy(
new IdTable( rootEntityDescriptor, basename -> "HT_" + basename ),
() -> new TempIdTableExporter() {
@Override
protected String getCreateCommand() {
return "create temporary table if not exists";
}
@Override
protected String getDropCommand() {
return "drop temporary table";
}
},
AfterUseAction.NONE,
TempTableDdlTransactionHandling.NONE
);
}
@Override

View File

@ -35,6 +35,7 @@ import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter;
import org.hibernate.exception.spi.ViolatedConstraintNameExtracter;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.procedure.internal.StandardCallableStatementSupport;
import org.hibernate.procedure.spi.CallableStatementSupport;
@ -622,7 +623,7 @@ public class Oracle8iDialect extends Dialect {
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) {
throw new NotYetImplementedFor6Exception( getClass() );
// return new GlobalTemporaryTableBulkIdStrategy(

View File

@ -17,6 +17,7 @@ import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter;
import org.hibernate.exception.spi.ViolatedConstraintNameExtracter;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorOracleDatabaseImpl;
@ -344,7 +345,7 @@ public class Oracle9Dialect extends Dialect {
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) {
throw new NotYetImplementedFor6Exception( getClass() );
// return new GlobalTemporaryTableBulkIdStrategy(

View File

@ -36,7 +36,7 @@ import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter;
import org.hibernate.exception.spi.ViolatedConstraintNameExtracter;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.procedure.internal.PostgresCallableStatementSupport;
import org.hibernate.procedure.spi.CallableStatementSupport;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
@ -366,7 +366,7 @@ public class PostgreSQL81Dialect extends Dialect {
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) {
throw new NotYetImplementedFor6Exception( getClass() );
// return new LocalTemporaryTableBulkIdStrategy(

View File

@ -8,7 +8,7 @@ package org.hibernate.dialect;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.PostgresUUIDType;
@ -33,7 +33,7 @@ public class PostgreSQL82Dialect extends PostgreSQL81Dialect {
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) {
throw new NotYetImplementedFor6Exception( getClass() );
// return new LocalTemporaryTableBulkIdStrategy(

View File

@ -15,7 +15,7 @@ import org.hibernate.dialect.function.NoArgSQLFunction;
import org.hibernate.dialect.function.SQLFunctionTemplate;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.dialect.function.VarArgsSQLFunction;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.sql.CaseFragment;
import org.hibernate.sql.DecodeCaseFragment;
@ -226,7 +226,7 @@ public class SAPDBDialect extends Dialect {
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) {
throw new NotYetImplementedFor6Exception( getClass() );
// return new LocalTemporaryTableBulkIdStrategy(

View File

@ -5,6 +5,7 @@
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.dialect;
import java.sql.Types;
import org.hibernate.HibernateException;
@ -12,7 +13,7 @@ import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.SQLFunctionTemplate;
import org.hibernate.dialect.function.VarArgsSQLFunction;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.type.StandardBasicTypes;
@ -119,7 +120,7 @@ public class TeradataDialect extends Dialect {
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) {
throw new NotYetImplementedFor6Exception( getClass() );
// return new GlobalTemporaryTableBulkIdStrategy( this, AfterUseAction.CLEAN );
}

View File

@ -24,6 +24,7 @@ import org.hibernate.dialect.lock.UpdateLockingStrategy;
import org.hibernate.dialect.pagination.FirstLimitHandler;
import org.hibernate.dialect.pagination.LegacyFirstLimitHandler;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Lockable;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
@ -218,7 +219,7 @@ public class TimesTenDialect extends Dialect {
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityPersister runtimeRootEntityDescriptor) {
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(EntityMappingType rootEntityDescriptor) {
throw new NotYetImplementedFor6Exception( getClass() );
// return new GlobalTemporaryTableBulkIdStrategy(

View File

@ -94,4 +94,8 @@ public interface JdbcServices extends Service {
default JdbcMutationExecutor getJdbcUpdateExecutor() {
return StandardJdbcMutationExecutor.INSTANCE;
}
default JdbcMutationExecutor getJdbcInsertExecutor() {
return StandardJdbcMutationExecutor.INSTANCE;
}
}

View File

@ -119,6 +119,13 @@ public final class StringHelper {
return new String( buffer );
}
public static void repeat(String string, int times, String separator, StringBuilder buffer) {
buffer.append( string );
for ( int i = 1; i < times; i++ ) {
buffer.append( separator ).append( string );
}
}
public static String replace(String template, String placeholder, String replacement) {
return replace( template, placeholder, replacement, false );
}

View File

@ -100,9 +100,6 @@ class DatabaseSnapshotExecutor {
final NavigablePath idPath = rootPath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME );
entityDescriptor.getIdentifierMapping().visitColumns(
idPath,
rootTableGroup,
state,
(col, tab, jdbcMapping) -> {
final TableReference tableReference = rootTableGroup.resolveTableReference( tab );
@ -151,9 +148,6 @@ class DatabaseSnapshotExecutor {
contributorMapping -> {
final NavigablePath attrPath = rootPath.append( contributorMapping.getAttributeName() );
contributorMapping.visitColumns(
attrPath,
rootTableGroup,
state,
(columnExpression, containingTableExpression, jdbcMapping) -> {
final TableReference tableReference = rootTableGroup.resolveTableReference(
containingTableExpression );
@ -203,7 +197,7 @@ class DatabaseSnapshotExecutor {
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
jdbcSelect = sqlAstTranslatorFactory.buildSelectConverter( sessionFactory ).interpret( selectStatement );
jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory ).translate( selectStatement );
}
Object[] loadDatabaseSnapshot(Object id, SharedSessionContractImplementor session) {

View File

@ -252,9 +252,6 @@ public class MetamodelSelectBuilderProcess {
final List<ColumnReference> columnReferences = new ArrayList<>( numberOfKeyColumns );
keyPart.visitColumns(
keyPath,
rootTableGroup,
sqlAstCreationState,
(columnExpression, containingTableExpression, jdbcMapping) -> {
final TableReference tableReference = rootTableGroup.resolveTableReference( containingTableExpression );
columnReferences.add(

View File

@ -99,7 +99,7 @@ public class SingleIdEntityLoaderDynamicBatch<T> extends SingleIdEntityLoaderSup
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectConverter( sessionFactory ).interpret( sqlAstDescriptor.getSqlAst() );
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory ).translate( sqlAstDescriptor.getSqlAst() );
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl(
getLoadable().getIdentifierMapping().getJdbcTypeCount( sessionFactory.getTypeConfiguration() )

View File

@ -50,7 +50,7 @@ class SingleIdLoadPlan<T> {
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectConverter( sessionFactory ).interpret( sqlAstDescriptor.getSqlAst() );
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory ).translate( sqlAstDescriptor.getSqlAst() );
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl(
restrictivePart.getJdbcTypeCount( sessionFactory.getTypeConfiguration() )

View File

@ -0,0 +1,19 @@
/*
* 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.metamodel.mapping;
/**
* @author Steve Ebersole
*/
@FunctionalInterface
public interface ColumnConsumer {
// todo (6.0) : pass values `updateable`, `checkable`, etc
void accept(
String columnExpression,
String containingTableExpression,
JdbcMapping jdbcMapping);
}

View File

@ -152,6 +152,14 @@ public interface EntityMappingType extends ManagedMappingType, Loadable {
// by default do nothing
}
default void visitConstraintOrderedTables(ConstraintOrderedTableConsumer consumer) {
throw new NotYetImplementedFor6Exception( getClass() );
}
interface ConstraintOrderedTableConsumer {
void consume(String tableExpression, Supplier<Consumer<ColumnConsumer>> tableKeyColumnVisitationSupplier);
}
@Override
default void visitAttributeMappings(Consumer<AttributeMapping> action) {

View File

@ -18,7 +18,9 @@ import org.hibernate.sql.results.spi.DomainResultCreationState;
/**
* @author Steve Ebersole
*/
public interface ForeignKeyDescriptor {
public interface ForeignKeyDescriptor extends VirtualModelPart {
String PART_NAME = "{fk}";
DomainResult createDomainResult(NavigablePath collectionPath, TableGroup tableGroup, DomainResultCreationState creationState);
Predicate generateJoinPredicate(
@ -27,4 +29,32 @@ public interface ForeignKeyDescriptor {
JoinType joinType,
SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext);
@Override
default String getPartName() {
return PART_NAME;
}
/**
* Visits the FK "referring" columns
*/
@Override
default void visitColumns(ColumnConsumer consumer) {
visitReferringColumns( consumer );
}
void visitReferringColumns(ColumnConsumer consumer);
void visitTargetColumns(ColumnConsumer consumer);
void visitColumnMappings(FkColumnMappingConsumer consumer);
interface FkColumnMappingConsumer {
void consume(
String referringTable,
String referringColumn,
String targetTable,
String targetColumn,
JdbcMapping jdbcMapping);
}
}

View File

@ -7,16 +7,11 @@
package org.hibernate.metamodel.mapping;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.results.spi.DomainResult;
import org.hibernate.sql.results.spi.DomainResultCreationState;
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
@ -70,20 +65,8 @@ public interface ModelPart extends MappingModelExpressable {
throw new NotYetImplementedFor6Exception( getClass() );
}
default void visitColumns(
NavigablePath navigablePath,
TableGroup tableGroup,
DomainResultCreationState creationState,
ColumnConsumer consumer) {
default void visitColumns(ColumnConsumer consumer) {
}
@FunctionalInterface
interface ColumnConsumer {
// todo (6.0) : pass values `updateable`, `checkable`, etc
void accept(
String columnExpression,
String containingTableExpression,
JdbcMapping jdbcMapping);
}
}

View File

@ -41,4 +41,6 @@ public interface PluralAttributeMapping
default void visitFetchables(Consumer<Fetchable> fetchableConsumer, EntityMappingType treatTargetType) {
fetchableConsumer.accept( getElementDescriptor() );
}
String getSeparateCollectionTable();
}

View File

@ -7,6 +7,7 @@
package org.hibernate.metamodel.mapping;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.hibernate.boot.spi.SessionFactoryOptions;

View File

@ -13,6 +13,7 @@ import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.ColumnConsumer;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
@ -215,4 +216,9 @@ public class BasicValuedSingularAttributeMapping extends AbstractSingularAttribu
TypeConfiguration typeConfiguration) {
action.accept( getJdbcMapping() );
}
@Override
public void visitColumns(ColumnConsumer consumer) {
consumer.accept( mappedColumnExpression, tableExpression, jdbcMapping );
}
}

View File

@ -16,6 +16,7 @@ import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.ColumnConsumer;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType;
@ -129,12 +130,8 @@ public class EmbeddedAttributeMapping
}
@Override
public void visitColumns(
NavigablePath navigablePath,
TableGroup tableGroup,
DomainResultCreationState creationState,
ColumnConsumer consumer) {
getEmbeddableTypeDescriptor().visitColumns( navigablePath, tableGroup, creationState, consumer );
public void visitColumns(ColumnConsumer consumer) {
getEmbeddableTypeDescriptor().visitColumns( consumer );
}
@Override

View File

@ -25,6 +25,7 @@ import org.hibernate.dialect.Dialect;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
@ -46,6 +47,7 @@ import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.CollectionIdentifierDescriptor;
import org.hibernate.metamodel.mapping.CollectionMappingType;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.ColumnConsumer;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
@ -136,6 +138,11 @@ public class MappingModelCreationHelper {
return 1;
}
@Override
public void visitColumns(ColumnConsumer consumer) {
consumer.accept( getMappedColumnExpression(), getContainingTableExpression(), getJdbcMapping() );
}
@Override
public void visitJdbcTypes(
Consumer<JdbcMapping> action,
@ -635,7 +642,7 @@ public class MappingModelCreationHelper {
final CollectionPersister collectionDescriptor = domainModel.findCollectionDescriptor( bootValueMapping.getRole() );
assert collectionDescriptor != null;
String tableExpression = ( (Joinable) collectionDescriptor ).getTableName();
final String tableExpression = ( (Joinable) collectionDescriptor ).getTableName();
final String sqlAliasStem = SqlAliasStemHelper.INSTANCE.generateStemFromAttributeName( bootProperty.getName() );
@ -823,6 +830,7 @@ public class MappingModelCreationHelper {
return new PluralAttributeMappingImpl(
attrName,
bootValueMapping,
propertyAccess,
entityMappingType -> contributorMetadata,
collectionMappingType,
@ -869,6 +877,7 @@ public class MappingModelCreationHelper {
final BasicValuedModelPart simpleFkTarget = (BasicValuedModelPart) fkTarget;
return new SimpleForeignKeyDescriptor(
bootValueMapping.getKey().getTable().getName(),
bootValueMapping.getKey().getColumnIterator().next().getText( dialect ),
simpleFkTarget.getContainingTableExpression(),
simpleFkTarget.getMappedColumnExpression(),
@ -896,10 +905,19 @@ public class MappingModelCreationHelper {
fkTarget = referencedEntityDescriptor.findSubPart( bootValueMapping.getReferencedPropertyName() );
}
final JdbcServices jdbcServices = creationProcess.getCreationContext().getSessionFactory().getJdbcServices();
if ( fkTarget instanceof BasicValuedModelPart ) {
final BasicValuedModelPart simpleFkTarget = (BasicValuedModelPart) fkTarget;
return new SimpleForeignKeyDescriptor(
creationProcess.getCreationContext()
.getBootstrapContext()
.getMetadataBuildingOptions()
.getPhysicalNamingStrategy().toPhysicalTableName(
bootValueMapping.getTable().getNameIdentifier(),
jdbcServices.getJdbcEnvironment()
).getText(),
bootValueMapping.getColumnIterator().next().getText( dialect ),
simpleFkTarget.getContainingTableExpression(),
simpleFkTarget.getMappedColumnExpression(),

View File

@ -12,6 +12,7 @@ import org.hibernate.LockMode;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.mapping.Collection;
import org.hibernate.metamodel.mapping.CollectionIdentifierDescriptor;
import org.hibernate.metamodel.mapping.CollectionMappingType;
import org.hibernate.metamodel.mapping.CollectionPart;
@ -22,6 +23,7 @@ import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
@ -59,12 +61,14 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
private final CascadeStyle cascadeStyle;
private final CollectionPersister collectionDescriptor;
private final String separateCollectionTable;
private final String sqlAliasStem;
@SuppressWarnings("WeakerAccess")
public PluralAttributeMappingImpl(
String attributeName,
Collection bootDescriptor,
PropertyAccess propertyAccess,
StateArrayContributorMetadataAccess stateArrayContributorMetadataAccess,
CollectionMappingType collectionMappingType,
@ -90,6 +94,13 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
this.collectionDescriptor = collectionDescriptor;
this.sqlAliasStem = SqlAliasStemHelper.INSTANCE.generateStemFromAttributeName( attributeName );
if ( bootDescriptor.isOneToMany() ) {
separateCollectionTable = null;
}
else {
separateCollectionTable = ( (Joinable) collectionDescriptor ).getTableName();
}
}
@Override
@ -122,6 +133,11 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
return identifierDescriptor;
}
@Override
public String getSeparateCollectionTable() {
return separateCollectionTable;
}
@Override
public int getStateArrayPosition() {
return stateArrayPosition;

View File

@ -6,10 +6,17 @@
*/
package org.hibernate.metamodel.mapping.internal;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.ColumnConsumer;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.query.ComparisonOperator;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.JoinType;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
@ -23,21 +30,26 @@ import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.results.internal.domain.basic.BasicResult;
import org.hibernate.sql.results.spi.DomainResult;
import org.hibernate.sql.results.spi.DomainResultCreationState;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* @author Steve Ebersole
*/
public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor {
private final String keyColumnContainingTable;
private final String keyColumnExpression;
private final String targetColumnContainingTable;
private final String targetColumnExpression;
private final JdbcMapping jdbcMapping;
public SimpleForeignKeyDescriptor(
String keyColumnContainingTable,
String keyColumnExpression,
String targetColumnContainingTable,
String targetColumnExpression,
JdbcMapping jdbcMapping) {
this.keyColumnContainingTable = keyColumnContainingTable;
this.keyColumnExpression = keyColumnExpression;
this.targetColumnContainingTable = targetColumnContainingTable;
this.targetColumnExpression = targetColumnExpression;
@ -114,4 +126,51 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor {
keyReference
);
}
@Override
public JavaTypeDescriptor getJavaTypeDescriptor() {
return jdbcMapping.getJavaTypeDescriptor();
}
@Override
public void visitReferringColumns(ColumnConsumer consumer) {
consumer.accept( keyColumnContainingTable, keyColumnExpression, jdbcMapping );
}
@Override
public void visitTargetColumns(ColumnConsumer consumer) {
consumer.accept( targetColumnContainingTable, targetColumnExpression, jdbcMapping );
}
@Override
public void visitColumnMappings(FkColumnMappingConsumer consumer) {
consumer.consume( keyColumnContainingTable, keyColumnExpression, targetColumnContainingTable, targetColumnExpression, jdbcMapping );
}
@Override
public int getJdbcTypeCount(TypeConfiguration typeConfiguration) {
return 1;
}
@Override
public List<JdbcMapping> getJdbcMappings(TypeConfiguration typeConfiguration) {
return Collections.singletonList( jdbcMapping );
}
@Override
public void visitJdbcTypes(
Consumer<JdbcMapping> action,
Clause clause,
TypeConfiguration typeConfiguration) {
action.accept( jdbcMapping );
}
@Override
public void visitJdbcValues(
Object value,
Clause clause,
JdbcValuesConsumer valuesConsumer,
SharedSessionContractImplementor session) {
valuesConsumer.consume( value, jdbcMapping );
}
}

View File

@ -10,12 +10,20 @@ import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Consumer;
import javax.persistence.metamodel.Bindable;
import javax.persistence.metamodel.IdentifiableType;
import javax.persistence.metamodel.SingularAttribute;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.model.domain.internal.AttributeContainer;
import org.hibernate.metamodel.model.domain.internal.BasicSqmPathSource;
import org.hibernate.metamodel.model.domain.internal.EmbeddedSqmPathSource;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
import org.jboss.logging.Logger;
/**
* Defines commonality for the JPA {@link IdentifiableType} types. JPA defines
@ -36,6 +44,8 @@ public abstract class AbstractIdentifiableType<J>
private final boolean hasIdClass;
private SingularPersistentAttribute<J,?> id;
private Set<SingularPersistentAttribute<? super J,?>> idClassAttributes;
private SqmPathSource identifierDescriptor;
private final boolean isVersioned;
private SingularPersistentAttribute<J, ?> versionAttribute;
@ -66,6 +76,11 @@ public abstract class AbstractIdentifiableType<J>
return (InFlightAccessImpl) super.getInFlightAccess();
}
@Override
public SqmPathSource getIdentifierDescriptor() {
return identifierDescriptor;
}
public boolean hasIdClass() {
return hasIdClass;
}
@ -173,7 +188,6 @@ public abstract class AbstractIdentifiableType<J>
*
* @return IdClass attributes or {@code null}
*/
@SuppressWarnings("unchecked")
public Set<SingularPersistentAttribute<? super J, ?>> getIdClassAttributesSafely() {
if ( !hasIdClass() ) {
return null;
@ -335,6 +349,47 @@ public abstract class AbstractIdentifiableType<J>
@Override
public void finishUp() {
managedTypeAccess.finishUp();
identifierDescriptor = interpretIdDescriptor();
}
}
private static final Logger log = Logger.getLogger( AbstractIdentifiableType.class );
private SqmPathSource interpretIdDescriptor() {
log.tracef( "Interpreting domain-model identifier descriptor" );
if ( getSuperType() != null ) {
return getSuperType().getIdentifierDescriptor();
}
else if ( id != null ) {
// simple id or aggregate composite id
final SimpleDomainType<?> type = id.getType();
if ( type instanceof BasicDomainType ) {
//noinspection unchecked
return new BasicSqmPathSource(
EntityIdentifierMapping.ROLE_LOCAL_NAME,
(BasicDomainType) type,
Bindable.BindableType.SINGULAR_ATTRIBUTE
);
}
else {
assert type instanceof EmbeddableDomainType;
final EmbeddableDomainType compositeType = (EmbeddableDomainType) type;
//noinspection unchecked
return new EmbeddedSqmPathSource(
EntityIdentifierMapping.ROLE_LOCAL_NAME,
compositeType,
Bindable.BindableType.SINGULAR_ATTRIBUTE
);
}
}
else if ( idClassAttributes != null && ! idClassAttributes.isEmpty() ) {
// non-aggregate composite id
throw new NotYetImplementedFor6Exception( getClass() );
}
else {
throw new UnsupportedOperationException( "Could not build SqmPathSource for entity identifier : " + getTypeName() );
}
}
}

View File

@ -11,12 +11,16 @@ import java.util.function.Consumer;
import javax.persistence.metamodel.IdentifiableType;
import javax.persistence.metamodel.SingularAttribute;
import org.hibernate.query.sqm.SqmPathSource;
/**
* Extension to the JPA {@link IdentifiableType} contract
*
* @author Steve Ebersole
*/
public interface IdentifiableDomainType<J> extends ManagedDomainType<J>, IdentifiableType<J> {
SqmPathSource getIdentifierDescriptor();
@Override
<Y> SingularPersistentAttribute<? super J, Y> getId(Class<Y> type);

View File

@ -18,8 +18,8 @@ import org.hibernate.metamodel.model.domain.IdentifiableDomainType;
import org.hibernate.metamodel.model.domain.JpaMetamodel;
import org.hibernate.metamodel.model.domain.PersistentAttribute;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;

View File

@ -6,13 +6,22 @@
*/
package org.hibernate.metamodel.model.domain.internal;
import javax.persistence.metamodel.Bindable;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.graph.spi.SubGraphImplementor;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.mapping.Property;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.model.domain.AbstractIdentifiableType;
import org.hibernate.metamodel.model.domain.IdentifiableDomainType;
import org.hibernate.metamodel.model.domain.JpaMetamodel;
import org.hibernate.metamodel.model.domain.MappedSuperclassDomainType;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.type.BasicType;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
/**

View File

@ -130,6 +130,7 @@ import org.hibernate.mapping.Component;
import org.hibernate.mapping.Formula;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.Subclass;
import org.hibernate.mapping.Table;
@ -166,6 +167,8 @@ import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.property.access.spi.Setter;
import org.hibernate.query.ComparisonOperator;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper;
import org.hibernate.query.sqm.mutation.internal.cte.CteBasedMutationStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.Alias;
@ -6189,14 +6192,40 @@ public abstract class AbstractEntityPersister
final SessionFactoryImplementor sessionFactory = creationContext.getSessionFactory();
if ( isMultiTable() ) {
sqmMultiTableMutationStrategy = null;
//sessionFactory.getJdbcServices().getJdbcEnvironment().getDialect().getFallbackSqmMutationStrategy( this )
sqmMultiTableMutationStrategy = interpretSqmMultiTableStrategy(
this,
creationProcess
);
}
else {
sqmMultiTableMutationStrategy = null;
}
}
protected static SqmMultiTableMutationStrategy interpretSqmMultiTableStrategy(
AbstractEntityPersister entityMappingDescriptor,
MappingModelCreationProcess creationProcess) {
assert entityMappingDescriptor.isMultiTable();
if ( entityMappingDescriptor.getSuperMappingType() != null ) {
return entityMappingDescriptor.getSuperMappingType().getSqmMultiTableMutationStrategy();
}
// we need the boot model so we can have access to the Table
final RootClass entityBootDescriptor = (RootClass) creationProcess.getCreationContext()
.getBootModel()
.getEntityBinding( entityMappingDescriptor.getRootEntityName() );
final Table rootTable = entityBootDescriptor.getRootTable();
return SqmMutationStrategyHelper.resolveStrategy(
entityBootDescriptor,
entityMappingDescriptor,
creationProcess.getCreationContext().getSessionFactory().getSessionFactoryOptions() ,
creationProcess.getCreationContext().getSessionFactory().getServiceRegistry()
);
}
@Override
public SqmMultiTableMutationStrategy getSqmMultiTableMutationStrategy() {
return sqmMultiTableMutationStrategy;

View File

@ -15,6 +15,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Supplier;
@ -1251,6 +1252,25 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
}
}
@Override
public void visitConstraintOrderedTables(ConstraintOrderedTableConsumer consumer) {
final AtomicInteger tablePositionWrapper = new AtomicInteger( );
for ( String tableName : constraintOrderedTableNames ) {
final int tablePosition = tablePositionWrapper.getAndIncrement();
consumer.consume(
tableName,
() -> columnConsumer -> {
final String[] keyColumnNames = constraintOrderedKeyColumnNames[tablePosition];
for ( String column : keyColumnNames ) {
columnConsumer.accept( column, tableName, null );
}
}
);
}
}
private class CaseSearchedExpressionInfo{
CaseSearchedExpression caseSearchedExpression;
List<ColumnReference> columnReferences = new ArrayList<>( );

View File

@ -14,6 +14,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Supplier;
@ -919,4 +920,23 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
)
);
}
@Override
public void visitConstraintOrderedTables(ConstraintOrderedTableConsumer consumer) {
final AtomicInteger tablePositionWrapper = new AtomicInteger( );
for ( String tableName : constraintOrderedTableNames ) {
final int tablePosition = tablePositionWrapper.getAndIncrement();
consumer.consume(
tableName,
() -> columnConsumer -> {
final String[] keyColumnNames = constraintOrderedKeyColumnNames[tablePosition];
for ( String column : keyColumnNames ) {
columnConsumer.accept( column, tableName, null );
}
}
);
}
}
}

View File

@ -14,6 +14,7 @@ import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Supplier;
@ -417,6 +418,26 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
return isAbstract() || hasSubclasses();
}
@Override
public void visitConstraintOrderedTables(ConstraintOrderedTableConsumer consumer) {
final AtomicInteger tablePositionWrapper = new AtomicInteger( );
for ( String tableName : constraintOrderedTableNames ) {
final int tablePosition = tablePositionWrapper.getAndIncrement();
consumer.consume(
tableName,
() -> columnConsumer -> {
final String[] keyColumnNames = constraintOrderedKeyColumnNames[tablePosition];
for ( String column : keyColumnNames ) {
columnConsumer.accept( column, tableName, null );
}
}
);
}
}
@Override
protected void buildDiscriminatorMapping() {
if ( hasSubclasses() ) {

View File

@ -33,11 +33,13 @@ import org.hibernate.query.sqm.internal.SqmCreationOptionsStandard;
import org.hibernate.query.sqm.internal.SqmCriteriaNodeBuilder;
import org.hibernate.query.sqm.produce.function.SqmFunctionRegistry;
import org.hibernate.query.sqm.spi.SqmCreationContext;
import org.hibernate.query.sqm.sql.SimpleSqmDeleteToSqlAstConverter;
import org.hibernate.query.sqm.sql.SqmSelectToSqlAstConverter;
import org.hibernate.query.sqm.sql.SimpleSqmDeleteTranslator;
import org.hibernate.query.sqm.sql.SqmInsertSelectTranslator;
import org.hibernate.query.sqm.sql.SqmSelectTranslator;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
import org.hibernate.query.sqm.sql.internal.StandardSqmDeleteToSqlAstConverter;
import org.hibernate.query.sqm.sql.internal.StandardSqmSelectToSqlAstConverter;
import org.hibernate.query.sqm.sql.internal.StandardSqmDeleteTranslator;
import org.hibernate.query.sqm.sql.internal.StandardSqmInsertSelectTranslator;
import org.hibernate.query.sqm.sql.internal.StandardSqmSelectTranslator;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
@ -145,16 +147,15 @@ public class QueryEngine {
return dialect.getSqmTranslatorFactory();
}
//noinspection Convert2Lambda
return new SqmTranslatorFactory() {
@Override
public SqmSelectToSqlAstConverter createSelectConverter(
public SqmSelectTranslator createSelectTranslator(
QueryOptions queryOptions,
DomainParameterXref domainParameterXref,
QueryParameterBindings domainParameterBindings,
LoadQueryInfluencers influencers,
SqlAstCreationContext creationContext) {
return new StandardSqmSelectToSqlAstConverter(
return new StandardSqmSelectTranslator(
queryOptions,
domainParameterXref,
domainParameterBindings,
@ -164,13 +165,28 @@ public class QueryEngine {
}
@Override
public SimpleSqmDeleteToSqlAstConverter createSimpleDeleteConverter(
public SimpleSqmDeleteTranslator createSimpleDeleteTranslator(
QueryOptions queryOptions,
DomainParameterXref domainParameterXref,
QueryParameterBindings domainParameterBindings,
LoadQueryInfluencers influencers,
SqlAstCreationContext creationContext) {
return new StandardSqmDeleteToSqlAstConverter(
return new StandardSqmDeleteTranslator(
creationContext,
queryOptions,
domainParameterXref,
domainParameterBindings
);
}
@Override
public SqmInsertSelectTranslator createInsertSelectTranslator(
QueryOptions queryOptions,
DomainParameterXref domainParameterXref,
QueryParameterBindings domainParameterBindings,
LoadQueryInfluencers influencers,
SqlAstCreationContext creationContext) {
return new StandardSqmInsertSelectTranslator(
creationContext,
queryOptions,
domainParameterXref,

View File

@ -25,9 +25,9 @@ import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterImplementor;
import org.hibernate.query.spi.ScrollableResultsImplementor;
import org.hibernate.query.spi.SelectQueryPlan;
import org.hibernate.query.sqm.sql.SqmSelectToSqlAstConverter;
import org.hibernate.query.sqm.sql.SqmSelectTranslator;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
import org.hibernate.query.sqm.sql.SqmSelectInterpretation;
import org.hibernate.query.sqm.sql.SqmSelectTranslation;
import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
@ -164,7 +164,7 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
final SqmTranslatorFactory sqmTranslatorFactory = queryEngine.getSqmTranslatorFactory();
final SqmSelectToSqlAstConverter sqmConverter = sqmTranslatorFactory.createSelectConverter(
final SqmSelectTranslator sqmConverter = sqmTranslatorFactory.createSelectTranslator(
executionContext.getQueryOptions(),
domainParameterXref,
executionContext.getQueryParameterBindings(),
@ -172,14 +172,14 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
sessionFactory
);
final SqmSelectInterpretation interpretation = sqmConverter.interpret( sqm );
final SqmSelectTranslation interpretation = sqmConverter.translate( sqm );
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
jdbcSelect = sqlAstTranslatorFactory.buildSelectConverter( sessionFactory )
.interpret( interpretation.getSqlAst() );
jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory )
.translate( interpretation.getSqlAst() );
this.jdbcParamsXref = SqmUtil.generateJdbcParamsXref(
domainParameterXref,

View File

@ -16,8 +16,8 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.query.spi.NonSelectQueryPlan;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.spi.QueryParameterImplementor;
import org.hibernate.query.sqm.sql.SimpleSqmDeleteInterpretation;
import org.hibernate.query.sqm.sql.SimpleSqmDeleteToSqlAstConverter;
import org.hibernate.query.sqm.sql.SimpleSqmDeleteTranslation;
import org.hibernate.query.sqm.sql.SimpleSqmDeleteTranslator;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
@ -55,7 +55,7 @@ public class SimpleDeleteQueryPlan implements NonSelectQueryPlan {
final QueryEngine queryEngine = factory.getQueryEngine();
final SqmTranslatorFactory converterFactory = queryEngine.getSqmTranslatorFactory();
final SimpleSqmDeleteToSqlAstConverter converter = converterFactory.createSimpleDeleteConverter(
final SimpleSqmDeleteTranslator converter = converterFactory.createSimpleDeleteTranslator(
executionContext.getQueryOptions(),
domainParameterXref,
executionContext.getQueryParameterBindings(),
@ -63,12 +63,12 @@ public class SimpleDeleteQueryPlan implements NonSelectQueryPlan {
factory
);
final SimpleSqmDeleteInterpretation sqmInterpretation = converter.interpret( sqmDelete );
final SimpleSqmDeleteTranslation sqmInterpretation = converter.translate( sqmDelete );
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
final SqlAstDeleteTranslator sqlAstTranslator = sqlAstTranslatorFactory.buildDeleteConverter( factory );
final SqlAstDeleteTranslator sqlAstTranslator = sqlAstTranslatorFactory.buildDeleteTranslator( factory );
jdbcDelete = sqlAstTranslator.translate( sqmInterpretation.getSqlAst() );

View File

@ -0,0 +1,140 @@
/*
* 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;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.collections.Stack;
import org.hibernate.internal.util.collections.StandardStack;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.hql.spi.SqmCreationOptions;
import org.hibernate.query.hql.spi.SqmCreationProcessingState;
import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.SqmQuerySource;
import org.hibernate.query.sqm.internal.SqmQuerySpecCreationProcessingStateStandardImpl;
import org.hibernate.query.sqm.mutation.internal.idtable.IdTableSessionUidColumn;
import org.hibernate.query.sqm.spi.SqmCreationContext;
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.query.sqm.tree.from.SqmFromClause;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.select.SqmQuerySpec;
import org.hibernate.query.sqm.tree.select.SqmSelectClause;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import org.hibernate.query.sqm.tree.select.SqmSelection;
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.type.StringType;
import org.jboss.logging.Logger;
/**
* Helper used to generate the SELECT for selection of an entity's
* identifier, here specifically intended to be used as the SELECT
* portion of a multi-table SQM mutation
*
* @author Steve Ebersole
*/
public class SqmIdSelectGenerator {
private static final Logger log = Logger.getLogger( SqmIdSelectGenerator.class );
public static SqmQuerySpec generateSqmEntityIdSelect(
SqmDeleteOrUpdateStatement sqmStatement,
ExecutionContext executionContext,
SessionFactoryImplementor sessionFactory) {
final SqmIdSelectGenerator generator = new SqmIdSelectGenerator( sqmStatement, executionContext, sessionFactory );
return generator.process();
}
private final SqmDeleteOrUpdateStatement sourceSqmStatement;
private final ExecutionContext executionContext;
private final SqmCreationContext creationContext;
private final EntityDomainType entityType;
public SqmIdSelectGenerator(
SqmDeleteOrUpdateStatement sourceSqmStatement,
ExecutionContext executionContext,
SqmCreationContext creationContext) {
this.sourceSqmStatement = sourceSqmStatement;
this.executionContext = executionContext;
this.creationContext = creationContext;
final String targetEntityName = sourceSqmStatement.getTarget().getEntityName();
this.entityType = creationContext.getJpaMetamodel().entity( targetEntityName );
}
private SqmQuerySpec process() {
final SqmQuerySpec sqmQuerySpec = new SqmQuerySpec( creationContext.getNodeBuilder() );
final Stack<SqmCreationProcessingState> processingStateStack = new StandardStack<>();
final SqmCreationState creationState = new SqmCreationState() {
@Override
public SqmCreationContext getCreationContext() {
return creationContext;
}
@Override
public SqmCreationOptions getCreationOptions() {
return () -> false;
}
@Override
public Stack<SqmCreationProcessingState> getProcessingStateStack() {
return processingStateStack;
}
};
// temporary - used just for creating processingState
final SqmSelectStatement sqmSelectStatement = new SqmSelectStatement( creationContext.getNodeBuilder() );
//noinspection unchecked
sqmSelectStatement.setQuerySpec( sqmQuerySpec );
final SqmCreationProcessingState processingState = new SqmQuerySpecCreationProcessingStateStandardImpl(
null,
sqmSelectStatement,
creationState
);
processingStateStack.push( processingState );
final SqmFromClause sqmFromClause = new SqmFromClause();
sqmQuerySpec.setFromClause( sqmFromClause );
//noinspection unchecked
final SqmRoot<?> sqmRoot = new SqmRoot( entityType, null, sourceSqmStatement.nodeBuilder() );
sqmFromClause.addRoot( sqmRoot );
final SqmSelectClause sqmSelectClause = new SqmSelectClause( true, creationContext.getNodeBuilder() );
sqmQuerySpec.setSelectClause( sqmSelectClause );
applySelections( sqmQuerySpec, sqmRoot, processingState );
if ( sourceSqmStatement.getWhereClause() != null ) {
sqmQuerySpec.applyPredicate( sourceSqmStatement.getWhereClause().getPredicate() );
}
return sqmQuerySpec;
}
private void applySelections(
SqmQuerySpec sqmQuerySpec,
SqmRoot<?> sqmRoot,
SqmCreationProcessingState processingState) {
//noinspection unchecked
final SqmPath idPath = entityType.getIdentifierDescriptor().createSqmPath( sqmRoot, processingState.getCreationState() );
//noinspection unchecked
sqmQuerySpec.getSelectClause().add(
new SqmSelection(
idPath,
null,
processingState.getCreationState().getCreationContext().getNodeBuilder()
)
);
}
}

View File

@ -7,30 +7,46 @@
package org.hibernate.query.sqm.mutation.internal;
import java.util.List;
import java.util.Map;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.mapping.RootClass;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.spi.QueryParameterImplementor;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.internal.SqmUtil;
import org.hibernate.query.sqm.mutation.internal.idtable.IdTable;
import org.hibernate.query.sqm.mutation.spi.DeleteHandler;
import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.mutation.spi.UpdateHandler;
import org.hibernate.query.sqm.sql.SqmSelectTranslation;
import org.hibernate.query.sqm.sql.SqmSelectTranslator;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
import org.hibernate.query.sqm.tree.domain.SqmSimplePath;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.predicate.SqmBetweenPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmGroupedPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmInListPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmComparisonPredicate;
import org.hibernate.query.sqm.tree.predicate.SqmJunctivePredicate;
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
import org.hibernate.query.sqm.tree.select.SqmQuerySpec;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcParameter;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.exec.spi.JdbcSelect;
/**
* @author Steve Ebersole
@ -49,7 +65,7 @@ public class SqmMutationStrategyHelper {
* entity hierarchy.
*/
public static SqmMultiTableMutationStrategy resolveStrategy(
RootClass bootRootEntityDescriptor,
RootClass bootEntityDescriptor,
EntityPersister runtimeRootEntityDescriptor,
SessionFactoryOptions options,
ServiceRegistry serviceRegistry) {
@ -213,43 +229,59 @@ public class SqmMutationStrategyHelper {
* or UPDATE SQM query
*/
public static List<Object> selectMatchingIds(
DomainParameterXref domainParameterXref,
SqmDeleteOrUpdateStatement sqmDeleteStatement,
DomainParameterXref domainParameterXref,
ExecutionContext executionContext) {
throw new NotYetImplementedFor6Exception( SqmMutationStrategyHelper.class );
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
final QueryEngine queryEngine = factory.getQueryEngine();
final SqmTranslatorFactory sqmTranslatorFactory = queryEngine.getSqmTranslatorFactory();
// final SqmQuerySpec sqmIdSelectQuerySpec = SqmIdSelectGenerator.generateSqmEntityIdSelect(
// sqmDeleteStatement,
// executionContext.getSession().getSessionFactory()
// );
//
// final SqmSelectToSqlAstConverter sqmConverter = new SqmSelectToSqlAstConverter(
// executionContext.getQueryOptions(),
// domainParameterXref,
// executionContext.getDomainParameterBindingContext().getQueryParameterBindings(),
// executionContext.getLoadQueryInfluencers(),
// executionContext.getCallback(),
// executionContext.getSession().getSessionFactory()
// );
//
// final SqmSelectStatement sqmIdSelect = new SqmSelectStatement( sqmIdSelectQuerySpec );
//
// final SqmSelectInterpretation sqmSelectInterpretation = sqmConverter.interpret( sqmIdSelect );
//
// final JdbcSelect jdbcSelect = SqlAstSelectToJdbcSelectConverter.interpret(
// sqmSelectInterpretation,
// executionContext.getSession().getSessionFactory()
// );
//
// return JdbcSelectExecutorStandardImpl.INSTANCE.list(
// jdbcSelect,
// QueryHelper.buildJdbcParameterBindings(
// domainParameterXref,
// SqmConsumeHelper.generateJdbcParamsXref( domainParameterXref, sqmConverter ),
// executionContext
// ),
// executionContext,
// row -> row[0]
// );
final SqmSelectTranslator selectConverter = sqmTranslatorFactory.createSelectTranslator(
executionContext.getQueryOptions(),
domainParameterXref,
executionContext.getQueryParameterBindings(),
executionContext.getLoadQueryInfluencers(),
factory
);
final SqmQuerySpec sqmIdSelectQuerySpec = SqmIdSelectGenerator.generateSqmEntityIdSelect(
sqmDeleteStatement,
executionContext,
factory
);
final SqmSelectStatement sqmIdSelect = new SqmSelectStatement( factory.getNodeBuilder() );
//noinspection unchecked
sqmIdSelect.setQuerySpec( sqmIdSelectQuerySpec );
final SqmSelectTranslation sqmInterpretation = selectConverter.translate( sqmIdSelect );
final JdbcServices jdbcServices = factory.getJdbcServices();
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( factory ).translate( sqmInterpretation.getSqlAst() );
final Map<QueryParameterImplementor<?>, Map<SqmParameter, List<JdbcParameter>>> jdbcParamsXref = SqmUtil.generateJdbcParamsXref(
domainParameterXref,
sqmInterpretation::getJdbcParamsBySqmParam
);
final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(
executionContext.getQueryParameterBindings(),
domainParameterXref,
jdbcParamsXref,
// todo (6.0) : ugh. this one is important
null,
executionContext.getSession()
);
return factory.getJdbcServices().getJdbcSelectExecutor().list(
jdbcSelect,
jdbcParameterBindings,
executionContext,
row -> row
);
}
}

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.cte;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.spi.AbstractMutationHandler;
import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext;
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
import org.hibernate.sql.ast.tree.cte.CteTable;
/**
* Defines how identifier values are selected from the updatable/deletable tables.
*
* @author Evandro Pires da Silva
* @author Vlad Mihalcea
* @author Steve Ebersole
*/
public abstract class AbstractCteMutationHandler extends AbstractMutationHandler {
private final CteTable cteTable;
private final DomainParameterXref domainParameterXref;
private final CteBasedMutationStrategy strategy;
public AbstractCteMutationHandler(
CteTable cteTable,
SqmDeleteOrUpdateStatement sqmStatement,
DomainParameterXref domainParameterXref,
CteBasedMutationStrategy strategy,
HandlerCreationContext creationContext) {
super( sqmStatement, creationContext );
this.cteTable = cteTable;
this.domainParameterXref = domainParameterXref;
this.strategy = strategy;
}
public CteTable getCteTable() {
return cteTable;
}
public DomainParameterXref getDomainParameterXref() {
return domainParameterXref;
}
public CteBasedMutationStrategy getStrategy() {
return strategy;
}
}

View File

@ -6,16 +6,21 @@
*/
package org.hibernate.query.sqm.mutation.internal.cte;
import org.hibernate.NotYetImplementedFor6Exception;
import java.util.Locale;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.spi.DeleteHandler;
import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.mutation.spi.UpdateHandler;
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
import org.hibernate.sql.ast.tree.cte.CteTable;
/**
* @asciidoc
@ -79,9 +84,43 @@ public class CteBasedMutationStrategy implements SqmMultiTableMutationStrategy {
public static final String SHORT_NAME = "cte";
public static final String TABLE_NAME = "id_cte";
private final EntityPersister rootDescriptor;
private final CteTable cteTable;
public CteBasedMutationStrategy(
EntityPersister rootDescriptor,
BootstrapContext bootstrapContext) {
this.rootDescriptor = rootDescriptor;
final Dialect dialect = bootstrapContext.getTypeConfiguration()
.getSessionFactory()
.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"
);
}
if ( !dialect.supportsRowValueConstructorSyntaxInInList() ) {
throw new UnsupportedOperationException(
getClass().getSimpleName() +
" can only be used with Dialects that support IN clause row-value expressions (for composite identifiers)"
);
}
this.cteTable = new CteTable( rootDescriptor, bootstrapContext );
}
@Override
@ -89,7 +128,28 @@ public class CteBasedMutationStrategy implements SqmMultiTableMutationStrategy {
SqmUpdateStatement sqmUpdateStatement,
DomainParameterXref domainParameterXref,
HandlerCreationContext creationContext) {
throw new NotYetImplementedFor6Exception( getClass() );
checkMatch( sqmUpdateStatement, creationContext );
return new CteUpdateHandler( cteTable, sqmUpdateStatement, domainParameterXref, this, creationContext );
}
private void checkMatch(SqmDeleteOrUpdateStatement sqmStatement, HandlerCreationContext creationContext) {
final String targetEntityName = sqmStatement.getTarget().getEntityName();
final EntityPersister targetEntityDescriptor = creationContext.getSessionFactory()
.getDomainModel()
.getEntityDescriptor( targetEntityName );
if ( targetEntityDescriptor != rootDescriptor && ! rootDescriptor.isSubclassEntityName( targetEntityDescriptor.getEntityName() ) ) {
throw new IllegalArgumentException(
String.format(
Locale.ROOT,
"Target of query [%s] did not match configured entity [%s]",
targetEntityName,
rootDescriptor.getEntityName()
)
);
}
}
@Override
@ -97,6 +157,8 @@ public class CteBasedMutationStrategy implements SqmMultiTableMutationStrategy {
SqmDeleteStatement sqmDeleteStatement,
DomainParameterXref domainParameterXref,
HandlerCreationContext creationContext) {
throw new NotYetImplementedFor6Exception( getClass() );
checkMatch( sqmDeleteStatement, creationContext );
return new CteDeleteHandler( cteTable, sqmDeleteStatement, domainParameterXref, this, creationContext );
}
}

View File

@ -0,0 +1,260 @@
/*
* 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 java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.ColumnConsumer;
import org.hibernate.metamodel.mapping.MappingModelExpressable;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper;
import org.hibernate.query.sqm.mutation.spi.DeleteHandler;
import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.tree.cte.CteStatement;
import org.hibernate.sql.ast.tree.cte.CteTable;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcDelete;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
/**
* Bulk-id delete handler that uses CTE and VALUES lists.
*
* @author Evandro Pires da Silva
* @author Vlad Mihalcea
* @author Steve Ebersole
*/
@SuppressWarnings("WeakerAccess")
public class CteDeleteHandler extends AbstractCteMutationHandler implements DeleteHandler {
private final SqlAstTranslatorFactory sqlAstTranslatorFactory;
protected CteDeleteHandler(
CteTable cteTable,
SqmDeleteStatement sqmDeleteStatement,
DomainParameterXref domainParameterXref,
CteBasedMutationStrategy strategy,
HandlerCreationContext creationContext) {
super( cteTable, sqmDeleteStatement, domainParameterXref, strategy, creationContext );
final SessionFactoryImplementor sessionFactory = creationContext.getSessionFactory();
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
}
@Override
public SqmDeleteStatement getSqmDeleteOrUpdateStatement() {
return (SqmDeleteStatement) super.getSqmDeleteOrUpdateStatement();
}
@Override
public int execute(ExecutionContext executionContext) {
final List<Object> ids = selectMatchingIds( executionContext );
if ( ids == null || ids.isEmpty() ) {
return 0;
}
final QuerySpec cteQuerySpec = getCteTable().createCteSubQuery( executionContext );
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( getDomainParameterXref().getQueryParameterCount() );
final QuerySpec cteDefinitionQuerySpec = getCteTable().createCteDefinition(
ids,
jdbcParameterBindings,
executionContext
);
// for every table to be deleted, create the CteStatement and execute it
getEntityDescriptor().visitAttributeMappings(
attribute -> {
if ( attribute instanceof PluralAttributeMapping ) {
final PluralAttributeMapping pluralAttribute = (PluralAttributeMapping) attribute;
if ( pluralAttribute.getSeparateCollectionTable() != null ) {
// this collection has a separate collection table, meaning it is one of:
// 1) element-collection
// 2) many-to-many
// 3) one-to many using a dedicated join-table
//
// in all of these cases, we should clean up the matching rows in the
// collection table
executeDelete(
cteDefinitionQuerySpec,
pluralAttribute.getSeparateCollectionTable(),
() -> columnConsumer -> pluralAttribute.getKeyDescriptor().visitReferringColumns( columnConsumer ),
pluralAttribute.getKeyDescriptor(),
cteQuerySpec,
jdbcParameterBindings,
executionContext
);
}
}
}
);
getEntityDescriptor().visitConstraintOrderedTables(
(tableExpression, tableColumnsVisitationSupplier) -> {
executeDelete(
cteDefinitionQuerySpec,
tableExpression,
tableColumnsVisitationSupplier,
getEntityDescriptor().getIdentifierMapping(),
cteQuerySpec,
jdbcParameterBindings,
executionContext
);
}
);
return ids.size();
}
private List<Object> selectMatchingIds(ExecutionContext executionContext) {
return SqmMutationStrategyHelper.selectMatchingIds(
getSqmDeleteOrUpdateStatement(),
getDomainParameterXref(),
executionContext
);
}
protected void executeDelete(
QuerySpec cteDefinition,
String targetTable,
Supplier<Consumer<ColumnConsumer>> columnsToMatchVisitationSupplier,
MappingModelExpressable cteType,
QuerySpec cteSubQuery,
JdbcParameterBindings jdbcParameterBindings,
ExecutionContext executionContext) {
final CteStatement cteStatement = generateCteStatement(
cteDefinition,
targetTable,
columnsToMatchVisitationSupplier,
cteType,
cteSubQuery,
executionContext
);
final SessionFactoryImplementor sessionFactory = getCreationContext().getSessionFactory();
final JdbcDelete jdbcDelete = sqlAstTranslatorFactory.buildDeleteTranslator( sessionFactory )
.translate( cteStatement );
final LogicalConnectionImplementor logicalConnection = executionContext.getSession()
.getJdbcCoordinator()
.getLogicalConnection();
sessionFactory.getJdbcServices().getJdbcDeleteExecutor().execute(
jdbcDelete,
jdbcParameterBindings,
sql -> {
try {
return logicalConnection.getPhysicalConnection().prepareStatement( sql );
}
catch (SQLException e) {
throw sessionFactory.getJdbcServices().getSqlExceptionHelper().convert(
e,
"Error performing DELETE",
sql
);
}
},
(integer, preparedStatement) -> {},
executionContext
);
}
protected CteStatement generateCteStatement(
QuerySpec cteDefinition,
String targetTable,
Supplier<Consumer<ColumnConsumer>> columnsToMatchVisitationSupplier,
MappingModelExpressable cteType,
QuerySpec cteSubQuery,
ExecutionContext executionContext) {
final DeleteStatement deleteStatement = generateCteConsumer(
targetTable,
columnsToMatchVisitationSupplier,
cteType,
cteSubQuery,
executionContext
);
return new CteStatement(
cteDefinition,
CteBasedMutationStrategy.TABLE_NAME,
getCteTable(),
deleteStatement
);
}
private DeleteStatement generateCteConsumer(
String targetTable,
Supplier<Consumer<ColumnConsumer>> columnsToMatchVisitationSupplier,
MappingModelExpressable cteType,
QuerySpec cteSubQuery,
ExecutionContext executionContext) {
final SessionFactoryImplementor sessionFactory = executionContext.getSession().getFactory();
final TableReference targetTableReference = new TableReference(
targetTable,
null,
false,
sessionFactory
);
final List<ColumnReference> columnsToMatchReferences = new ArrayList<>();
columnsToMatchVisitationSupplier.get().accept(
(columnExpression, containingTableExpression, jdbcMapping) -> {
columnsToMatchReferences.add(
new ColumnReference(
targetTableReference,
columnExpression,
jdbcMapping,
sessionFactory
)
);
}
);
final Expression columnsToMatchExpression;
if ( columnsToMatchReferences.size() == 1 ) {
columnsToMatchExpression = columnsToMatchReferences.get( 0 );
}
else {
columnsToMatchExpression = new SqlTuple( columnsToMatchReferences, cteType );
}
return new DeleteStatement(
targetTableReference,
new InSubQueryPredicate(
columnsToMatchExpression,
cteSubQuery,
false
)
);
}
}

View File

@ -0,0 +1,40 @@
/*
* 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.NotYetImplementedFor6Exception;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext;
import org.hibernate.query.sqm.mutation.spi.UpdateHandler;
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
import org.hibernate.sql.ast.tree.cte.CteTable;
import org.hibernate.sql.exec.spi.ExecutionContext;
/**
*
* @author Evandro Pires da Silva
* @author Vlad Mihalcea
* @author Steve Ebersole
*/
public class CteUpdateHandler
extends AbstractCteMutationHandler
implements UpdateHandler {
public CteUpdateHandler(
CteTable cteTable,
SqmUpdateStatement sqmStatement,
DomainParameterXref domainParameterXref,
CteBasedMutationStrategy strategy,
HandlerCreationContext creationContext) {
super( cteTable, sqmStatement, domainParameterXref, strategy, creationContext );
}
@Override
public int execute(ExecutionContext executionContext) {
throw new NotYetImplementedFor6Exception();
}
}

View File

@ -0,0 +1,342 @@
/*
* 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.Collections;
import java.util.function.Function;
import java.util.function.Supplier;
import org.hibernate.LockMode;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.query.ComparisonOperator;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.internal.SqmUtil;
import org.hibernate.query.sqm.mutation.internal.SqmIdSelectGenerator;
import org.hibernate.query.sqm.mutation.spi.AbstractMutationHandler;
import org.hibernate.query.sqm.mutation.spi.Handler;
import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext;
import org.hibernate.query.sqm.sql.SqmQuerySpecTranslation;
import org.hibernate.query.sqm.sql.SqmSelectTranslator;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.query.sqm.tree.select.SqmQuerySpec;
import org.hibernate.query.sqm.tree.select.SqmSelection;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstInsertSelectTranslator;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.from.StandardTableGroup;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
import org.hibernate.sql.ast.tree.select.QuerySpec;
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.results.internal.SqlSelectionImpl;
import org.hibernate.type.UUIDCharType;
/**
* Support for {@link Handler} implementations
*
* @author Steve Ebersole
*/
public abstract class AbstractTableBasedHandler extends AbstractMutationHandler {
private final IdTable idTable;
private final TempTableDdlTransactionHandling ddlTransactionHandling;
private final BeforeUseAction beforeUseAction;
private final AfterUseAction afterUseAction;
private final DomainParameterXref domainParameterXref;
private final Function<SharedSessionContractImplementor,String> sessionUidAccess;
private final Supplier<IdTableExporter> exporterSupplier;
public AbstractTableBasedHandler(
SqmDeleteOrUpdateStatement sqmDeleteOrUpdateStatement,
IdTable idTable,
TempTableDdlTransactionHandling ddlTransactionHandling,
DomainParameterXref domainParameterXref,
BeforeUseAction beforeUseAction,
AfterUseAction afterUseAction,
Function<SharedSessionContractImplementor,String> sessionUidAccess,
Supplier<IdTableExporter> exporterSupplier,
HandlerCreationContext creationContext) {
super( sqmDeleteOrUpdateStatement, creationContext );
this.idTable = idTable;
this.ddlTransactionHandling = ddlTransactionHandling;
this.beforeUseAction = beforeUseAction;
this.afterUseAction = afterUseAction;
this.domainParameterXref = domainParameterXref;
this.sessionUidAccess = sessionUidAccess;
this.exporterSupplier = exporterSupplier;
}
public IdTable getIdTable() {
return idTable;
}
public DomainParameterXref getDomainParameterXref() {
return domainParameterXref;
}
public BeforeUseAction getBeforeUseAction() {
return beforeUseAction;
}
public AfterUseAction getAfterUseAction() {
return afterUseAction;
}
public Function<SharedSessionContractImplementor, String> getSessionUidAccess() {
return sessionUidAccess;
}
public Supplier<IdTableExporter> getExporterSupplier() {
return exporterSupplier;
}
@Override
public int execute(ExecutionContext executionContext) {
// In general:
// 1) prepare for use - this is completely a subclass hook
// 2) perform execution
// 3) release after use - again, completely a subclass hook
beforeExecution( executionContext );
try {
return performExecution( executionContext );
}
finally {
afterExecution( executionContext );
}
}
/**
* Allow subclasses a chance to perform any preliminary work they need
* to perform prior to execution
*/
protected void beforeExecution(ExecutionContext executionContext) {
}
/**
* Allow subclasses a chance to perform any clean-up work they need
* to perform after execution
*/
protected void afterExecution(ExecutionContext executionContext) {
}
protected int performExecution(ExecutionContext executionContext) {
performBeforeUseActions( executionContext );
try {
// 1) save the matching ids into the id table
final int affectedRowCount = saveMatchingIdsIntoIdTable( executionContext );
// 2) perform the actual individual update or deletes, using
// inclusion in the id-table as restriction
performMutations( executionContext );
return affectedRowCount;
}
finally {
performAfterUseActions( executionContext );
}
}
private void performBeforeUseActions(ExecutionContext executionContext) {
if ( getBeforeUseAction() == BeforeUseAction.CREATE ) {
IdTableHelper.createIdTable( idTable, getExporterSupplier().get(), ddlTransactionHandling, executionContext.getSession() );
}
}
private void performAfterUseActions(ExecutionContext executionContext) {
if ( getAfterUseAction() == AfterUseAction.CLEAN ) {
IdTableHelper.cleanIdTableRows(
idTable,
getExporterSupplier().get(),
sessionUidAccess,
executionContext.getSession()
);
}
else if ( getAfterUseAction() == AfterUseAction.DROP ) {
IdTableHelper.dropIdTable(
idTable,
getExporterSupplier().get(),
ddlTransactionHandling,
executionContext.getSession()
);
}
}
protected int saveMatchingIdsIntoIdTable(ExecutionContext executionContext) {
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
final SqmQuerySpec sqmIdSelect = SqmIdSelectGenerator.generateSqmEntityIdSelect(
getSqmDeleteOrUpdateStatement(),
executionContext,
factory
);
if ( getIdTable().getSessionUidColumn() != null ) {
//noinspection unchecked
sqmIdSelect.getSelectClause().add(
new SqmSelection(
new SqmLiteral(
executionContext.getSession().getSessionIdentifier().toString(),
UUIDCharType.INSTANCE,
executionContext.getSession().getFactory().getNodeBuilder()
),
null,
executionContext.getSession().getFactory().getNodeBuilder()
)
);
}
final SqmTranslatorFactory sqmTranslatorFactory = factory.getQueryEngine().getSqmTranslatorFactory();
final SqmSelectTranslator sqmTranslator = sqmTranslatorFactory.createSelectTranslator(
QueryOptions.NONE,
domainParameterXref,
executionContext.getQueryParameterBindings(),
executionContext.getSession().getLoadQueryInfluencers(),
factory
);
final SqmQuerySpecTranslation sqmIdSelectTranslation = sqmTranslator.translate( sqmIdSelect );
final InsertSelectStatement insertSelectStatement = new InsertSelectStatement();
final TableReference idTableReference = new TableReference( idTable.getTableExpression(), null, false, factory );
insertSelectStatement.setTargetTable( idTableReference );
insertSelectStatement.setSourceSelectStatement( sqmIdSelectTranslation.getSqlAst() );
final JdbcServices jdbcServices = factory.getJdbcServices();
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
final SqlAstInsertSelectTranslator sqlAstTranslator = sqlAstTranslatorFactory.buildInsertTranslator( factory );
final JdbcInsert jdbcInsert = sqlAstTranslator.translate( insertSelectStatement );
final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(
executionContext.getQueryParameterBindings(),
domainParameterXref,
SqmUtil.generateJdbcParamsXref(
domainParameterXref,
sqmIdSelectTranslation::getJdbcParamsBySqmParam
),
sqmTranslator,
executionContext.getSession()
);
return jdbcServices.getJdbcInsertExecutor().execute(
jdbcInsert,
jdbcParameterBindings,
sql -> executionContext.getSession()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sql ),
(integer, preparedStatement) -> {
},
executionContext
);
}
public QuerySpec createIdTableSubQuery(ExecutionContext executionContext) {
final QuerySpec querySpec = new QuerySpec( false );
final TableReference idTableReference = new TableReference(
idTable.getTableExpression(),
null,
true,
executionContext.getSession().getFactory()
);
final TableGroup idTableGroup = new StandardTableGroup(
new NavigablePath( idTableReference.getTableExpression() ),
getEntityDescriptor(),
LockMode.NONE,
idTableReference,
Collections.emptyList(),
null,
executionContext.getSession().getFactory()
);
querySpec.getFromClause().addRoot( idTableGroup );
applySelections( querySpec, idTableReference, executionContext );
applyRestrictions( querySpec, idTableReference, executionContext );
return querySpec;
}
private void applySelections(
QuerySpec querySpec,
TableReference tableReference,
ExecutionContext executionContext) {
for ( int i = 0; i < idTable.getIdTableColumns().size(); i++ ) {
final IdTableColumn idTableColumn = idTable.getIdTableColumns().get( i );
if ( idTableColumn != idTable.getSessionUidColumn() ) {
querySpec.getSelectClause().addSqlSelection(
new SqlSelectionImpl(
i+1,
i,
new ColumnReference(
tableReference,
idTableColumn.getColumnName(),
idTableColumn.getJdbcMapping(),
executionContext.getSession().getFactory()
),
idTableColumn.getJdbcMapping()
)
);
}
}
}
private void applyRestrictions(
QuerySpec querySpec,
TableReference idTableReference,
ExecutionContext executionContext) {
if ( idTable.getSessionUidColumn() != null ) {
querySpec.applyPredicate(
new ComparisonPredicate(
new ColumnReference(
idTableReference,
idTable.getSessionUidColumn().getColumnName(),
idTable.getSessionUidColumn().getJdbcMapping(),
executionContext.getSession().getFactory()
),
ComparisonOperator.EQUAL,
new QueryLiteral(
sessionUidAccess.apply( executionContext.getSession() ),
UUIDCharType.INSTANCE,
Clause.WHERE
)
)
);
}
}
protected abstract void performMutations(ExecutionContext executionContext);
}

View File

@ -0,0 +1,18 @@
/*
* 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;
/**
* Actions to perform in regards to an id-table after each use.
*
* @author Steve Ebersole
*/
public enum AfterUseAction {
CLEAN,
DROP,
NONE
}

View File

@ -0,0 +1,17 @@
/*
* 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;
/**
* Actions to perform in regards to an id-table prior to each use.
*
* @author Steve Ebersole
*/
public enum BeforeUseAction {
CREATE,
NONE
}

View File

@ -0,0 +1,83 @@
/*
* 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.List;
import java.util.function.Function;
import org.hibernate.boot.model.relational.Exportable;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.Queryable;
import org.hibernate.persister.entity.Joinable;
/**
* @author Steve Ebersole
*/
public class IdTable implements Exportable {
private final EntityMappingType entityDescriptor;
private final String qualifiedTableName;
private IdTableSessionUidColumn sessionUidColumn;
private final List<IdTableColumn> columns = new ArrayList<>();
public IdTable(
EntityMappingType entityDescriptor,
Function<String,String> idTableNameAdjuster) {
this.entityDescriptor = entityDescriptor;
this.qualifiedTableName = idTableNameAdjuster.apply(
( (Joinable) entityDescriptor.getEntityPersister() ).getTableName()
);
entityDescriptor.getIdentifierMapping().visitColumns(
(columnExpression, containingTableExpression, jdbcMapping) -> columns.add(
new IdTableColumn(
this,
columnExpression,
jdbcMapping
)
)
);
}
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;
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Exportable
@Override
public String getExportIdentifier() {
return getQualifiedTableName();
}
}

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.idtable;
import org.hibernate.metamodel.mapping.JdbcMapping;
/**
* A column in a IdTable. As these columns mirror the entity id columns, we know a few things about it inherently,
* such as being non-nullable
*
* @author Steve Ebersole
*/
public class IdTableColumn {
private final IdTable containingTable;
private final String columnName;
private final JdbcMapping jdbcMapping;
public IdTableColumn(
IdTable containingTable,
String columnName,
JdbcMapping jdbcMapping) {
this.containingTable = containingTable;
this.columnName = columnName;
this.jdbcMapping = jdbcMapping;
}
public IdTable getContainingTable() {
return containingTable;
}
public String getColumnName() {
return columnName;
}
public JdbcMapping getJdbcMapping() {
return jdbcMapping;
}
public String getDefaultValue() {
return null;
}
public String getSqlTypeDefinition() {
// todo (6.0) : this really ought to get the type def from the persister
return null;
}
}

View File

@ -0,0 +1,25 @@
/*
* 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
*/
public interface IdTableExporter {
String getSqlCreateCommand(IdTable idTable);
String getSqlDropCommand(IdTable idTable);
String getSqlTruncateCommand(
IdTable idTable,
Function<SharedSessionContractImplementor, String> sessionUidAccess,
SharedSessionContractImplementor session);
}

View File

@ -0,0 +1,235 @@
/*
* 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.PreparedStatement;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.function.Function;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.engine.jdbc.internal.FormatStyle;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
import org.hibernate.engine.jdbc.spi.SqlStatementLogger;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.jdbc.AbstractWork;
/**
* @author Steve Ebersole
*/
public class IdTableHelper {
private final static CoreMessageLogger log = CoreLogging.messageLogger( IdTableHelper.class );
public static final String SESSION_ID_COLUMN_NAME = "hib_sess_id";
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Creation
public static void createIdTable(
IdTable idTable,
IdTableExporter exporter,
TempTableDdlTransactionHandling ddlTransactionHandling,
SharedSessionContractImplementor session) {
executeWork(
new IdTableCreationWork( idTable, exporter, session ),
ddlTransactionHandling,
session
);
}
private static class IdTableCreationWork extends AbstractWork {
private final IdTable idTable;
private final IdTableExporter exporter;
private final SharedSessionContractImplementor session;
IdTableCreationWork(
IdTable idTable,
IdTableExporter exporter,
SharedSessionContractImplementor session) {
this.idTable = idTable;
this.exporter = exporter;
this.session = session;
}
@Override
public void execute(Connection connection) {
final JdbcServices jdbcServices = session.getFactory().getJdbcServices();
try {
final String creationCommand = exporter.getSqlCreateCommand( idTable );
logStatement( creationCommand, jdbcServices );
try (Statement statement = connection.createStatement()) {
statement.executeUpdate( creationCommand );
jdbcServices.getSqlExceptionHelper().handleAndClearWarnings( statement, WARNING_HANDLER );
}
catch (SQLException e) {
log.debugf(
"unable to create id table [%s]; `%s` failed : %s",
idTable.getQualifiedTableName(),
creationCommand,
e.getMessage()
);
}
}
catch( Exception e ) {
log.debugf( "Error creating id table(s) : %s", e.getMessage() );
}
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Drop
public static void dropIdTable(
IdTable idTable,
IdTableExporter exporter,
TempTableDdlTransactionHandling ddlTransactionHandling,
SharedSessionContractImplementor session) {
executeWork(
new IdTableDropWork( idTable, exporter, session ),
ddlTransactionHandling,
session
);
}
private static class IdTableDropWork extends AbstractWork {
private final IdTable idTable;
private final IdTableExporter exporter;
private final SharedSessionContractImplementor session;
IdTableDropWork(
IdTable idTable,
IdTableExporter exporter,
SharedSessionContractImplementor session) {
this.idTable = idTable;
this.exporter = exporter;
this.session = session;
}
@Override
public void execute(Connection connection) {
final JdbcServices jdbcServices = session.getFactory().getJdbcServices();
try {
final String dropCommand = exporter.getSqlCreateCommand( idTable );
logStatement( dropCommand, jdbcServices );
try (Statement statement = connection.createStatement()) {
statement.executeUpdate( dropCommand );
jdbcServices.getSqlExceptionHelper().handleAndClearWarnings( statement, WARNING_HANDLER );
}
catch (SQLException e) {
log.debugf(
"unable to drop id table [%s]; `%s` failed : %s",
idTable.getQualifiedTableName(),
dropCommand,
e.getMessage()
);
}
}
catch( Exception e ) {
log.debugf( "Error dropping id table(s) : %s", e.getMessage() );
}
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Clean
public static void cleanIdTableRows(
IdTable idTable,
IdTableExporter exporter,
Function<SharedSessionContractImplementor,String> sessionUidAccess,
SharedSessionContractImplementor session) {
PreparedStatement ps = null;
try {
final String sql = exporter.getSqlTruncateCommand( idTable, sessionUidAccess, session );
ps = session.getJdbcCoordinator().getStatementPreparer().prepareStatement( sql, false );
if ( idTable.getSessionUidColumn() != null ) {
final String sessionUid = sessionUidAccess.apply( session );
ps.setString( 1, sessionUid );
}
session.getJdbcCoordinator().getResultSetReturn().executeUpdate( ps );
}
catch( Throwable t ) {
log.unableToCleanupTemporaryIdTable(t);
}
finally {
if ( ps != null ) {
try {
session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( ps );
}
catch( Throwable ignore ) {
// ignore
}
}
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Misc
private static void executeWork(
AbstractWork work,
TempTableDdlTransactionHandling ddlTransactionHandling,
SharedSessionContractImplementor session) {
if ( ddlTransactionHandling == TempTableDdlTransactionHandling.NONE ) {
// simply execute the work using a Connection obtained from JdbcConnectionAccess
//
// NOTE : we do not (potentially) release the Connection here
// via LogicalConnectionImplementor#afterStatement because
// for sure we will be immediately using it again to
// populate the id table and use it...
try {
work.execute( session.getJdbcCoordinator().getLogicalConnection().getPhysicalConnection() );
}
catch (SQLException e) {
log.error( "Unable to use JDBC Connection to create perform id table management", e );
}
}
else {
session.getTransactionCoordinator()
.createIsolationDelegate()
.delegateWork( work, ddlTransactionHandling == TempTableDdlTransactionHandling.ISOLATE_AND_TRANSACT );
}
}
private static SqlExceptionHelper.WarningHandler WARNING_HANDLER = new SqlExceptionHelper.WarningHandlerLoggingSupport() {
public boolean doProcess() {
return log.isDebugEnabled();
}
public void prepare(SQLWarning warning) {
log.warningsCreatingTempTable( warning );
}
@Override
protected void logWarning(String description, String message) {
log.debug( description );
log.debug( message );
}
};
private static void logStatement(String sql, JdbcServices jdbcServices) {
final SqlStatementLogger statementLogger = jdbcServices.getSqlStatementLogger();
statementLogger.logStatement( sql, FormatStyle.BASIC.getFormatter() );
}
}

View File

@ -0,0 +1,22 @@
/*
* 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 org.hibernate.type.UUIDCharType;
/**
* @author Steve Ebersole
*/
public class IdTableSessionUidColumn extends IdTableColumn {
public IdTableSessionUidColumn(IdTable containingTable) {
super(
containingTable,
IdTableHelper.SESSION_ID_COLUMN_NAME,
UUIDCharType.INSTANCE
);
}
}

View File

@ -6,7 +6,10 @@
*/
package org.hibernate.query.sqm.mutation.internal.idtable;
import java.util.function.Supplier;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.spi.DeleteHandler;
import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext;
@ -23,6 +26,34 @@ import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
public class LocalTemporaryTableStrategy implements SqmMultiTableMutationStrategy {
public static final String SHORT_NAME = "local_temporary";
private final IdTable idTable;
private final Supplier<IdTableExporter> idTableExporterAccess;
private final AfterUseAction afterUseAction;
private final TempTableDdlTransactionHandling ddlTransactionHandling;
public LocalTemporaryTableStrategy(
IdTable idTable,
Supplier<IdTableExporter> idTableExporterAccess,
AfterUseAction afterUseAction,
TempTableDdlTransactionHandling ddlTransactionHandling) {
this.idTable = idTable;
this.idTableExporterAccess = idTableExporterAccess;
this.afterUseAction = afterUseAction;
this.ddlTransactionHandling = ddlTransactionHandling;
}
public LocalTemporaryTableStrategy(
IdTable idTable,
AfterUseAction afterUseAction,
TempTableDdlTransactionHandling ddlTransactionHandling) {
this(
idTable,
() -> new TempIdTableExporter( true ),
afterUseAction,
ddlTransactionHandling
);
}
@Override
public UpdateHandler buildUpdateHandler(
SqmUpdateStatement sqmUpdateStatement,
@ -36,6 +67,30 @@ public class LocalTemporaryTableStrategy implements SqmMultiTableMutationStrateg
SqmDeleteStatement sqmDeleteStatement,
DomainParameterXref domainParameterXref,
HandlerCreationContext creationContext) {
throw new NotYetImplementedFor6Exception( getClass() );
if ( sqmDeleteStatement.getWhereClause() == null
|| sqmDeleteStatement.getWhereClause().getPredicate() == null ) {
// optimization - special handler not needing the temp table
return new UnrestrictedTableBasedDeleteHandler(
sqmDeleteStatement,
idTable,
ddlTransactionHandling,
domainParameterXref,
BeforeUseAction.NONE,
afterUseAction,
sessionContractImplementor -> null,
creationContext
);
}
return new TableBasedDeleteHandler(
sqmDeleteStatement,
idTable,
idTableExporterAccess,
BeforeUseAction.CREATE,
AfterUseAction.NONE,
ddlTransactionHandling,
domainParameterXref,
creationContext
);
}
}

View File

@ -0,0 +1,90 @@
/*
* 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.boot.Metadata;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices;
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

@ -0,0 +1,169 @@
/*
* 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.List;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.ColumnConsumer;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.spi.DeleteHandler;
import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
import org.hibernate.sql.ast.SqlAstDeleteTranslator;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcDelete;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
/**
* @author Steve Ebersole
*/
public class TableBasedDeleteHandler
extends AbstractTableBasedHandler
implements DeleteHandler {
public TableBasedDeleteHandler(
SqmDeleteStatement sqmDeleteStatement,
IdTable idTable,
Supplier<IdTableExporter> exporterSupplier,
BeforeUseAction beforeUseAction,
AfterUseAction afterUseAction,
TempTableDdlTransactionHandling transactionality,
DomainParameterXref domainParameterXref,
HandlerCreationContext creationContext) {
super(
sqmDeleteStatement,
idTable,
transactionality,
domainParameterXref,
beforeUseAction,
afterUseAction,
session -> session.getSessionIdentifier().toString(),
exporterSupplier,
creationContext
);
}
@Override
public SqmDeleteStatement getSqmDeleteOrUpdateStatement() {
return (SqmDeleteStatement) super.getSqmDeleteOrUpdateStatement();
}
@Override
protected void performMutations(ExecutionContext executionContext) {
// create the selection of "matching ids" from the id-table. this is used as the subquery in
// used to restrict the deletions from each table
final QuerySpec idTableSelectSubQuerySpec = createIdTableSubQuery( executionContext );
getEntityDescriptor().visitConstraintOrderedTables(
(tableExpression, tableKeyColumnsVisitationSupplier) -> {
deleteFrom( tableExpression, tableKeyColumnsVisitationSupplier, idTableSelectSubQuerySpec, executionContext );
}
);
}
private static class TableKeyExpressionCollector {
private final EntityMappingType entityMappingType;
public TableKeyExpressionCollector(EntityMappingType entityMappingType) {
this.entityMappingType = entityMappingType;
}
Expression firstColumnExpression;
List<Expression> collectedColumnExpressions;
void apply(ColumnReference columnReference) {
if ( firstColumnExpression == null ) {
firstColumnExpression = columnReference;
}
else if ( collectedColumnExpressions == null ) {
collectedColumnExpressions = new ArrayList<>();
collectedColumnExpressions.add( firstColumnExpression );
collectedColumnExpressions.add( columnReference );
}
else {
collectedColumnExpressions.add( columnReference );
}
}
Expression buildKeyExpression() {
if ( collectedColumnExpressions == null ) {
return firstColumnExpression;
}
return new SqlTuple( collectedColumnExpressions, entityMappingType.getIdentifierMapping() );
}
}
private void deleteFrom(
String tableExpression,
Supplier<Consumer<ColumnConsumer>> tableKeyColumnVisitationSupplier,
QuerySpec idTableSelectSubQuery,
ExecutionContext executionContext) {
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
final TableKeyExpressionCollector keyColumnCollector = new TableKeyExpressionCollector( getEntityDescriptor() );
tableKeyColumnVisitationSupplier.get().accept(
(columnExpression, containingTableExpression, jdbcMapping) -> {
assert containingTableExpression.equals( tableExpression );
keyColumnCollector.apply(
new ColumnReference(
(String) null,
columnExpression,
jdbcMapping,
factory
)
);
}
);
final InSubQueryPredicate predicate = new InSubQueryPredicate(
keyColumnCollector.buildKeyExpression(),
idTableSelectSubQuery,
false
);
final DeleteStatement deleteStatement = new DeleteStatement(
new TableReference( tableExpression, null, true, factory ),
predicate
);
final JdbcServices jdbcServices = factory.getJdbcServices();
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
final SqlAstDeleteTranslator sqlAstTranslator = sqlAstTranslatorFactory.buildDeleteTranslator( factory );
final JdbcDelete jdbcDelete = sqlAstTranslator.translate( deleteStatement );
jdbcServices.getJdbcDeleteExecutor().execute(
jdbcDelete,
JdbcParameterBindings.NO_BINDINGS,
sql -> executionContext.getSession()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sql ),
(integer, preparedStatement) -> {},
executionContext
);
}
}

View File

@ -0,0 +1,95 @@
/*
* 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;
public TempIdTableExporter() {
this( true );
}
public TempIdTableExporter(boolean isLocal) {
this.isLocal = isLocal;
}
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( ' ' );
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

@ -0,0 +1,96 @@
/*
* 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.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.spi.DeleteHandler;
import org.hibernate.query.sqm.mutation.spi.HandlerCreationContext;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
import org.hibernate.sql.ast.SqlAstDeleteTranslator;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcDelete;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
/**
* @author Steve Ebersole
*/
public class UnrestrictedTableBasedDeleteHandler extends TableBasedDeleteHandler implements DeleteHandler {
public UnrestrictedTableBasedDeleteHandler(
SqmDeleteStatement sqmDeleteStatement,
IdTable idTable,
TempTableDdlTransactionHandling ddlTransactionHandling,
DomainParameterXref domainParameterXref,
BeforeUseAction beforeUseAction,
AfterUseAction afterUseAction,
Function<SharedSessionContractImplementor, String> sessionUidAccess,
HandlerCreationContext creationContext) {
super(
sqmDeleteStatement,
idTable,
() -> null,
beforeUseAction,
afterUseAction,
ddlTransactionHandling,
domainParameterXref,
creationContext
);
}
@Override
public int execute(ExecutionContext executionContext) {
final AtomicInteger rows = new AtomicInteger();
getEntityDescriptor().visitConstraintOrderedTables(
(tableExpression, tableKeyColumnsVisitationSupplier) -> {
rows.set( deleteFrom( tableExpression, executionContext ) );
}
);
return rows.get();
}
private int deleteFrom(
String tableExpression,
ExecutionContext executionContext) {
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
final DeleteStatement deleteStatement = new DeleteStatement(
new TableReference( tableExpression, null, true, factory ),
null
);
final JdbcServices jdbcServices = factory.getJdbcServices();
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
final SqlAstDeleteTranslator sqlAstTranslator = sqlAstTranslatorFactory.buildDeleteTranslator( factory );
final JdbcDelete jdbcDelete = sqlAstTranslator.translate( deleteStatement );
return jdbcServices.getJdbcDeleteExecutor().execute(
jdbcDelete,
JdbcParameterBindings.NO_BINDINGS,
sql -> executionContext.getSession()
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( sql ),
(integer, preparedStatement) -> {},
executionContext
);
}
}

View File

@ -0,0 +1,21 @@
/*
* 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
*/
/**
* Support for multi-table SQM mutation (insert, update, delete) operations using
* a table to temporarily hold the matching ids. There are 3 forms:
*
* * {@link org.hibernate.query.sqm.mutation.internal.idtable.LocalTemporaryTableStrategy} uses
* local temp tables as defined by the SQL spec
* * {@link org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy} uses
* global temp tables as defined by the SQL spec
* * {@link org.hibernate.query.sqm.mutation.internal.idtable.PersistentTableStrategy} uses normal table
* managed by Hibernate.
*
* @asciidoc
*/
package org.hibernate.query.sqm.mutation.internal.idtable;

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.query.sqm.mutation.spi;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.sqm.tree.SqmDeleteOrUpdateStatement;
@ -27,7 +28,7 @@ public abstract class AbstractMutationHandler implements Handler {
return sqmDeleteOrUpdateStatement;
}
public EntityPersister getEntityDescriptor() {
public EntityMappingType getEntityDescriptor() {
final String entityName = sqmDeleteOrUpdateStatement.getTarget()
.getReferencedPathSource()
.getHibernateEntityName();

View File

@ -16,11 +16,11 @@ import org.hibernate.sql.exec.spi.JdbcParameter;
/**
* @author Steve Ebersole
*/
public class SimpleSqmDeleteInterpretation implements SqmInterpretation {
public class SimpleSqmDeleteTranslation implements SqmTranslation {
private final DeleteStatement sqlAst;
private final Map<SqmParameter, List<JdbcParameter>> jdbcParamMap;
public SimpleSqmDeleteInterpretation(
public SimpleSqmDeleteTranslation(
DeleteStatement sqlAst,
Map<SqmParameter, List<JdbcParameter>> jdbcParamMap) {
this.sqlAst = sqlAst;

View File

@ -12,6 +12,6 @@ import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
/**
* @author Steve Ebersole
*/
public interface SimpleSqmDeleteToSqlAstConverter extends SqmToSqlAstConverter, JdbcParameterBySqmParameterAccess {
SimpleSqmDeleteInterpretation interpret(SqmDeleteStatement statement);
public interface SimpleSqmDeleteTranslator extends SqmToSqlAstConverter, JdbcParameterBySqmParameterAccess {
SimpleSqmDeleteTranslation translate(SqmDeleteStatement statement);
}

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.sql;
import java.util.List;
import java.util.Map;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.exec.spi.JdbcParameter;
/**
* @author Steve Ebersole
*/
public class SqmInsertSelectTranslation {
private final InsertSelectStatement sqlAst;
private final Map<SqmParameter, List<JdbcParameter>> jdbcParamMap;
public SqmInsertSelectTranslation(
InsertSelectStatement sqlAst,
Map<SqmParameter, List<JdbcParameter>> jdbcParamMap) {
this.sqlAst = sqlAst;
this.jdbcParamMap = jdbcParamMap;
}
public InsertSelectStatement getSqlAst() {
return sqlAst;
}
public Map<SqmParameter, List<JdbcParameter>> getJdbcParamMap() {
return jdbcParamMap;
}
}

View File

@ -0,0 +1,17 @@
/*
* 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.sql;
import org.hibernate.query.sqm.spi.JdbcParameterBySqmParameterAccess;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
/**
* @author Steve Ebersole
*/
public interface SqmInsertSelectTranslator extends SqmToSqlAstConverter, JdbcParameterBySqmParameterAccess {
SqmInsertSelectTranslation translate(SqmInsertSelectStatement statement);
}

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.sql;
import java.util.List;
import java.util.Map;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.exec.spi.JdbcParameter;
/**
* @author Steve Ebersole
*/
public class SqmQuerySpecTranslation {
private final QuerySpec sqlAst;
private final Map<SqmParameter, List<JdbcParameter>> jdbcParamsBySqmParam;
public SqmQuerySpecTranslation(
QuerySpec sqlAst,
Map<SqmParameter, List<JdbcParameter>> jdbcParamsBySqmParam) {
this.sqlAst = sqlAst;
this.jdbcParamsBySqmParam = jdbcParamsBySqmParam;
}
public QuerySpec getSqlAst() {
return sqlAst;
}
public Map<SqmParameter, List<JdbcParameter>> getJdbcParamsBySqmParam() {
return jdbcParamsBySqmParam;
}
}

View File

@ -9,7 +9,7 @@ package org.hibernate.query.sqm.sql;
import java.util.List;
import java.util.Map;
import org.hibernate.query.sqm.sql.internal.StandardSqmSelectToSqlAstConverter;
import org.hibernate.query.sqm.sql.internal.StandardSqmSelectTranslator;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.exec.spi.JdbcParameter;
@ -17,15 +17,15 @@ import org.hibernate.sql.exec.spi.JdbcParameter;
/**
* Details of the result of interpreting an SQM SELECT AST into a SQL SELECT AST
*
* @see StandardSqmSelectToSqlAstConverter#interpret(org.hibernate.query.sqm.tree.select.SqmSelectStatement)
* @see StandardSqmSelectTranslator#translate(org.hibernate.query.sqm.tree.select.SqmSelectStatement)
*
* @author Steve Ebersole
*/
public class SqmSelectInterpretation implements SqmInterpretation {
public class SqmSelectTranslation implements SqmTranslation {
private final SelectStatement sqlAst;
private final Map<SqmParameter,List<JdbcParameter>> jdbcParamsBySqmParam;
public SqmSelectInterpretation(
public SqmSelectTranslation(
SelectStatement sqlAst,
Map<SqmParameter, List<JdbcParameter>> jdbcParamsBySqmParam) {
this.sqlAst = sqlAst;

View File

@ -7,11 +7,13 @@
package org.hibernate.query.sqm.sql;
import org.hibernate.query.sqm.spi.JdbcParameterBySqmParameterAccess;
import org.hibernate.query.sqm.tree.select.SqmQuerySpec;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
/**
* @author Steve Ebersole
*/
public interface SqmSelectToSqlAstConverter extends SqmToSqlAstConverter, JdbcParameterBySqmParameterAccess {
SqmSelectInterpretation interpret(SqmSelectStatement statement);
public interface SqmSelectTranslator extends SqmToSqlAstConverter, JdbcParameterBySqmParameterAccess {
SqmSelectTranslation translate(SqmSelectStatement sqmStatement);
SqmQuerySpecTranslation translate(SqmQuerySpec sqmQuerySpec);
}

View File

@ -18,7 +18,7 @@ import org.hibernate.sql.exec.spi.JdbcParameter;
*
* @author Steve Ebersole
*/
public interface SqmInterpretation {
public interface SqmTranslation {
Statement getSqlAst();
Map<SqmParameter, List<JdbcParameter>> getJdbcParamsBySqmParam();
}

View File

@ -17,14 +17,21 @@ import org.hibernate.sql.ast.spi.SqlAstCreationContext;
* @author Steve Ebersole
*/
public interface SqmTranslatorFactory {
SqmSelectToSqlAstConverter createSelectConverter(
SqmSelectTranslator createSelectTranslator(
QueryOptions queryOptions,
DomainParameterXref domainParameterXref,
QueryParameterBindings domainParameterBindings,
LoadQueryInfluencers influencers,
SqlAstCreationContext creationContext);
SimpleSqmDeleteToSqlAstConverter createSimpleDeleteConverter(
SimpleSqmDeleteTranslator createSimpleDeleteTranslator(
QueryOptions queryOptions,
DomainParameterXref domainParameterXref,
QueryParameterBindings domainParameterBindings,
LoadQueryInfluencers influencers,
SqlAstCreationContext creationContext);
SqmInsertSelectTranslator createInsertSelectTranslator(
QueryOptions queryOptions,
DomainParameterXref domainParameterXref,
QueryParameterBindings domainParameterBindings,

View File

@ -8,14 +8,15 @@ package org.hibernate.query.sqm.sql.internal;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter;
import org.hibernate.query.sqm.sql.SimpleSqmDeleteInterpretation;
import org.hibernate.query.sqm.sql.SimpleSqmDeleteToSqlAstConverter;
import org.hibernate.query.sqm.sql.SimpleSqmDeleteTranslation;
import org.hibernate.query.sqm.sql.SimpleSqmDeleteTranslator;
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
import org.hibernate.query.sqm.tree.predicate.SqmWhereClause;
import org.hibernate.sql.ast.Clause;
@ -25,15 +26,22 @@ import org.hibernate.sql.ast.spi.SqlAstTreeHelper;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.exec.spi.JdbcDelete;
/**
* @author Steve Ebersole
*/
public class StandardSqmDeleteToSqlAstConverter
public class StandardSqmDeleteTranslator
extends BaseSqmToSqlAstConverter
implements SimpleSqmDeleteToSqlAstConverter {
implements SimpleSqmDeleteTranslator {
public StandardSqmDeleteToSqlAstConverter(
public static JdbcDelete translate(
SqmDeleteStatement statement,
SessionFactoryImplementor factory) {
return null;
}
public StandardSqmDeleteTranslator(
SqlAstCreationContext creationContext,
QueryOptions queryOptions,
DomainParameterXref domainParameterXref,
@ -42,9 +50,9 @@ public class StandardSqmDeleteToSqlAstConverter
}
@Override
public SimpleSqmDeleteInterpretation interpret(SqmDeleteStatement statement) {
public SimpleSqmDeleteTranslation translate(SqmDeleteStatement statement) {
final DeleteStatement deleteStatement = visitDeleteStatement( statement );
return new SimpleSqmDeleteInterpretation(
return new SimpleSqmDeleteTranslation(
deleteStatement,
getJdbcParamsBySqmParam()
);

View File

@ -0,0 +1,92 @@
/*
* 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.sql.internal;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter;
import org.hibernate.query.sqm.sql.SqmInsertSelectTranslation;
import org.hibernate.query.sqm.sql.SqmInsertSelectTranslator;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
import org.hibernate.sql.ast.JoinType;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
/**
* @author Steve Ebersole
*/
public class StandardSqmInsertSelectTranslator
extends BaseSqmToSqlAstConverter
implements SqmInsertSelectTranslator {
public StandardSqmInsertSelectTranslator(
SqlAstCreationContext creationContext,
QueryOptions queryOptions,
DomainParameterXref domainParameterXref,
QueryParameterBindings domainParameterBindings) {
super( creationContext, queryOptions, domainParameterXref, domainParameterBindings );
}
@Override
public SqmInsertSelectTranslation translate(SqmInsertSelectStatement sqmStatement) {
return new SqmInsertSelectTranslation( visitInsertSelectStatement( sqmStatement ), getJdbcParamsBySqmParam() );
}
@Override
public InsertSelectStatement visitInsertSelectStatement(SqmInsertSelectStatement sqmStatement) {
final InsertSelectStatement insertSelectStatement = new InsertSelectStatement();
final String entityName = sqmStatement.getTarget().getEntityName();
final EntityPersister entityDescriptor = getCreationContext().getDomainModel().getEntityDescriptor( entityName );
assert entityDescriptor != null;
getProcessingStateStack().push(
new SqlAstProcessingStateImpl(
null,
this,
getCurrentClauseStack()::getCurrent
)
);
try {
final NavigablePath rootPath = new NavigablePath( entityName );
final TableGroup rootTableGroup = entityDescriptor.createRootTableGroup(
rootPath,
null,
JoinType.LEFT,
LockMode.WRITE,
stem -> getSqlAliasBaseGenerator().createSqlAliasBase( stem ),
getSqlExpressionResolver(),
() -> predicate -> additionalRestrictions = predicate,
getCreationContext()
);
if ( ! rootTableGroup.getTableReferenceJoins().isEmpty()
|| rootTableGroup.getTableGroupJoins().isEmpty() ) {
throw new HibernateException( "Not expecting multiple table references for an SQM INSERT-SELECT" );
}
getFromClauseIndex().registerTableGroup( rootPath, rootTableGroup );
insertSelectStatement.setTargetTable( rootTableGroup.getPrimaryTableReference() );
insertSelectStatement.setSourceSelectStatement(
visitQuerySpec( sqmStatement.getSelectQuerySpec() )
);
return insertSelectStatement;
}
finally {
getProcessingStateStack().pop();
}
}
}

View File

@ -30,13 +30,15 @@ import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter;
import org.hibernate.query.sqm.sql.SqmSelectInterpretation;
import org.hibernate.query.sqm.sql.SqmSelectToSqlAstConverter;
import org.hibernate.query.sqm.sql.SqmQuerySpecTranslation;
import org.hibernate.query.sqm.sql.SqmSelectTranslation;
import org.hibernate.query.sqm.sql.SqmSelectTranslator;
import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType;
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiation;
import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiationArgument;
import org.hibernate.query.sqm.tree.select.SqmDynamicInstantiationTarget;
import org.hibernate.query.sqm.tree.select.SqmQuerySpec;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import org.hibernate.query.sqm.tree.select.SqmSelection;
import org.hibernate.sql.ast.JoinType;
@ -67,9 +69,9 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
* @author John O'Hara
*/
@SuppressWarnings("unchecked")
public class StandardSqmSelectToSqlAstConverter
public class StandardSqmSelectTranslator
extends BaseSqmToSqlAstConverter
implements DomainResultCreationState, SqmSelectToSqlAstConverter {
implements DomainResultCreationState, SqmSelectTranslator {
private final LoadQueryInfluencers fetchInfluencers;
private final CircularFetchDetector circularFetchDetector = new CircularFetchDetector();
@ -78,7 +80,7 @@ public class StandardSqmSelectToSqlAstConverter
private GraphImplementor<?> currentJpaGraphNode;
public StandardSqmSelectToSqlAstConverter(
public StandardSqmSelectTranslator(
QueryOptions queryOptions,
DomainParameterXref domainParameterXref,
QueryParameterBindings domainParameterBindings,
@ -95,9 +97,17 @@ public class StandardSqmSelectToSqlAstConverter
}
@Override
public SqmSelectInterpretation interpret(SqmSelectStatement statement) {
return new SqmSelectInterpretation(
visitSelectStatement( statement ),
public SqmSelectTranslation translate(SqmSelectStatement sqmStatement) {
return new SqmSelectTranslation(
visitSelectStatement( sqmStatement ),
getJdbcParamsBySqmParam()
);
}
@Override
public SqmQuerySpecTranslation translate(SqmQuerySpec querySpec) {
return new SqmQuerySpecTranslation(
visitQuerySpec( querySpec ),
getJdbcParamsBySqmParam()
);
}
@ -304,7 +314,7 @@ public class StandardSqmSelectToSqlAstConverter
joined,
lockMode,
alias,
StandardSqmSelectToSqlAstConverter.this
StandardSqmSelectTranslator.this
);
}
catch (RuntimeException e) {

View File

@ -390,6 +390,11 @@ public class SqmPolymorphicRootDescriptor<T> implements EntityDomainType<T> {
throw new UnsupportedOperationException( );
}
@Override
public SqmPathSource getIdentifierDescriptor() {
return null;
}
@Override
public <Y> SingularPersistentAttribute<? super T, Y> getId(Class<Y> type) {
throw new UnsupportedOperationException( );

View File

@ -8,6 +8,7 @@ package org.hibernate.sql.ast;
import org.hibernate.sql.ast.spi.SqlAstToJdbcOperationConverter;
import org.hibernate.sql.ast.spi.SqlSelectAstWalker;
import org.hibernate.sql.ast.tree.cte.CteStatement;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.exec.spi.JdbcDelete;
@ -16,4 +17,6 @@ import org.hibernate.sql.exec.spi.JdbcDelete;
*/
public interface SqlAstDeleteTranslator extends SqlSelectAstWalker, SqlAstToJdbcOperationConverter {
JdbcDelete translate(DeleteStatement sqlAst);
JdbcDelete translate(CteStatement cteStatement);
}

View File

@ -0,0 +1,18 @@
/*
* 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.sql.ast;
import org.hibernate.sql.ast.spi.SqlAstToJdbcOperationConverter;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.exec.spi.JdbcInsert;
/**
* @author Steve Ebersole
*/
public interface SqlAstInsertSelectTranslator extends SqlAstToJdbcOperationConverter {
JdbcInsert translate(InsertSelectStatement sqlAst);
}

View File

@ -20,10 +20,10 @@ public interface SqlAstSelectTranslator extends SqlSelectAstWalker, SqlAstToJdbc
/**
* Translate the SelectStatement into the executable JdbcSelect
*/
JdbcSelect interpret(SelectStatement selectStatement);
JdbcSelect translate(SelectStatement selectStatement);
/**
* Translate the QuerySpec into the executable JdbcSelect
*/
JdbcSelect interpret(QuerySpec querySpec);
JdbcSelect translate(QuerySpec querySpec);
}

View File

@ -9,11 +9,25 @@ package org.hibernate.sql.ast;
import org.hibernate.engine.spi.SessionFactoryImplementor;
/**
* Factory for obtaining single-use SQL AST translators
*
* @author Steve Ebersole
*/
public interface SqlAstTranslatorFactory {
SqlAstSelectTranslator buildSelectConverter(SessionFactoryImplementor sessionFactory);
SqlAstDeleteTranslator buildDeleteConverter(SessionFactoryImplementor sessionFactory);
/**
* Builds a single-use select translator
*/
SqlAstSelectTranslator buildSelectTranslator(SessionFactoryImplementor sessionFactory);
// todo (6.0) : update, delete, etc
/**
* Builds a single-use delete translator
*/
SqlAstDeleteTranslator buildDeleteTranslator(SessionFactoryImplementor sessionFactory);
/**
* Builds a single-use delete translator
*/
SqlAstInsertSelectTranslator buildInsertTranslator(SessionFactoryImplementor sessionFactory);
// todo (6.0) : update, insert, etc
}

View File

@ -17,11 +17,11 @@ import org.hibernate.sql.ast.tree.update.Assignment;
/**
* @author Steve Ebersole
*/
public class AbstractSqlAstToJdbcOperationConverter
public abstract class AbstractSqlAstToJdbcOperationConverter
extends AbstractSqlAstWalker
implements SqlAstToJdbcOperationConverter {
private final Set<String> affectedTableExpressions = new HashSet<>();
private final Set<String> affectedTableNames = new HashSet<>();
protected AbstractSqlAstToJdbcOperationConverter(SessionFactoryImplementor sessionFactory) {
super( sessionFactory );
@ -33,8 +33,8 @@ public class AbstractSqlAstToJdbcOperationConverter
}
@Override
public Set<String> getAffectedTableExpressions() {
return affectedTableExpressions;
public Set<String> getAffectedTableNames() {
return affectedTableNames;
}
@Override
@ -48,6 +48,6 @@ public class AbstractSqlAstToJdbcOperationConverter
}
protected void registerAffectedTable(String tableExpression) {
affectedTableExpressions.add( tableExpression );
affectedTableNames.add( tableExpression );
}
}

View File

@ -8,6 +8,7 @@ package org.hibernate.sql.ast.spi;
import java.util.Set;
import org.hibernate.sql.ast.tree.cte.CteStatement;
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
@ -18,5 +19,7 @@ import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
* @author Steve Ebersole
*/
public interface SqlAstToJdbcOperationConverter extends SqlAstWalker, SqlTypeDescriptorIndicators {
Set<String> getAffectedTableExpressions();
Set<String> getAffectedTableNames();
JdbcOperation translate(CteStatement cteStatement);
}

View File

@ -6,12 +6,13 @@
*/
package org.hibernate.sql.ast.spi;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.sql.ast.tree.cte.CteColumn;
import org.hibernate.sql.ast.SqlAstDeleteTranslator;
import org.hibernate.sql.ast.tree.cte.CteStatement;
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.exec.spi.JdbcDelete;
@ -20,9 +21,7 @@ import org.hibernate.sql.exec.spi.JdbcParameterBinder;
/**
* @author Steve Ebersole
*/
public class StandardSqlAstDeleteTranslator
extends AbstractSqlAstToJdbcOperationConverter
implements SqlAstDeleteTranslator {
public class StandardSqlAstDeleteTranslator extends AbstractSqlAstToJdbcOperationConverter implements SqlAstDeleteTranslator {
public StandardSqlAstDeleteTranslator(SessionFactoryImplementor sessionFactory) {
super( sessionFactory );
}
@ -50,7 +49,7 @@ public class StandardSqlAstDeleteTranslator
@Override
public Set<String> getAffectedTableNames() {
return getAffectedTableExpressions();
return getAffectedTableNames();
}
};
}
@ -60,4 +59,48 @@ public class StandardSqlAstDeleteTranslator
// generally we do not want to render the qualifier
appendSql( columnReference.getColumnExpression() );
}
@Override
public JdbcDelete translate(CteStatement sqlAst) {
assert sqlAst.getCteConsumer() instanceof DeleteStatement;
appendSql( "with " );
appendSql( sqlAst.getCteLabel() );
appendSql( " (" );
String separator = "";
for ( int i = 0; i < sqlAst.getCteTable().getCteColumns().size(); i++ ) {
final CteColumn cteColumn = sqlAst.getCteTable().getCteColumns().get( i );
appendSql( separator );
appendSql( cteColumn.getColumnExpression() );
separator = ", ";
}
appendSql( ") as (" );
visitQuerySpec( sqlAst.getCteDefinition() );
appendSql( ") " );
translate( (DeleteStatement) sqlAst.getCteConsumer() );
return new JdbcDelete() {
@Override
public String getSql() {
return StandardSqlAstDeleteTranslator.this.getSql();
}
@Override
public List<JdbcParameterBinder> getParameterBinders() {
return StandardSqlAstDeleteTranslator.this.getParameterBinders();
}
@Override
public Set<String> getAffectedTableNames() {
return StandardSqlAstDeleteTranslator.this.getAffectedTableNames();
}
};
}
}

View File

@ -0,0 +1,64 @@
/*
* 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.sql.ast.spi;
import java.util.List;
import java.util.Set;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.sql.ast.SqlAstInsertSelectTranslator;
import org.hibernate.sql.ast.tree.cte.CteStatement;
import org.hibernate.sql.ast.tree.insert.InsertSelectStatement;
import org.hibernate.sql.exec.spi.JdbcInsert;
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.exec.spi.JdbcParameterBinder;
/**
* @author Steve Ebersole
*/
public class StandardSqlAstInsertSelectTranslator
extends AbstractSqlAstToJdbcOperationConverter
implements SqlAstInsertSelectTranslator {
public StandardSqlAstInsertSelectTranslator(SessionFactoryImplementor sessionFactory) {
super( sessionFactory );
}
@Override
public JdbcInsert translate(InsertSelectStatement sqlAst) {
appendSql( "insert into " );
appendSql( sqlAst.getTargetTable().getTableExpression() );
appendSql( " " );
// todo (6.0) : for now we do not provide an explicit target columns (VALUES) list - we should...
// it works for our limited usage of insert-select
visitQuerySpec( sqlAst.getSourceSelectStatement() );
return new JdbcInsert() {
@Override
public String getSql() {
return StandardSqlAstInsertSelectTranslator.this.getSql();
}
@Override
public List<JdbcParameterBinder> getParameterBinders() {
return StandardSqlAstInsertSelectTranslator.this.getParameterBinders();
}
@Override
public Set<String> getAffectedTableNames() {
return StandardSqlAstInsertSelectTranslator.this.getAffectedTableNames();
}
};
}
@Override
public JdbcOperation translate(CteStatement cteStatement) {
throw new NotYetImplementedFor6Exception( getClass() );
}
}

View File

@ -8,12 +8,15 @@ package org.hibernate.sql.ast.spi;
import java.util.Collections;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.sql.ast.SqlAstSelectTranslator;
import org.hibernate.sql.ast.SqlTreePrinter;
import org.hibernate.sql.ast.tree.SqlAstTreeLogger;
import org.hibernate.sql.ast.tree.cte.CteStatement;
import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.exec.spi.JdbcSelect;
import org.hibernate.sql.results.internal.JdbcValuesMappingProducerStandard;
@ -28,13 +31,17 @@ public class StandardSqlAstSelectTranslator
extends AbstractSqlAstToJdbcOperationConverter
implements SqlAstSelectTranslator {
@SuppressWarnings("WeakerAccess")
public StandardSqlAstSelectTranslator(SessionFactoryImplementor sessionFactory) {
super( sessionFactory );
}
@Override
public JdbcSelect interpret(QuerySpec querySpec) {
public JdbcSelect translate(CteStatement cteStatement) {
throw new NotYetImplementedFor6Exception( getClass() );
}
@Override
public JdbcSelect translate(QuerySpec querySpec) {
visitQuerySpec( querySpec );
return new JdbcSelect(
@ -44,12 +51,12 @@ public class StandardSqlAstSelectTranslator
querySpec.getSelectClause().getSqlSelections(),
Collections.emptyList()
),
getAffectedTableExpressions()
getAffectedTableNames()
);
}
@Override
public JdbcSelect interpret(SelectStatement sqlAstSelect) {
public JdbcSelect translate(SelectStatement sqlAstSelect) {
if ( SqlAstTreeLogger.DEBUG_ENABLED ) {
SqlTreePrinter.print( sqlAstSelect );
}
@ -63,7 +70,7 @@ public class StandardSqlAstSelectTranslator
sqlAstSelect.getQuerySpec().getSelectClause().getSqlSelections(),
sqlAstSelect.getDomainResultDescriptors()
),
getAffectedTableExpressions()
getAffectedTableNames()
);
}

View File

@ -6,8 +6,10 @@
*/
package org.hibernate.sql.ast.spi;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.sql.ast.SqlAstDeleteTranslator;
import org.hibernate.sql.ast.SqlAstInsertSelectTranslator;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.SqlAstSelectTranslator;
@ -16,12 +18,17 @@ import org.hibernate.sql.ast.SqlAstSelectTranslator;
*/
public class StandardSqlAstTranslatorFactory implements SqlAstTranslatorFactory {
@Override
public SqlAstSelectTranslator buildSelectConverter(SessionFactoryImplementor sessionFactory) {
public SqlAstSelectTranslator buildSelectTranslator(SessionFactoryImplementor sessionFactory) {
return new StandardSqlAstSelectTranslator( sessionFactory);
}
@Override
public SqlAstDeleteTranslator buildDeleteConverter(SessionFactoryImplementor sessionFactory) {
public SqlAstDeleteTranslator buildDeleteTranslator(SessionFactoryImplementor sessionFactory) {
return new StandardSqlAstDeleteTranslator( sessionFactory );
}
@Override
public SqlAstInsertSelectTranslator buildInsertTranslator(SessionFactoryImplementor sessionFactory) {
return new StandardSqlAstInsertSelectTranslator( sessionFactory );
}
}

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.sql.ast.tree.cte;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.sql.ast.tree.cte.CteTable;
/**
* @author Steve Ebersole
*/
public class CteColumn {
private final CteTable cteTable;
private final String columnExpression;
private final JdbcMapping jdbcMapping;
public CteColumn(CteTable cteTable, String columnExpression, JdbcMapping jdbcMapping) {
this.cteTable = cteTable;
this.columnExpression = columnExpression;
this.jdbcMapping = jdbcMapping;
}
public CteTable getCteTable() {
return cteTable;
}
public String getColumnExpression() {
return columnExpression;
}
public JdbcMapping getJdbcMapping() {
return jdbcMapping;
}
}

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.sql.ast.tree.cte;
import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.select.QuerySpec;
/**
* A statement using a CTE
*
* @author Steve Ebersole
*/
public class CteStatement implements Statement {
private final String cteLabel;
private final CteTable cteTable;
private final QuerySpec cteDefinition;
private final Statement cteConsumer;
public CteStatement(QuerySpec cteDefinition, String cteLabel, CteTable cteTable, Statement cteConsumer) {
this.cteDefinition = cteDefinition;
this.cteLabel = cteLabel;
this.cteTable = cteTable;
this.cteConsumer = cteConsumer;
}
public String getCteLabel() {
return cteLabel;
}
public CteTable getCteTable() {
return cteTable;
}
public QuerySpec getCteDefinition() {
return cteDefinition;
}
public Statement getCteConsumer() {
return cteConsumer;
}
}

View File

@ -0,0 +1,193 @@
/*
* 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.sql.ast.tree.cte;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.hibernate.LockMode;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.mutation.internal.cte.CteBasedMutationStrategy;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.from.StandardTableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.sql.exec.internal.JdbcParameterImpl;
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcParameter;
import org.hibernate.sql.exec.spi.JdbcParameterBinding;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.results.internal.SqlSelectionImpl;
/**
* Describes the CTE and exposes ways to consume it
*
* @author Steve Ebersole
*/
public class CteTable {
private final EntityMappingType entityDescriptor;
private final SessionFactoryImplementor sessionFactory;
private final List<CteColumn> cteColumns;
public CteTable(EntityMappingType entityDescriptor, BootstrapContext bootstrapContext) {
this.entityDescriptor = entityDescriptor;
this.sessionFactory = entityDescriptor.getEntityPersister().getFactory();
final int numberOfColumns = entityDescriptor.getIdentifierMapping().getJdbcTypeCount( bootstrapContext.getTypeConfiguration() );
cteColumns = new ArrayList<>( numberOfColumns );
entityDescriptor.getIdentifierMapping().visitColumns(
(columnExpression, containingTableExpression, jdbcMapping) -> cteColumns.add(
new CteColumn(
this,
"cte_" + columnExpression,
jdbcMapping
)
)
);
}
public String getTableExpression() {
return CteBasedMutationStrategy.TABLE_NAME;
}
public List<CteColumn> getCteColumns() {
return cteColumns;
}
public QuerySpec createCteDefinition(
List<?> matchingIds,
JdbcParameterBindings jdbcParameterBindings,
ExecutionContext executionContext) {
final QuerySpec querySpec = new QuerySpec( false );
final TableReference tableValueConstructorReference = createCteDefinitionTableValueCtor(
matchingIds,
jdbcParameterBindings,
executionContext
);
final StandardTableGroup tableValueCtorGroup = new StandardTableGroup(
new NavigablePath( "cte" ),
null,
LockMode.NONE,
tableValueConstructorReference,
Collections.emptyList(),
null,
sessionFactory
);
querySpec.getFromClause().addRoot( tableValueCtorGroup );
applySelections( querySpec, tableValueConstructorReference );
return querySpec;
}
private TableReference createCteDefinitionTableValueCtor(
List<?> matchingIds,
JdbcParameterBindings jdbcParameterBindings,
ExecutionContext executionContext) {
// use `DerivedTable` as the TableValueConstructor
// so its `#expression` would be something like `values ( (a1, b1), (a2, b2), ... )`
final int numberOfColumns = getCteColumns().size();
final StringBuilder tableValueCtorExpressionBuffer = new StringBuilder( "values(" );
String rowSeparator = "";
int idProcessedCount = 0;
for ( Object matchingId : matchingIds ) {
tableValueCtorExpressionBuffer.append( rowSeparator );
tableValueCtorExpressionBuffer.append( '(' );
StringHelper.repeat( "?", numberOfColumns, ",", tableValueCtorExpressionBuffer );
tableValueCtorExpressionBuffer.append( ')' );
final int currentIdPosition = idProcessedCount;
entityDescriptor.getIdentifierMapping().visitJdbcValues(
matchingId,
Clause.IRRELEVANT,
(value, type) -> {
final JdbcParameter jdbcParameter = new JdbcParameterImpl( type );
JdbcParameterBinding jdbcParameterBinding = new JdbcParameterBinding() {
@Override
public JdbcMapping getBindType() {
return type;
}
@Override
public Object getBindValue() {
return value;
}
};
jdbcParameterBindings.addBinding(
jdbcParameter,
jdbcParameterBinding
);
},
executionContext.getSession()
);
rowSeparator = ", ";
idProcessedCount++;
}
tableValueCtorExpressionBuffer.append( ')' );
return new TableReference(
tableValueCtorExpressionBuffer.toString(),
CteBasedMutationStrategy.TABLE_NAME,
false,
sessionFactory
);
}
public QuerySpec createCteSubQuery(ExecutionContext executionContext) {
final QuerySpec querySpec = new QuerySpec( false );
final TableReference cteTableReference = new TableReference(
getTableExpression(),
null,
false,
sessionFactory
);
final CteTableGroup cteTableGroup = new CteTableGroup( cteTableReference );
querySpec.getFromClause().addRoot( cteTableGroup );
applySelections( querySpec, cteTableReference );
return querySpec;
}
private void applySelections(QuerySpec querySpec, TableReference tableReference) {
for ( int i = 0; i < cteColumns.size(); i++ ) {
final CteColumn cteColumn = cteColumns.get( i );
querySpec.getSelectClause().addSqlSelection(
new SqlSelectionImpl(
i + 1,
i,
new ColumnReference(
tableReference,
cteColumn.getColumnExpression(),
cteColumn.getJdbcMapping(),
sessionFactory
),
cteColumn.getJdbcMapping()
)
);
}
}
}

View File

@ -0,0 +1,113 @@
/*
* 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.sql.ast.tree.cte;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.hibernate.LockMode;
import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.mutation.internal.cte.CteBasedMutationStrategy;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
/**
* Wraps a {@link TableReference} representing the CTE and adapts it to
* {@link org.hibernate.sql.ast.tree.from.TableGroup} for use in SQL AST
*
* @author Steve Ebersole
*/
public class CteTableGroup implements TableGroup {
private final NavigablePath navigablePath;
private final TableReference cteTableReference;
public CteTableGroup(TableReference cteTableReference) {
this.navigablePath = new NavigablePath( CteBasedMutationStrategy.SHORT_NAME );
this.cteTableReference = cteTableReference;
}
@Override
public NavigablePath getNavigablePath() {
return navigablePath;
}
@Override
public LockMode getLockMode() {
return LockMode.NONE;
}
@Override
public ModelPartContainer getModelPart() {
return null;
}
@Override
public Set<TableGroupJoin> getTableGroupJoins() {
return Collections.emptySet();
}
@Override
public TableReference resolveTableReference(String tableExpression) {
return cteTableReference;
}
@Override
public TableReference resolveTableReference(
String tableExpression,
Supplier<TableReference> creator) {
return cteTableReference;
}
@Override
public void setTableGroupJoins(Set<TableGroupJoin> joins) {
}
@Override
public void visitTableGroupJoins(Consumer<TableGroupJoin> consumer) {
}
@Override
public String getGroupAlias() {
return null;
}
@Override
public boolean hasTableGroupJoins() {
return false;
}
@Override
public boolean isInnerJoinPossible() {
return false;
}
@Override
public void addTableGroupJoin(TableGroupJoin join) {
throw new UnsupportedOperationException( );
}
@Override
public void applyAffectedTableNames(Consumer<String> nameCollector) {
nameCollector.accept( cteTableReference.getTableExpression() );
}
@Override
public TableReference getPrimaryTableReference() {
return cteTableReference;
}
@Override
public List<TableReferenceJoin> getTableReferenceJoins() {
return Collections.emptyList();
}
}

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.sql.exec.spi;
/**
* @author Steve Ebersole
*/
public interface JdbcInsert extends JdbcMutation {
}

View File

@ -0,0 +1,94 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.test.metamodel.mapping;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.SecondaryTable;
import javax.persistence.Table;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
/**
* @author Steve Ebersole
*/
@DomainModel( annotatedClasses = SecondaryTableTests.SimpleEntityWithSecondaryTables.class )
@ServiceRegistry
@SessionFactory
public class SecondaryTableTests {
@Test
public void simpleTest(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "select e from SimpleEntityWithSecondaryTables e" ).list();
}
);
}
@Entity( name = "SimpleEntityWithSecondaryTables" )
@Table( name = "simple_w_secondary_tables0" )
@SecondaryTable( name = "simple_w_secondary_tables1" )
@SecondaryTable( name = "simple_w_secondary_tables2" )
public static class SimpleEntityWithSecondaryTables {
private Integer id;
private String name;
private Date dob;
private String data;
public SimpleEntityWithSecondaryTables() {
}
public SimpleEntityWithSecondaryTables(Integer id, String name, Date dob, String data) {
this.id = id;
this.name = name;
this.dob = dob;
this.data = data;
}
@Id
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Column( table = "simple_w_secondary_tables1" )
public Date getDob() {
return dob;
}
public void setDob(Date dob) {
this.dob = dob;
}
@Column( table = "simple_w_secondary_tables2" )
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}
}

View File

@ -16,8 +16,8 @@ import org.hibernate.query.NavigablePath;
import org.hibernate.query.hql.spi.HqlQueryImplementor;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.query.sqm.internal.QuerySqmImpl;
import org.hibernate.query.sqm.sql.SqmSelectInterpretation;
import org.hibernate.query.sqm.sql.internal.StandardSqmSelectToSqlAstConverter;
import org.hibernate.query.sqm.sql.SqmSelectTranslation;
import org.hibernate.query.sqm.sql.internal.StandardSqmSelectTranslator;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.spi.StandardSqlAstSelectTranslator;
@ -77,7 +77,7 @@ public class SmokeTests {
//noinspection unchecked
final SqmSelectStatement<String> sqmStatement = (SqmSelectStatement<String>) hqlQuery.getSqmStatement();
final StandardSqmSelectToSqlAstConverter sqmConverter = new StandardSqmSelectToSqlAstConverter(
final StandardSqmSelectTranslator sqmConverter = new StandardSqmSelectTranslator(
hqlQuery.getQueryOptions(),
( (QuerySqmImpl) hqlQuery ).getDomainParameterXref(),
query.getParameterBindings(),
@ -85,7 +85,7 @@ public class SmokeTests {
scope.getSessionFactory()
);
final SqmSelectInterpretation sqmInterpretation = sqmConverter.interpret( sqmStatement );
final SqmSelectTranslation sqmInterpretation = sqmConverter.translate( sqmStatement );
final SelectStatement sqlAst = sqmInterpretation.getSqlAst();
final FromClause fromClause = sqlAst.getQuerySpec().getFromClause();
@ -112,7 +112,7 @@ public class SmokeTests {
assertThat( sqlSelection.getJdbcValueExtractor(), notNullValue() );
final JdbcSelect jdbcSelectOperation = new StandardSqlAstSelectTranslator( session.getSessionFactory() )
.interpret( sqlAst );
.translate( sqlAst );
assertThat(
jdbcSelectOperation.getSql(),
@ -131,7 +131,7 @@ public class SmokeTests {
//noinspection unchecked
final SqmSelectStatement<Gender> sqmStatement = (SqmSelectStatement<Gender>) hqlQuery.getSqmStatement();
final StandardSqmSelectToSqlAstConverter sqmConverter = new StandardSqmSelectToSqlAstConverter(
final StandardSqmSelectTranslator sqmConverter = new StandardSqmSelectTranslator(
hqlQuery.getQueryOptions(),
( (QuerySqmImpl) hqlQuery ).getDomainParameterXref(),
query.getParameterBindings(),
@ -139,7 +139,7 @@ public class SmokeTests {
scope.getSessionFactory()
);
final SqmSelectInterpretation sqmInterpretation = sqmConverter.interpret( sqmStatement );
final SqmSelectTranslation sqmInterpretation = sqmConverter.translate( sqmStatement );
final SelectStatement sqlAst = sqmInterpretation.getSqlAst();
final FromClause fromClause = sqlAst.getQuerySpec().getFromClause();
@ -214,7 +214,7 @@ public class SmokeTests {
assertThat( ( (BasicResultAssembler) resultAssembler ).getValueConverter(), nullValue() );
final JdbcSelect jdbcSelectOperation = new StandardSqlAstSelectTranslator( session.getSessionFactory() )
.interpret( sqlAst );
.translate( sqlAst );
assertThat(
jdbcSelectOperation.getSql(),

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