HHH-16103 - MERGE for optional table updates on SQL Server

This commit is contained in:
Steve Ebersole 2023-01-25 17:07:03 -06:00
parent 375f6b5f14
commit 5525b8d9b7
6 changed files with 40 additions and 41 deletions

View File

@ -40,7 +40,6 @@ import java.util.TimeZone;
import java.util.UUID; import java.util.UUID;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import jakarta.persistence.GenerationType;
import org.hibernate.AssertionFailure; import org.hibernate.AssertionFailure;
import org.hibernate.Incubating; import org.hibernate.Incubating;
import org.hibernate.Length; import org.hibernate.Length;
@ -147,7 +146,7 @@ import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.spi.StringBuilderSqlAppender; import org.hibernate.sql.ast.spi.StringBuilderSqlAppender;
import org.hibernate.sql.model.MutationOperation; import org.hibernate.sql.model.MutationOperation;
import org.hibernate.sql.model.internal.TableUpsert; import org.hibernate.sql.model.internal.OptionalTableUpdate;
import org.hibernate.sql.model.jdbc.OptionalTableUpdateOperation; import org.hibernate.sql.model.jdbc.OptionalTableUpdateOperation;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
@ -192,6 +191,7 @@ import org.hibernate.type.spi.TypeConfiguration;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import jakarta.persistence.GenerationType;
import jakarta.persistence.TemporalType; import jakarta.persistence.TemporalType;
import static java.lang.Math.ceil; import static java.lang.Math.ceil;
@ -4709,9 +4709,9 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
*/ */
public MutationOperation createUpsertOperation( public MutationOperation createUpsertOperation(
EntityMutationTarget mutationTarget, EntityMutationTarget mutationTarget,
TableUpsert tableUpsert, OptionalTableUpdate optionalTableUpdate,
SessionFactoryImplementor factory) { SessionFactoryImplementor factory) {
return new OptionalTableUpdateOperation( mutationTarget, tableUpsert, factory ); return new OptionalTableUpdateOperation( mutationTarget, optionalTableUpdate, factory );
} }
/** /**

View File

@ -62,7 +62,7 @@ import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.model.MutationOperation; import org.hibernate.sql.model.MutationOperation;
import org.hibernate.sql.model.internal.TableUpsert; import org.hibernate.sql.model.internal.OptionalTableUpdate;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorH2DatabaseImpl; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorH2DatabaseImpl;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
@ -874,9 +874,9 @@ public class H2Dialect extends Dialect {
@Override @Override
public MutationOperation createUpsertOperation( public MutationOperation createUpsertOperation(
EntityMutationTarget mutationTarget, EntityMutationTarget mutationTarget,
TableUpsert tableUpsert, OptionalTableUpdate optionalTableUpdate,
SessionFactoryImplementor factory) { SessionFactoryImplementor factory) {
final H2SqlAstTranslator<?> translator = new H2SqlAstTranslator<>( factory, tableUpsert ); final H2SqlAstTranslator<?> translator = new H2SqlAstTranslator<>( factory, optionalTableUpdate );
return translator.visitUpsert( tableUpsert ); return translator.visitUpsert( optionalTableUpdate );
} }
} }

View File

@ -37,8 +37,8 @@ import org.hibernate.sql.ast.tree.select.SelectClause;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.model.MutationOperation; import org.hibernate.sql.model.MutationOperation;
import org.hibernate.sql.model.ast.ColumnValueBinding; import org.hibernate.sql.model.ast.ColumnValueBinding;
import org.hibernate.sql.model.internal.OptionalTableUpdate;
import org.hibernate.sql.model.internal.TableInsertStandard; import org.hibernate.sql.model.internal.TableInsertStandard;
import org.hibernate.sql.model.internal.TableUpsert;
import org.hibernate.sql.model.jdbc.UpsertOperation; import org.hibernate.sql.model.jdbc.UpsertOperation;
import static org.hibernate.internal.util.collections.CollectionHelper.isNotEmpty; import static org.hibernate.internal.util.collections.CollectionHelper.isNotEmpty;
@ -312,7 +312,7 @@ public class H2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAstT
return getDialect().getVersion().isSameOrAfter( 1, 4, 198 ); return getDialect().getVersion().isSameOrAfter( 1, 4, 198 );
} }
public MutationOperation visitUpsert(TableUpsert tableUpsert) { public MutationOperation visitUpsert(OptionalTableUpdate optionalTableUpdate) {
// template: // template:
// //
// merge into [table] as t // merge into [table] as t
@ -326,35 +326,35 @@ public class H2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAstT
// when matched // when matched
// then update set ... // then update set ...
renderMergeInto( tableUpsert ); renderMergeInto( optionalTableUpdate );
appendSql( " " ); appendSql( " " );
renderMergeUsing( tableUpsert ); renderMergeUsing( optionalTableUpdate );
appendSql( " " ); appendSql( " " );
renderMergeOn( tableUpsert ); renderMergeOn( optionalTableUpdate );
appendSql( " " ); appendSql( " " );
renderMergeInsert( tableUpsert ); renderMergeInsert( optionalTableUpdate );
appendSql( " " ); appendSql( " " );
renderMergeDelete( tableUpsert ); renderMergeDelete( optionalTableUpdate );
appendSql( " " ); appendSql( " " );
renderMergeUpdate( tableUpsert ); renderMergeUpdate( optionalTableUpdate );
return new UpsertOperation( return new UpsertOperation(
tableUpsert.getMutatingTable().getTableMapping(), optionalTableUpdate.getMutatingTable().getTableMapping(),
tableUpsert.getMutationTarget(), optionalTableUpdate.getMutationTarget(),
getSql(), getSql(),
getParameterBinders() getParameterBinders()
); );
} }
private void renderMergeInto(TableUpsert tableUpsert) { private void renderMergeInto(OptionalTableUpdate optionalTableUpdate) {
appendSql( "merge into " ); appendSql( "merge into " );
appendSql( tableUpsert.getMutatingTable().getTableName() ); appendSql( optionalTableUpdate.getMutatingTable().getTableName() );
appendSql( " as t" ); appendSql( " as t" );
} }
private void renderMergeUsing(TableUpsert tableUpsert) { private void renderMergeUsing(OptionalTableUpdate optionalTableUpdate) {
final List<ColumnValueBinding> valueBindings = tableUpsert.getValueBindings(); final List<ColumnValueBinding> valueBindings = optionalTableUpdate.getValueBindings();
final List<ColumnValueBinding> keyBindings = tableUpsert.getKeyBindings(); final List<ColumnValueBinding> keyBindings = optionalTableUpdate.getKeyBindings();
final StringBuilder columnList = new StringBuilder(); final StringBuilder columnList = new StringBuilder();
@ -382,12 +382,12 @@ public class H2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAstT
appendSql( ")" ); appendSql( ")" );
} }
private void renderMergeOn(TableUpsert tableUpsert) { private void renderMergeOn(OptionalTableUpdate optionalTableUpdate) {
appendSql( "on " ); appendSql( "on " );
// todo : optimistic locks? // todo : optimistic locks?
final List<ColumnValueBinding> keyBindings = tableUpsert.getKeyBindings(); final List<ColumnValueBinding> keyBindings = optionalTableUpdate.getKeyBindings();
for ( int i = 0; i < keyBindings.size(); i++ ) { for ( int i = 0; i < keyBindings.size(); i++ ) {
final ColumnValueBinding keyBinding = keyBindings.get( i ); final ColumnValueBinding keyBinding = keyBindings.get( i );
if ( i > 0 ) { if ( i > 0 ) {
@ -399,9 +399,9 @@ public class H2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAstT
} }
} }
private void renderMergeInsert(TableUpsert tableUpsert) { private void renderMergeInsert(OptionalTableUpdate optionalTableUpdate) {
final List<ColumnValueBinding> valueBindings = tableUpsert.getValueBindings(); final List<ColumnValueBinding> valueBindings = optionalTableUpdate.getValueBindings();
final List<ColumnValueBinding> keyBindings = tableUpsert.getKeyBindings(); final List<ColumnValueBinding> keyBindings = optionalTableUpdate.getKeyBindings();
final StringBuilder valuesList = new StringBuilder(); final StringBuilder valuesList = new StringBuilder();
@ -428,8 +428,8 @@ public class H2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAstT
appendSql( ")" ); appendSql( ")" );
} }
private void renderMergeDelete(TableUpsert tableUpsert) { private void renderMergeDelete(OptionalTableUpdate optionalTableUpdate) {
final List<ColumnValueBinding> valueBindings = tableUpsert.getValueBindings(); final List<ColumnValueBinding> valueBindings = optionalTableUpdate.getValueBindings();
appendSql( " when matched " ); appendSql( " when matched " );
for ( int i = 0; i < valueBindings.size(); i++ ) { for ( int i = 0; i < valueBindings.size(); i++ ) {
@ -441,8 +441,8 @@ public class H2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAstT
appendSql( " then delete" ); appendSql( " then delete" );
} }
private void renderMergeUpdate(TableUpsert tableUpsert) { private void renderMergeUpdate(OptionalTableUpdate optionalTableUpdate) {
final List<ColumnValueBinding> valueBindings = tableUpsert.getValueBindings(); final List<ColumnValueBinding> valueBindings = optionalTableUpdate.getValueBindings();
appendSql( " when matched then update set " ); appendSql( " when matched then update set " );
for ( int i = 0; i < valueBindings.size(); i++ ) { for ( int i = 0; i < valueBindings.size(); i++ ) {

View File

@ -13,13 +13,12 @@ import org.hibernate.sql.model.MutationOperation;
import org.hibernate.sql.model.MutationTarget; import org.hibernate.sql.model.MutationTarget;
import org.hibernate.sql.model.TableMapping; import org.hibernate.sql.model.TableMapping;
import org.hibernate.sql.model.ast.ColumnValueBinding; import org.hibernate.sql.model.ast.ColumnValueBinding;
import org.hibernate.sql.model.ast.ColumnValueParameter;
import org.hibernate.sql.model.ast.MutatingTableReference; import org.hibernate.sql.model.ast.MutatingTableReference;
import org.hibernate.sql.model.ast.RestrictedTableMutation; import org.hibernate.sql.model.ast.RestrictedTableMutation;
import org.hibernate.sql.model.internal.OptionalTableUpdate;
import org.hibernate.sql.model.internal.TableUpdateCustomSql; import org.hibernate.sql.model.internal.TableUpdateCustomSql;
import org.hibernate.sql.model.internal.TableUpdateNoSet; import org.hibernate.sql.model.internal.TableUpdateNoSet;
import org.hibernate.sql.model.internal.TableUpdateStandard; import org.hibernate.sql.model.internal.TableUpdateStandard;
import org.hibernate.sql.model.internal.TableUpsert;
/** /**
* Standard TableUpdateBuilder implementation * Standard TableUpdateBuilder implementation
@ -62,7 +61,7 @@ public class TableUpdateBuilderStandard<O extends MutationOperation> extends Abs
} }
if ( getMutatingTable().getTableMapping().isOptional() ) { if ( getMutatingTable().getTableMapping().isOptional() ) {
return (RestrictedTableMutation<O>) new TableUpsert( return (RestrictedTableMutation<O>) new OptionalTableUpdate(
getMutatingTable(), getMutatingTable(),
getMutationTarget(), getMutationTarget(),
valueBindings, valueBindings,

View File

@ -34,12 +34,12 @@ import static org.hibernate.sql.model.ast.AbstractTableUpdate.collectParameters;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class TableUpsert public class OptionalTableUpdate
extends AbstractRestrictedTableMutation<MutationOperation> extends AbstractRestrictedTableMutation<MutationOperation>
implements RestrictedTableMutation<MutationOperation> { implements RestrictedTableMutation<MutationOperation> {
private final List<ColumnValueBinding> valueBindings; private final List<ColumnValueBinding> valueBindings;
public TableUpsert( public OptionalTableUpdate(
MutatingTableReference mutatingTable, MutatingTableReference mutatingTable,
MutationTarget<?> mutationTarget, MutationTarget<?> mutationTarget,
List<ColumnValueBinding> valueBindings, List<ColumnValueBinding> valueBindings,
@ -55,7 +55,7 @@ public class TableUpsert
); );
} }
public TableUpsert( public OptionalTableUpdate(
MutatingTableReference mutatingTable, MutatingTableReference mutatingTable,
MutationTarget<?> mutationTarget, MutationTarget<?> mutationTarget,
String comment, String comment,
@ -75,7 +75,7 @@ public class TableUpsert
@Override @Override
protected String getLoggableName() { protected String getLoggableName() {
return "TableUpsert"; return "OptionalTableUpdate";
} }
@Override @Override

View File

@ -46,13 +46,13 @@ import org.hibernate.sql.model.ast.MutatingTableReference;
import org.hibernate.sql.model.ast.TableDelete; import org.hibernate.sql.model.ast.TableDelete;
import org.hibernate.sql.model.ast.TableInsert; import org.hibernate.sql.model.ast.TableInsert;
import org.hibernate.sql.model.ast.TableUpdate; import org.hibernate.sql.model.ast.TableUpdate;
import org.hibernate.sql.model.internal.OptionalTableUpdate;
import org.hibernate.sql.model.internal.TableDeleteCustomSql; import org.hibernate.sql.model.internal.TableDeleteCustomSql;
import org.hibernate.sql.model.internal.TableDeleteStandard; import org.hibernate.sql.model.internal.TableDeleteStandard;
import org.hibernate.sql.model.internal.TableInsertCustomSql; import org.hibernate.sql.model.internal.TableInsertCustomSql;
import org.hibernate.sql.model.internal.TableInsertStandard; import org.hibernate.sql.model.internal.TableInsertStandard;
import org.hibernate.sql.model.internal.TableUpdateCustomSql; import org.hibernate.sql.model.internal.TableUpdateCustomSql;
import org.hibernate.sql.model.internal.TableUpdateStandard; import org.hibernate.sql.model.internal.TableUpdateStandard;
import org.hibernate.sql.model.internal.TableUpsert;
import static org.hibernate.sql.model.ModelMutationLogging.MODEL_MUTATION_LOGGER; import static org.hibernate.sql.model.ModelMutationLogging.MODEL_MUTATION_LOGGER;
@ -76,7 +76,7 @@ public class OptionalTableUpdateOperation implements SelfExecutingUpdateOperatio
public OptionalTableUpdateOperation( public OptionalTableUpdateOperation(
MutationTarget<?> mutationTarget, MutationTarget<?> mutationTarget,
TableUpsert upsert, OptionalTableUpdate upsert,
@SuppressWarnings("unused") SessionFactoryImplementor factory) { @SuppressWarnings("unused") SessionFactoryImplementor factory) {
this.mutationTarget = (EntityMutationTarget) mutationTarget; this.mutationTarget = (EntityMutationTarget) mutationTarget;
this.tableMapping = (EntityTableMapping) upsert.getMutatingTable().getTableMapping(); this.tableMapping = (EntityTableMapping) upsert.getMutatingTable().getTableMapping();