Make sure DML updates do inserts into secondary tables when necessary
This commit is contained in:
parent
955e8265dc
commit
20564a5547
|
@ -15,7 +15,6 @@ import java.util.Map;
|
||||||
|
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.boot.model.naming.Identifier;
|
|
||||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
|
@ -69,7 +68,6 @@ import org.hibernate.type.spi.TypeConfiguration;
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractCteMutationHandler extends AbstractMutationHandler {
|
public abstract class AbstractCteMutationHandler extends AbstractMutationHandler {
|
||||||
|
|
||||||
public static final String DML_RESULT_TABLE_NAME_PREFIX = "dml_cte_";
|
|
||||||
public static final String CTE_TABLE_IDENTIFIER = "id";
|
public static final String CTE_TABLE_IDENTIFIER = "id";
|
||||||
|
|
||||||
private final SqmCteTable cteTable;
|
private final SqmCteTable cteTable;
|
||||||
|
@ -126,7 +124,7 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler
|
||||||
executionContext.getQueryParameterBindings(),
|
executionContext.getQueryParameterBindings(),
|
||||||
factory
|
factory
|
||||||
);
|
);
|
||||||
final Map<SqmParameter, List<JdbcParameter>> parameterResolutions;
|
final Map<SqmParameter<?>, List<JdbcParameter>> parameterResolutions;
|
||||||
if ( domainParameterXref.getSqmParameterCount() == 0 ) {
|
if ( domainParameterXref.getSqmParameterCount() == 0 ) {
|
||||||
parameterResolutions = Collections.emptyMap();
|
parameterResolutions = Collections.emptyMap();
|
||||||
}
|
}
|
||||||
|
@ -148,7 +146,7 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler
|
||||||
MatchingIdSelectionHelper.generateMatchingIdSelectStatement(
|
MatchingIdSelectionHelper.generateMatchingIdSelectStatement(
|
||||||
entityDescriptor,
|
entityDescriptor,
|
||||||
sqmMutationStatement,
|
sqmMutationStatement,
|
||||||
false,
|
true,
|
||||||
restriction,
|
restriction,
|
||||||
sqmConverter,
|
sqmConverter,
|
||||||
executionContext,
|
executionContext,
|
||||||
|
@ -242,15 +240,41 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler
|
||||||
CteStatement idSelectCte,
|
CteStatement idSelectCte,
|
||||||
ModelPart fkModelPart,
|
ModelPart fkModelPart,
|
||||||
SessionFactoryImplementor factory) {
|
SessionFactoryImplementor factory) {
|
||||||
|
final Junction predicate = new Junction( Junction.Nature.CONJUNCTION );
|
||||||
|
final QuerySpec subQuery = createIdSubQuery(
|
||||||
|
idSelectCte,
|
||||||
|
fkModelPart,
|
||||||
|
factory
|
||||||
|
);
|
||||||
|
final Expression lhs;
|
||||||
|
if ( lhsExpressions.size() == 1 ) {
|
||||||
|
lhs = lhsExpressions.get( 0 );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lhs = new SqlTuple( lhsExpressions, null );
|
||||||
|
}
|
||||||
|
predicate.add(
|
||||||
|
new InSubQueryPredicate(
|
||||||
|
lhs,
|
||||||
|
subQuery,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return predicate;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected QuerySpec createIdSubQuery(
|
||||||
|
CteStatement idSelectCte,
|
||||||
|
ModelPart fkModelPart,
|
||||||
|
SessionFactoryImplementor factory) {
|
||||||
final NamedTableReference idSelectTableReference = new NamedTableReference(
|
final NamedTableReference idSelectTableReference = new NamedTableReference(
|
||||||
idSelectCte.getCteTable().getTableExpression(),
|
idSelectCte.getCteTable().getTableExpression(),
|
||||||
CTE_TABLE_IDENTIFIER,
|
CTE_TABLE_IDENTIFIER,
|
||||||
false,
|
false,
|
||||||
factory
|
factory
|
||||||
);
|
);
|
||||||
final Junction predicate = new Junction( Junction.Nature.CONJUNCTION );
|
|
||||||
final List<CteColumn> cteColumns = idSelectCte.getCteTable().getCteColumns();
|
final List<CteColumn> cteColumns = idSelectCte.getCteTable().getCteColumns();
|
||||||
final int size = lhsExpressions.size();
|
final int size = cteColumns.size();
|
||||||
final QuerySpec subQuery = new QuerySpec( false, 1 );
|
final QuerySpec subQuery = new QuerySpec( false, 1 );
|
||||||
subQuery.getFromClause().addRoot( new CteTableGroup( idSelectTableReference ) );
|
subQuery.getFromClause().addRoot( new CteTableGroup( idSelectTableReference ) );
|
||||||
final SelectClause subQuerySelectClause = subQuery.getSelectClause();
|
final SelectClause subQuerySelectClause = subQuery.getSelectClause();
|
||||||
|
@ -289,28 +313,14 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final Expression lhs;
|
return subQuery;
|
||||||
if ( lhsExpressions.size() == 1 ) {
|
|
||||||
lhs = lhsExpressions.get( 0 );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
lhs = new SqlTuple( lhsExpressions, null );
|
|
||||||
}
|
|
||||||
predicate.add(
|
|
||||||
new InSubQueryPredicate(
|
|
||||||
lhs,
|
|
||||||
subQuery,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
);
|
|
||||||
return predicate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void addDmlCtes(
|
protected abstract void addDmlCtes(
|
||||||
CteContainer statement,
|
CteContainer statement,
|
||||||
CteStatement idSelectCte,
|
CteStatement idSelectCte,
|
||||||
MultiTableSqmMutationConverter sqmConverter,
|
MultiTableSqmMutationConverter sqmConverter,
|
||||||
Map<SqmParameter, List<JdbcParameter>> parameterResolutions,
|
Map<SqmParameter<?>, List<JdbcParameter>> parameterResolutions,
|
||||||
SessionFactoryImplementor factory);
|
SessionFactoryImplementor factory);
|
||||||
|
|
||||||
|
|
||||||
|
@ -330,15 +340,5 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getCteTableName(String tableExpression) {
|
protected abstract String getCteTableName(String tableExpression);
|
||||||
if ( Identifier.isQuoted( tableExpression ) ) {
|
|
||||||
tableExpression = unquote( tableExpression );
|
|
||||||
return DML_RESULT_TABLE_NAME_PREFIX + tableExpression;
|
|
||||||
}
|
|
||||||
return DML_RESULT_TABLE_NAME_PREFIX + tableExpression;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String unquote(String tableExpression) {
|
|
||||||
return tableExpression.substring( 1, tableExpression.length() - 1 );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.hibernate.boot.model.naming.Identifier;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
|
@ -40,6 +41,8 @@ import org.hibernate.sql.results.graph.basic.BasicResult;
|
||||||
*/
|
*/
|
||||||
public class CteDeleteHandler extends AbstractCteMutationHandler implements DeleteHandler {
|
public class CteDeleteHandler extends AbstractCteMutationHandler implements DeleteHandler {
|
||||||
|
|
||||||
|
private static final String DELETE_RESULT_TABLE_NAME_PREFIX = "delete_cte_";
|
||||||
|
|
||||||
protected CteDeleteHandler(
|
protected CteDeleteHandler(
|
||||||
SqmCteTable cteTable,
|
SqmCteTable cteTable,
|
||||||
SqmDeleteStatement<?> sqmDeleteStatement,
|
SqmDeleteStatement<?> sqmDeleteStatement,
|
||||||
|
@ -54,7 +57,7 @@ public class CteDeleteHandler extends AbstractCteMutationHandler implements Dele
|
||||||
CteContainer statement,
|
CteContainer statement,
|
||||||
CteStatement idSelectCte,
|
CteStatement idSelectCte,
|
||||||
MultiTableSqmMutationConverter sqmConverter,
|
MultiTableSqmMutationConverter sqmConverter,
|
||||||
Map<SqmParameter, List<JdbcParameter>> parameterResolutions,
|
Map<SqmParameter<?>, List<JdbcParameter>> parameterResolutions,
|
||||||
SessionFactoryImplementor factory) {
|
SessionFactoryImplementor factory) {
|
||||||
final TableGroup updatingTableGroup = sqmConverter.getMutatingTableGroup();
|
final TableGroup updatingTableGroup = sqmConverter.getMutatingTableGroup();
|
||||||
final SelectStatement idSelectStatement = (SelectStatement) idSelectCte.getCteDefinition();
|
final SelectStatement idSelectStatement = (SelectStatement) idSelectCte.getCteDefinition();
|
||||||
|
@ -177,10 +180,19 @@ public class CteDeleteHandler extends AbstractCteMutationHandler implements Dele
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getCteTableName(String tableExpression) {
|
||||||
|
if ( Identifier.isQuoted( tableExpression ) ) {
|
||||||
|
tableExpression = tableExpression.substring( 1, tableExpression.length() - 1 );
|
||||||
|
return DELETE_RESULT_TABLE_NAME_PREFIX + tableExpression;
|
||||||
|
}
|
||||||
|
return DELETE_RESULT_TABLE_NAME_PREFIX + tableExpression;
|
||||||
|
}
|
||||||
|
|
||||||
protected String getCteTableName(PluralAttributeMapping pluralAttribute) {
|
protected String getCteTableName(PluralAttributeMapping pluralAttribute) {
|
||||||
final String hibernateEntityName = pluralAttribute.findContainingEntityMapping().getEntityName();
|
final String hibernateEntityName = pluralAttribute.findContainingEntityMapping().getEntityName();
|
||||||
final String jpaEntityName = getSessionFactory().getJpaMetamodel().entity( hibernateEntityName ).getName();
|
final String jpaEntityName = getSessionFactory().getJpaMetamodel().entity( hibernateEntityName ).getName();
|
||||||
return DML_RESULT_TABLE_NAME_PREFIX + jpaEntityName + "_" + pluralAttribute.getRootPathName().substring(
|
return DELETE_RESULT_TABLE_NAME_PREFIX + jpaEntityName + "_" + pluralAttribute.getRootPathName().substring(
|
||||||
hibernateEntityName.length() + 1
|
hibernateEntityName.length() + 1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,18 +7,20 @@
|
||||||
package org.hibernate.query.sqm.mutation.internal.cte;
|
package org.hibernate.query.sqm.mutation.internal.cte;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
import org.hibernate.boot.model.naming.Identifier;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
import org.hibernate.persister.entity.AbstractEntityPersister;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.persister.entity.Joinable;
|
import org.hibernate.persister.entity.Joinable;
|
||||||
import org.hibernate.query.SemanticException;
|
import org.hibernate.query.SemanticException;
|
||||||
|
import org.hibernate.query.results.TableGroupImpl;
|
||||||
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||||
import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter;
|
import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter;
|
||||||
import org.hibernate.query.sqm.mutation.internal.UpdateHandler;
|
import org.hibernate.query.sqm.mutation.internal.UpdateHandler;
|
||||||
|
@ -26,18 +28,28 @@ import org.hibernate.query.sqm.tree.cte.SqmCteTable;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||||
import org.hibernate.query.sqm.tree.update.SqmSetClause;
|
import org.hibernate.query.sqm.tree.update.SqmSetClause;
|
||||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
import org.hibernate.sql.ast.tree.MutationStatement;
|
import org.hibernate.sql.ast.tree.MutationStatement;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteContainer;
|
import org.hibernate.sql.ast.tree.cte.CteContainer;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteTable;
|
import org.hibernate.sql.ast.tree.cte.CteTable;
|
||||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
|
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
|
||||||
|
import org.hibernate.sql.ast.tree.insert.InsertStatement;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.ExistsPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||||
|
import org.hibernate.sql.ast.tree.select.SelectClause;
|
||||||
import org.hibernate.sql.ast.tree.update.Assignment;
|
import org.hibernate.sql.ast.tree.update.Assignment;
|
||||||
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
||||||
|
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -45,9 +57,12 @@ import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
||||||
*/
|
*/
|
||||||
public class CteUpdateHandler extends AbstractCteMutationHandler implements UpdateHandler {
|
public class CteUpdateHandler extends AbstractCteMutationHandler implements UpdateHandler {
|
||||||
|
|
||||||
|
private static final String UPDATE_RESULT_TABLE_NAME_PREFIX = "update_cte_";
|
||||||
|
private static final String INSERT_RESULT_TABLE_NAME_PREFIX = "insert_cte_";
|
||||||
|
|
||||||
public CteUpdateHandler(
|
public CteUpdateHandler(
|
||||||
SqmCteTable cteTable,
|
SqmCteTable cteTable,
|
||||||
SqmUpdateStatement sqmStatement,
|
SqmUpdateStatement<?> sqmStatement,
|
||||||
DomainParameterXref domainParameterXref,
|
DomainParameterXref domainParameterXref,
|
||||||
CteMutationStrategy strategy,
|
CteMutationStrategy strategy,
|
||||||
SessionFactoryImplementor sessionFactory) {
|
SessionFactoryImplementor sessionFactory) {
|
||||||
|
@ -59,13 +74,13 @@ public class CteUpdateHandler extends AbstractCteMutationHandler implements Upda
|
||||||
CteContainer statement,
|
CteContainer statement,
|
||||||
CteStatement idSelectCte,
|
CteStatement idSelectCte,
|
||||||
MultiTableSqmMutationConverter sqmConverter,
|
MultiTableSqmMutationConverter sqmConverter,
|
||||||
Map<SqmParameter, List<JdbcParameter>> parameterResolutions,
|
Map<SqmParameter<?>, List<JdbcParameter>> parameterResolutions,
|
||||||
SessionFactoryImplementor factory) {
|
SessionFactoryImplementor factory) {
|
||||||
final TableGroup updatingTableGroup = sqmConverter.getMutatingTableGroup();
|
final TableGroup updatingTableGroup = sqmConverter.getMutatingTableGroup();
|
||||||
final SqmUpdateStatement<?> updateStatement = (SqmUpdateStatement<?>) getSqmDeleteOrUpdateStatement();
|
final SqmUpdateStatement<?> updateStatement = (SqmUpdateStatement<?>) getSqmDeleteOrUpdateStatement();
|
||||||
final EntityMappingType entityDescriptor = getEntityDescriptor();
|
final EntityMappingType entityDescriptor = getEntityDescriptor();
|
||||||
|
|
||||||
final EntityPersister entityPersister = entityDescriptor.getEntityPersister();
|
final AbstractEntityPersister entityPersister = (AbstractEntityPersister) entityDescriptor.getEntityPersister();
|
||||||
final String rootEntityName = entityPersister.getRootEntityName();
|
final String rootEntityName = entityPersister.getRootEntityName();
|
||||||
final EntityPersister rootEntityDescriptor = factory.getRuntimeMetamodels()
|
final EntityPersister rootEntityDescriptor = factory.getRuntimeMetamodels()
|
||||||
.getMappingMetamodel()
|
.getMappingMetamodel()
|
||||||
|
@ -83,14 +98,12 @@ public class CteUpdateHandler extends AbstractCteMutationHandler implements Upda
|
||||||
// information about the assignments
|
// information about the assignments
|
||||||
final SqmSetClause setClause = updateStatement.getSetClause();
|
final SqmSetClause setClause = updateStatement.getSetClause();
|
||||||
final List<Assignment> assignments = new ArrayList<>( setClause.getAssignments().size() );
|
final List<Assignment> assignments = new ArrayList<>( setClause.getAssignments().size() );
|
||||||
final Map<SqmParameter, MappingModelExpressible> paramTypeResolutions = new LinkedHashMap<>();
|
|
||||||
|
|
||||||
sqmConverter.visitSetClause(
|
sqmConverter.visitSetClause(
|
||||||
setClause,
|
setClause,
|
||||||
assignments::add,
|
assignments::add,
|
||||||
(sqmParam, mappingType, jdbcParameters) -> {
|
(sqmParam, mappingType, jdbcParameters) -> {
|
||||||
parameterResolutions.put( sqmParam, jdbcParameters );
|
parameterResolutions.put( sqmParam, jdbcParameters );
|
||||||
paramTypeResolutions.put( sqmParam, mappingType );
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
sqmConverter.addVersionedAssignment( assignments::add, updateStatement );
|
sqmConverter.addVersionedAssignment( assignments::add, updateStatement );
|
||||||
|
@ -139,6 +152,111 @@ public class CteUpdateHandler extends AbstractCteMutationHandler implements Upda
|
||||||
assignmentsForTable.add( assignment );
|
assignmentsForTable.add( assignment );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For nullable tables we have to also generate an insert CTE
|
||||||
|
for (int i = 0; i < entityPersister.getTableSpan(); i++) {
|
||||||
|
if ( entityPersister.isNullableTable( i ) ) {
|
||||||
|
final String tableExpression = entityPersister.getTableName( i );
|
||||||
|
final TableReference updatingTableReference = updatingTableGroup.getTableReference(
|
||||||
|
updatingTableGroup.getNavigablePath(),
|
||||||
|
tableExpression,
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
final List<Assignment> assignmentList = assignmentsByTable.get( updatingTableReference );
|
||||||
|
if ( assignmentList == null ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final CteTable dmlResultCte = new CteTable(
|
||||||
|
getInsertCteTableName( tableExpression ),
|
||||||
|
idSelectCte.getCteTable().getCteColumns(),
|
||||||
|
factory
|
||||||
|
);
|
||||||
|
final NamedTableReference dmlTableReference = resolveUnionTableReference(
|
||||||
|
updatingTableReference,
|
||||||
|
tableExpression
|
||||||
|
);
|
||||||
|
final NamedTableReference existsTableReference = new NamedTableReference(
|
||||||
|
tableExpression,
|
||||||
|
"dml_",
|
||||||
|
false,
|
||||||
|
factory
|
||||||
|
);
|
||||||
|
final List<ColumnReference> existsKeyColumns = new ArrayList<>( idSelectCte.getCteTable().getCteColumns().size() );
|
||||||
|
final String[] keyColumns = entityPersister.getKeyColumns( i );
|
||||||
|
entityPersister.getIdentifierMapping().forEachSelectable(
|
||||||
|
(selectionIndex, selectableMapping) -> {
|
||||||
|
existsKeyColumns.add(
|
||||||
|
new ColumnReference(
|
||||||
|
existsTableReference,
|
||||||
|
keyColumns[selectionIndex],
|
||||||
|
selectableMapping.getJdbcMapping(),
|
||||||
|
factory
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Copy the subquery contents into a root query
|
||||||
|
final QuerySpec querySpec = createIdSubQuery( idSelectCte, null, factory ).asRootQuery();
|
||||||
|
|
||||||
|
// Prepare a not exists sub-query to avoid violating constraints
|
||||||
|
final QuerySpec existsQuerySpec = new QuerySpec( false );
|
||||||
|
existsQuerySpec.getSelectClause().addSqlSelection(
|
||||||
|
new SqlSelectionImpl(
|
||||||
|
-1,
|
||||||
|
0,
|
||||||
|
new QueryLiteral<>(
|
||||||
|
1,
|
||||||
|
factory.getTypeConfiguration().getBasicTypeForJavaType( Integer.class )
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
existsQuerySpec.getFromClause().addRoot(
|
||||||
|
new TableGroupImpl(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
existsTableReference,
|
||||||
|
entityPersister
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
existsQuerySpec.applyPredicate(
|
||||||
|
new ComparisonPredicate(
|
||||||
|
asExpression( existsKeyColumns ),
|
||||||
|
ComparisonOperator.EQUAL,
|
||||||
|
asExpression( querySpec.getSelectClause() )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
querySpec.applyPredicate(
|
||||||
|
new ExistsPredicate(
|
||||||
|
existsQuerySpec,
|
||||||
|
true,
|
||||||
|
factory.getTypeConfiguration().getBasicTypeForJavaType( Boolean.class )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Collect the target column references from the key expressions
|
||||||
|
final List<ColumnReference> targetColumnReferences = new ArrayList<>( existsKeyColumns );
|
||||||
|
// And transform assignments to target column references and selections
|
||||||
|
for ( Assignment assignment : assignments ) {
|
||||||
|
targetColumnReferences.addAll( assignment.getAssignable().getColumnReferences() );
|
||||||
|
querySpec.getSelectClause().addSqlSelection(
|
||||||
|
new SqlSelectionImpl(
|
||||||
|
0,
|
||||||
|
-1,
|
||||||
|
assignment.getAssignedValue()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final InsertStatement dmlStatement = new InsertStatement( dmlTableReference, existsKeyColumns );
|
||||||
|
dmlStatement.addTargetColumnReferences( targetColumnReferences.toArray( new ColumnReference[0] ) );
|
||||||
|
dmlStatement.setSourceSelectStatement( querySpec );
|
||||||
|
statement.addCteStatement( new CteStatement( dmlResultCte, dmlStatement ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getEntityDescriptor().visitConstraintOrderedTables(
|
getEntityDescriptor().visitConstraintOrderedTables(
|
||||||
(tableExpression, tableColumnsVisitationSupplier) -> {
|
(tableExpression, tableColumnsVisitationSupplier) -> {
|
||||||
final CteTable dmlResultCte = new CteTable(
|
final CteTable dmlResultCte = new CteTable(
|
||||||
|
@ -203,4 +321,40 @@ public class CteUpdateHandler extends AbstractCteMutationHandler implements Upda
|
||||||
|
|
||||||
throw new SemanticException( "Assignment referred to column of a joined association: " + columnReference );
|
throw new SemanticException( "Assignment referred to column of a joined association: " + columnReference );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getCteTableName(String tableExpression) {
|
||||||
|
if ( Identifier.isQuoted( tableExpression ) ) {
|
||||||
|
tableExpression = tableExpression.substring( 1, tableExpression.length() - 1 );
|
||||||
|
return UPDATE_RESULT_TABLE_NAME_PREFIX + tableExpression;
|
||||||
|
}
|
||||||
|
return UPDATE_RESULT_TABLE_NAME_PREFIX + tableExpression;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getInsertCteTableName(String tableExpression) {
|
||||||
|
if ( Identifier.isQuoted( tableExpression ) ) {
|
||||||
|
tableExpression = tableExpression.substring( 1, tableExpression.length() - 1 );
|
||||||
|
return INSERT_RESULT_TABLE_NAME_PREFIX + tableExpression;
|
||||||
|
}
|
||||||
|
return INSERT_RESULT_TABLE_NAME_PREFIX + tableExpression;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expression asExpression(SelectClause selectClause) {
|
||||||
|
final List<SqlSelection> sqlSelections = selectClause.getSqlSelections();
|
||||||
|
if ( sqlSelections.size() == 1 ) {
|
||||||
|
return sqlSelections.get( 0 ).getExpression();
|
||||||
|
}
|
||||||
|
final List<Expression> expressions = new ArrayList<>( sqlSelections.size() );
|
||||||
|
for ( SqlSelection sqlSelection : sqlSelections ) {
|
||||||
|
expressions.add( sqlSelection.getExpression() );
|
||||||
|
}
|
||||||
|
return new SqlTuple( expressions, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expression asExpression(List<ColumnReference> columnReferences) {
|
||||||
|
if ( columnReferences.size() == 1 ) {
|
||||||
|
return columnReferences.get( 0 );
|
||||||
|
}
|
||||||
|
return new SqlTuple( columnReferences, null );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
package org.hibernate.query.sqm.mutation.internal.temptable;
|
package org.hibernate.query.sqm.mutation.internal.temptable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
@ -22,28 +23,41 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
||||||
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||||
import org.hibernate.metamodel.mapping.SelectableConsumer;
|
import org.hibernate.metamodel.mapping.SelectableConsumer;
|
||||||
|
import org.hibernate.persister.entity.AbstractEntityPersister;
|
||||||
import org.hibernate.query.SemanticException;
|
import org.hibernate.query.SemanticException;
|
||||||
|
import org.hibernate.query.results.TableGroupImpl;
|
||||||
import org.hibernate.query.spi.DomainQueryExecutionContext;
|
import org.hibernate.query.spi.DomainQueryExecutionContext;
|
||||||
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||||
import org.hibernate.query.sqm.internal.SqmUtil;
|
import org.hibernate.query.sqm.internal.SqmUtil;
|
||||||
import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter;
|
import org.hibernate.query.sqm.mutation.internal.MultiTableSqmMutationConverter;
|
||||||
import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess;
|
import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess;
|
||||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||||
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.UnionTableReference;
|
import org.hibernate.sql.ast.tree.from.UnionTableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.insert.InsertStatement;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.ExistsPredicate;
|
||||||
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
|
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
|
||||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||||
|
import org.hibernate.sql.ast.tree.select.SelectClause;
|
||||||
import org.hibernate.sql.ast.tree.update.Assignment;
|
import org.hibernate.sql.ast.tree.update.Assignment;
|
||||||
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
||||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcInsert;
|
||||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||||
import org.hibernate.sql.exec.spi.JdbcUpdate;
|
import org.hibernate.sql.exec.spi.JdbcUpdate;
|
||||||
|
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
@ -179,6 +193,7 @@ public class UpdateExecutionDelegate implements TableBasedUpdateHandler.Executio
|
||||||
(tableExpression, tableKeyColumnVisitationSupplier) -> updateTable(
|
(tableExpression, tableKeyColumnVisitationSupplier) -> updateTable(
|
||||||
tableExpression,
|
tableExpression,
|
||||||
tableKeyColumnVisitationSupplier,
|
tableKeyColumnVisitationSupplier,
|
||||||
|
rows,
|
||||||
idTableSubQuery,
|
idTableSubQuery,
|
||||||
executionContext
|
executionContext
|
||||||
)
|
)
|
||||||
|
@ -224,6 +239,7 @@ public class UpdateExecutionDelegate implements TableBasedUpdateHandler.Executio
|
||||||
private void updateTable(
|
private void updateTable(
|
||||||
String tableExpression,
|
String tableExpression,
|
||||||
Supplier<Consumer<SelectableConsumer>> tableKeyColumnVisitationSupplier,
|
Supplier<Consumer<SelectableConsumer>> tableKeyColumnVisitationSupplier,
|
||||||
|
int expectedUpdateCount,
|
||||||
QuerySpec idTableSubQuery,
|
QuerySpec idTableSubQuery,
|
||||||
ExecutionContext executionContext) {
|
ExecutionContext executionContext) {
|
||||||
final TableReference updatingTableReference = updatingTableGroup.getTableReference(
|
final TableReference updatingTableReference = updatingTableGroup.getTableReference(
|
||||||
|
@ -259,8 +275,9 @@ public class UpdateExecutionDelegate implements TableBasedUpdateHandler.Executio
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final Expression keyExpression = keyColumnCollector.buildKeyExpression();
|
||||||
final InSubQueryPredicate idTableSubQueryPredicate = new InSubQueryPredicate(
|
final InSubQueryPredicate idTableSubQueryPredicate = new InSubQueryPredicate(
|
||||||
keyColumnCollector.buildKeyExpression(),
|
keyExpression,
|
||||||
idTableSubQuery,
|
idTableSubQuery,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
@ -268,8 +285,9 @@ public class UpdateExecutionDelegate implements TableBasedUpdateHandler.Executio
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// Create the SQL AST and convert it into a JdbcOperation
|
// Create the SQL AST and convert it into a JdbcOperation
|
||||||
|
final NamedTableReference dmlTableReference = resolveUnionTableReference( updatingTableReference, tableExpression );
|
||||||
final UpdateStatement sqlAst = new UpdateStatement(
|
final UpdateStatement sqlAst = new UpdateStatement(
|
||||||
resolveUnionTableReference( updatingTableReference, tableExpression ),
|
dmlTableReference,
|
||||||
assignments,
|
assignments,
|
||||||
idTableSubQueryPredicate
|
idTableSubQueryPredicate
|
||||||
);
|
);
|
||||||
|
@ -280,15 +298,154 @@ public class UpdateExecutionDelegate implements TableBasedUpdateHandler.Executio
|
||||||
.buildUpdateTranslator( sessionFactory, sqlAst )
|
.buildUpdateTranslator( sessionFactory, sqlAst )
|
||||||
.translate( jdbcParameterBindings, executionContext.getQueryOptions() );
|
.translate( jdbcParameterBindings, executionContext.getQueryOptions() );
|
||||||
|
|
||||||
jdbcServices.getJdbcMutationExecutor().execute(
|
final int updateCount = jdbcServices.getJdbcMutationExecutor().execute(
|
||||||
jdbcUpdate,
|
jdbcUpdate,
|
||||||
jdbcParameterBindings,
|
jdbcParameterBindings,
|
||||||
sql -> executionContext.getSession()
|
sql -> executionContext.getSession()
|
||||||
.getJdbcCoordinator()
|
.getJdbcCoordinator()
|
||||||
.getStatementPreparer()
|
.getStatementPreparer()
|
||||||
.prepareStatement( sql ),
|
.prepareStatement( sql ),
|
||||||
(integer, preparedStatement) -> {},
|
(integer, preparedStatement) -> {
|
||||||
|
},
|
||||||
executionContext
|
executionContext
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if ( updateCount == expectedUpdateCount ) {
|
||||||
|
// We are done when the update count matches
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Otherwise we have to check if the table is nullable, and if so, insert into that table
|
||||||
|
final AbstractEntityPersister entityPersister = (AbstractEntityPersister) entityDescriptor.getEntityPersister();
|
||||||
|
boolean isNullable = false;
|
||||||
|
for (int i = 0; i < entityPersister.getTableSpan(); i++) {
|
||||||
|
if ( tableExpression.equals( entityPersister.getTableName( i ) ) && entityPersister.isNullableTable( i ) ) {
|
||||||
|
isNullable = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( isNullable ) {
|
||||||
|
// Copy the subquery contents into a root query
|
||||||
|
final QuerySpec querySpec = new QuerySpec( true );
|
||||||
|
for ( TableGroup root : idTableSubQuery.getFromClause().getRoots() ) {
|
||||||
|
querySpec.getFromClause().addRoot( root );
|
||||||
|
}
|
||||||
|
for ( SqlSelection sqlSelection : idTableSubQuery.getSelectClause().getSqlSelections() ) {
|
||||||
|
querySpec.getSelectClause().addSqlSelection( sqlSelection );
|
||||||
|
}
|
||||||
|
querySpec.applyPredicate( idTableSubQuery.getWhereClauseRestrictions() );
|
||||||
|
|
||||||
|
// Prepare a not exists sub-query to avoid violating constraints
|
||||||
|
final QuerySpec existsQuerySpec = new QuerySpec( false );
|
||||||
|
existsQuerySpec.getSelectClause().addSqlSelection(
|
||||||
|
new SqlSelectionImpl(
|
||||||
|
-1,
|
||||||
|
0,
|
||||||
|
new QueryLiteral<>(
|
||||||
|
1,
|
||||||
|
sessionFactory.getTypeConfiguration().getBasicTypeForJavaType( Integer.class )
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
final NamedTableReference existsTableReference = new NamedTableReference(
|
||||||
|
tableExpression,
|
||||||
|
"dml_",
|
||||||
|
false,
|
||||||
|
sessionFactory
|
||||||
|
);
|
||||||
|
existsQuerySpec.getFromClause().addRoot(
|
||||||
|
new TableGroupImpl(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
existsTableReference,
|
||||||
|
entityPersister
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
final TableKeyExpressionCollector existsKeyColumnCollector = new TableKeyExpressionCollector( entityDescriptor );
|
||||||
|
tableKeyColumnVisitationSupplier.get().accept(
|
||||||
|
(columnIndex, selection) -> {
|
||||||
|
assert selection.getContainingTableExpression().equals( tableExpression );
|
||||||
|
existsKeyColumnCollector.apply(
|
||||||
|
new ColumnReference(
|
||||||
|
existsTableReference,
|
||||||
|
selection,
|
||||||
|
sessionFactory
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
existsQuerySpec.applyPredicate(
|
||||||
|
new ComparisonPredicate(
|
||||||
|
existsKeyColumnCollector.buildKeyExpression(),
|
||||||
|
ComparisonOperator.EQUAL,
|
||||||
|
asExpression(idTableSubQuery.getSelectClause())
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
querySpec.applyPredicate(
|
||||||
|
new ExistsPredicate(
|
||||||
|
existsQuerySpec,
|
||||||
|
true,
|
||||||
|
sessionFactory.getTypeConfiguration().getBasicTypeForJavaType( Boolean.class )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Collect the target column references from the key expressions
|
||||||
|
final List<ColumnReference> targetColumnReferences = new ArrayList<>();
|
||||||
|
if ( keyExpression instanceof SqlTuple ) {
|
||||||
|
//noinspection unchecked
|
||||||
|
targetColumnReferences.addAll( (Collection<? extends ColumnReference>) ( (SqlTuple) keyExpression ).getExpressions() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
targetColumnReferences.add( (ColumnReference) keyExpression );
|
||||||
|
}
|
||||||
|
// And transform assignments to target column references and selections
|
||||||
|
for ( Assignment assignment : assignments ) {
|
||||||
|
targetColumnReferences.addAll( assignment.getAssignable().getColumnReferences() );
|
||||||
|
querySpec.getSelectClause().addSqlSelection(
|
||||||
|
new SqlSelectionImpl(
|
||||||
|
0,
|
||||||
|
-1,
|
||||||
|
assignment.getAssignedValue()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final InsertStatement insertSqlAst = new InsertStatement(
|
||||||
|
dmlTableReference
|
||||||
|
);
|
||||||
|
insertSqlAst.addTargetColumnReferences( targetColumnReferences.toArray( new ColumnReference[0] ) );
|
||||||
|
insertSqlAst.setSourceSelectStatement( querySpec );
|
||||||
|
|
||||||
|
final JdbcInsert jdbcInsert = jdbcServices.getJdbcEnvironment()
|
||||||
|
.getSqlAstTranslatorFactory()
|
||||||
|
.buildInsertTranslator( sessionFactory, insertSqlAst )
|
||||||
|
.translate( jdbcParameterBindings, executionContext.getQueryOptions() );
|
||||||
|
|
||||||
|
final int insertCount = jdbcServices.getJdbcMutationExecutor().execute(
|
||||||
|
jdbcInsert,
|
||||||
|
jdbcParameterBindings,
|
||||||
|
sql -> executionContext.getSession()
|
||||||
|
.getJdbcCoordinator()
|
||||||
|
.getStatementPreparer()
|
||||||
|
.prepareStatement( sql ),
|
||||||
|
(integer, preparedStatement) -> {
|
||||||
|
},
|
||||||
|
executionContext
|
||||||
|
);
|
||||||
|
assert insertCount + updateCount == expectedUpdateCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expression asExpression(SelectClause selectClause) {
|
||||||
|
final List<SqlSelection> sqlSelections = selectClause.getSqlSelections();
|
||||||
|
if ( sqlSelections.size() == 1 ) {
|
||||||
|
return sqlSelections.get( 0 ).getExpression();
|
||||||
|
}
|
||||||
|
final List<Expression> expressions = new ArrayList<>( sqlSelections.size() );
|
||||||
|
for ( SqlSelection sqlSelection : sqlSelections ) {
|
||||||
|
expressions.add( sqlSelection.getExpression() );
|
||||||
|
}
|
||||||
|
return new SqlTuple( expressions, null );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,8 +54,8 @@ public class QuerySpec extends QueryPart implements SqlAstNode, PredicateContain
|
||||||
this.selectClause = new SelectClause();
|
this.selectClause = new SelectClause();
|
||||||
}
|
}
|
||||||
|
|
||||||
private QuerySpec(QuerySpec original) {
|
private QuerySpec(QuerySpec original, boolean root) {
|
||||||
super( false, original );
|
super( root, original );
|
||||||
this.fromClause = original.fromClause;
|
this.fromClause = original.fromClause;
|
||||||
this.selectClause = original.selectClause;
|
this.selectClause = original.selectClause;
|
||||||
this.whereClauseRestrictions = original.whereClauseRestrictions;
|
this.whereClauseRestrictions = original.whereClauseRestrictions;
|
||||||
|
@ -64,7 +64,11 @@ public class QuerySpec extends QueryPart implements SqlAstNode, PredicateContain
|
||||||
}
|
}
|
||||||
|
|
||||||
public QuerySpec asSubQuery() {
|
public QuerySpec asSubQuery() {
|
||||||
return isRoot() ? new QuerySpec( this ) : this;
|
return isRoot() ? new QuerySpec( this, false ) : this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QuerySpec asRootQuery() {
|
||||||
|
return isRoot() ? this : new QuerySpec( this, true );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -7,18 +7,20 @@
|
||||||
package org.hibernate.orm.test.mapping;
|
package org.hibernate.orm.test.mapping;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import jakarta.persistence.Column;
|
|
||||||
import jakarta.persistence.Entity;
|
|
||||||
import jakarta.persistence.Id;
|
|
||||||
import jakarta.persistence.SecondaryTable;
|
|
||||||
import jakarta.persistence.Table;
|
|
||||||
|
|
||||||
import org.hibernate.testing.orm.junit.DomainModel;
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.SecondaryTable;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
|
@ -36,6 +38,20 @@ public class SecondaryTableTests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateOnSecondaryTableColumn(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
session.persist( new SimpleEntityWithSecondaryTables( 1, "test", null, null ) );
|
||||||
|
session.flush();
|
||||||
|
session.clear();
|
||||||
|
session.createQuery( "update SimpleEntityWithSecondaryTables e set e.data = 'test'" ).executeUpdate();
|
||||||
|
SimpleEntityWithSecondaryTables entity = session.get( SimpleEntityWithSecondaryTables.class, 1 );
|
||||||
|
Assertions.assertEquals( "test", entity.data );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Entity( name = "SimpleEntityWithSecondaryTables" )
|
@Entity( name = "SimpleEntityWithSecondaryTables" )
|
||||||
@Table( name = "simple_w_secondary_tables0" )
|
@Table( name = "simple_w_secondary_tables0" )
|
||||||
@SecondaryTable( name = "simple_w_secondary_tables1" )
|
@SecondaryTable( name = "simple_w_secondary_tables1" )
|
||||||
|
|
Loading…
Reference in New Issue