HHH-15393 - Improve write-paths to use mapping model

This commit is contained in:
Steve Ebersole 2022-11-30 12:59:31 -06:00
parent 175fe0e44d
commit c04caa18de
19 changed files with 290 additions and 94 deletions

View File

@ -6,7 +6,6 @@
*/
package org.hibernate.persister.collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
@ -390,10 +389,10 @@ public class OneToManyPersister extends AbstractCollectionPersister {
return new TableUpdateStandard(
tableReference,
this,
parameters,
valueBindings,
keyRestrictionBindings,
Collections.emptyList(),
null,
parameters,
sqlWhereString
);
}

View File

@ -178,6 +178,7 @@ import org.hibernate.sql.exec.spi.JdbcOperationQueryUpdate;
import org.hibernate.sql.exec.spi.JdbcParameterBinder;
import org.hibernate.sql.exec.spi.JdbcParameterBinding;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.model.ast.ColumnValueParameter;
import org.hibernate.sql.model.ast.ColumnWriteFragment;
import org.hibernate.sql.model.ast.TableMutation;
import org.hibernate.sql.model.internal.TableDeleteCustomSql;
@ -615,6 +616,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
this.limitParameter = limitParameter;
}
@SuppressWarnings("unchecked")
protected <R> R interpretExpression(Expression expression, JdbcParameterBindings jdbcParameterBindings) {
if ( expression instanceof Literal ) {
return (R) ( (Literal) expression ).getLiteralValue();
@ -766,6 +768,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
}
}
//noinspection unchecked
return (T) jdbcOperation;
}
finally {
@ -902,6 +905,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
int startPosition,
JdbcParameterBindings jdbcParamBindings,
ExecutionContext executionContext) throws SQLException {
//noinspection unchecked
getJdbcMapping().getJdbcValueBinder().bind(
statement,
executionContext.getQueryOptions().getLimit().getFirstRow(),
@ -923,6 +927,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
int startPosition,
JdbcParameterBindings jdbcParamBindings,
ExecutionContext executionContext) throws SQLException {
//noinspection unchecked
getJdbcMapping().getJdbcValueBinder().bind(
statement,
executionContext.getQueryOptions().getLimit().getMaxRows(),
@ -1288,7 +1293,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
private enum LockKind {
NONE,
SHARE,
UPDATE;
UPDATE
}
protected String getForUpdate() {
@ -1625,12 +1630,10 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
if ( cte.getCteTable().getCteColumns() == null ) {
final List<String> columnExpressions = new ArrayList<>();
cte.getCteTable().getTableGroupProducer().visitSubParts(
modelPart -> {
modelPart.forEachSelectable(
0,
(index, mapping) -> columnExpressions.add( mapping.getSelectionExpression() )
);
},
modelPart -> modelPart.forEachSelectable(
0,
(index, mapping) -> columnExpressions.add( mapping.getSelectionExpression() )
),
null
);
for ( String columnExpression : columnExpressions ) {
@ -4013,6 +4016,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
throw new ExecutionException( "JDBC parameter value not bound - " + offsetParameter );
}
final Number bindValue = (Number) binding.getBindValue();
//noinspection unchecked
offsetParameter.getExpressionType().getJdbcMappings().get( 0 ).getJdbcValueBinder().bind(
statement,
bindValue.intValue() + offsetValue,
@ -4092,6 +4096,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
offsetValue = dynamicOffset.intValue() + staticOffset;
dynamicOffset = null;
}
//noinspection unchecked
fetchParameter.getExpressionType().getJdbcMappings().get( 0 ).getJdbcValueBinder().bind(
statement,
bindValue.intValue() + offsetValue,
@ -5062,7 +5067,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
assert literal.getExpressionType().getJdbcTypeCount() == 1;
final JdbcMapping jdbcMapping = literal.getJdbcMapping();
final JdbcLiteralFormatter literalFormatter = jdbcMapping.getJdbcLiteralFormatter();
final JdbcLiteralFormatter<Object> literalFormatter = jdbcMapping.getJdbcLiteralFormatter();
// If we encounter a plain literal in the select clause which has no literal formatter, we must render it as parameter
if ( literalFormatter == null ) {
@ -6510,7 +6515,8 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
else {
assert jdbcParameter.getExpressionType().getJdbcTypeCount() == 1;
final JdbcMapping jdbcMapping = jdbcParameter.getExpressionType().getJdbcMappings().get( 0 );
final JdbcLiteralFormatter literalFormatter = jdbcMapping.getJdbcLiteralFormatter();
//noinspection unchecked
final JdbcLiteralFormatter<Object> literalFormatter = jdbcMapping.getJdbcLiteralFormatter();
if ( literalFormatter == null ) {
throw new IllegalArgumentException( "Can't render parameter as literal, no literal formatter found" );
}
@ -7685,7 +7691,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
}
/**
* Handle rendering an insert with no columns (eye roll)
* Handle rendering an insert with no columns
*/
protected void renderInsertIntoNoColumns(TableInsertStandard tableInsert) {
renderIntoIntoAndTable( tableInsert );
@ -7697,10 +7703,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
assert sqlBuffer.toString().isEmpty();
sqlBuffer.append( tableInsert.getCustomSql() );
tableInsert.getParameters().forEach( (parameter) -> {
parameterBinders.add( parameter.getParameterBinder() );
jdbcParameters.addParameter( parameter );
} );
tableInsert.forEachParameter( this::applyParameter );
}
@Override
@ -7777,10 +7780,7 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
assert sqlBuffer.toString().isEmpty();
sqlBuffer.append( tableUpdate.getCustomSql() );
tableUpdate.getParameters().forEach( (parameter) -> {
parameterBinders.add( parameter.getParameterBinder() );
jdbcParameters.addParameter( parameter );
} );
tableUpdate.forEachParameter( this::applyParameter );
}
@Override
@ -7838,10 +7838,13 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
assert sqlBuffer.toString().isEmpty();
sqlBuffer.append( tableDelete.getCustomSql() );
tableDelete.getParameters().forEach( (parameter) -> {
parameterBinders.add( parameter.getParameterBinder() );
jdbcParameters.addParameter( parameter );
} );
tableDelete.forEachParameter( this::applyParameter );
}
protected void applyParameter(ColumnValueParameter parameter) {
assert parameter != null;
parameterBinders.add( parameter.getParameterBinder() );
jdbcParameters.addParameter( parameter );
}
@Override

View File

@ -40,9 +40,7 @@ public abstract class AbstractRestrictedTableMutation<O extends MutationOperatio
@Override
public void forEachKeyBinding(BiConsumer<Integer, ColumnValueBinding> consumer) {
for ( int i = 0; i < keyRestrictionBindings.size(); i++ ) {
consumer.accept( i, keyRestrictionBindings.get( i ) );
}
forEachThing( keyRestrictionBindings, consumer );
}
@Override
@ -52,8 +50,6 @@ public abstract class AbstractRestrictedTableMutation<O extends MutationOperatio
@Override
public void forEachOptimisticLockBinding(BiConsumer<Integer, ColumnValueBinding> consumer) {
for ( int i = 0; i < optLockRestrictionBindings.size(); i++ ) {
consumer.accept( i, optLockRestrictionBindings.get( i ) );
}
forEachThing( optLockRestrictionBindings, consumer );
}
}

View File

@ -8,6 +8,7 @@ package org.hibernate.sql.model.ast;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.hibernate.engine.jdbc.mutation.internal.MutationQueryOptions;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -75,7 +76,21 @@ public abstract class AbstractTableMutation<O extends MutationOperation>
return parameters;
}
protected <T> void forEachThing(List<T> list, BiConsumer<Integer,T> action) {
public void forEachParameter(Consumer<ColumnValueParameter> consumer) {
if ( parameters == null ) {
return;
}
for ( int i = 0; i < parameters.size(); i++ ) {
consumer.accept( parameters.get( i ) );
}
}
protected static <T> void forEachThing(List<T> list, BiConsumer<Integer,T> action) {
if ( list == null ) {
return;
}
for ( int i = 0; i < list.size(); i++ ) {
action.accept( i, list.get( i ) );
}

View File

@ -6,8 +6,10 @@
*/
package org.hibernate.sql.model.ast;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.hibernate.jdbc.Expectation;
import org.hibernate.sql.exec.spi.JdbcParameterBinder;
@ -17,6 +19,8 @@ import org.hibernate.sql.model.TableMapping;
import org.hibernate.sql.model.jdbc.JdbcUpdateMutation;
/**
* Base support for TableUpdate implementations
*
* @author Steve Ebersole
*/
public abstract class AbstractTableUpdate<O extends MutationOperation>
@ -27,31 +31,60 @@ public abstract class AbstractTableUpdate<O extends MutationOperation>
public AbstractTableUpdate(
MutatingTableReference mutatingTable,
MutationTarget<?> mutationTarget,
List<ColumnValueParameter> parameters,
List<ColumnValueBinding> valueBindings,
List<ColumnValueBinding> keyRestrictionBindings,
List<ColumnValueBinding> optLockRestrictionBindings) {
this(
super(
mutatingTable,
mutationTarget,
"update for " + mutationTarget.getRolePath(),
parameters,
valueBindings,
"update for " + mutationTarget.getNavigableRole(),
keyRestrictionBindings,
optLockRestrictionBindings
optLockRestrictionBindings,
collectParameters( valueBindings, keyRestrictionBindings, optLockRestrictionBindings )
);
this.valueBindings = valueBindings;
}
public AbstractTableUpdate(
MutatingTableReference mutatingTable,
public <T> AbstractTableUpdate(
MutatingTableReference tableReference,
MutationTarget<?> mutationTarget,
String sqlComment,
List<ColumnValueParameter> parameters,
List<ColumnValueBinding> valueBindings,
List<ColumnValueBinding> keyRestrictionBindings,
List<ColumnValueBinding> optLockRestrictionBindings,
List<ColumnValueParameter> parameters) {
super(
tableReference,
mutationTarget,
"update for " + mutationTarget.getNavigableRole(),
keyRestrictionBindings,
optLockRestrictionBindings,
parameters
);
this.valueBindings = valueBindings;
}
public static List<ColumnValueParameter> collectParameters(
List<ColumnValueBinding> valueBindings,
List<ColumnValueBinding> keyRestrictionBindings,
List<ColumnValueBinding> optLockRestrictionBindings) {
super( mutatingTable, mutationTarget, sqlComment, keyRestrictionBindings, optLockRestrictionBindings, parameters );
this.valueBindings = valueBindings;
final List<ColumnValueParameter> params = new ArrayList<>();
final BiConsumer<Integer,ColumnValueBinding> intermediateConsumer = (index, binding) -> {
final ColumnWriteFragment valueExpression = binding.getValueExpression();
if ( valueExpression != null ) {
final ColumnValueParameter parameter = valueExpression.getParameter();
if ( parameter != null ) {
params.add( parameter );
}
}
};
forEachThing( valueBindings, intermediateConsumer );
forEachThing( keyRestrictionBindings, intermediateConsumer );
forEachThing( optLockRestrictionBindings, intermediateConsumer );
return params;
}
@Override
@ -74,6 +107,20 @@ public abstract class AbstractTableUpdate<O extends MutationOperation>
forEachThing( valueBindings, consumer );
}
@Override
public void forEachParameter(Consumer<ColumnValueParameter> consumer) {
final BiConsumer<Integer,ColumnValueBinding> intermediateConsumer = (index, binding) -> {
final ColumnValueParameter parameter = binding.getValueExpression().getParameter();
if ( parameter != null ) {
consumer.accept( parameter );
}
};
forEachThing( getValueBindings(), intermediateConsumer );
forEachThing( getKeyBindings(), intermediateConsumer );
forEachThing( getOptimisticLockBindings(), intermediateConsumer );
}
@Override
protected O createMutationOperation(TableMapping tableDetails, String sql, List<JdbcParameterBinder> effectiveBinders) {
//noinspection unchecked

View File

@ -9,6 +9,7 @@ package org.hibernate.sql.model.ast;
import java.util.List;
import java.util.function.BiConsumer;
import org.hibernate.annotations.OptimisticLockType;
import org.hibernate.sql.model.MutationOperation;
/**
@ -19,20 +20,42 @@ import org.hibernate.sql.model.MutationOperation;
*/
public interface RestrictedTableMutation<O extends MutationOperation>
extends TableMutation<O> {
/**
* The bindings for each key restriction (WHERE clause).
*/
List<ColumnValueBinding> getKeyBindings();
/**
* The number of {@linkplain #getKeyBindings() key bindings}
*/
default int getNumberOfKeyBindings() {
return getKeyBindings().size();
}
/**
* Visit each {@linkplain #getKeyBindings() key binding}
*/
void forEachKeyBinding(BiConsumer<Integer,ColumnValueBinding> consumer);
/**
* All optimistic-lock bindings (WHERE clause), appended after
* {@linkplain #getKeyBindings() key bindings}
*
* @see OptimisticLockType
*/
List<ColumnValueBinding> getOptimisticLockBindings();
/**
* The number of {@linkplain #getOptimisticLockBindings() optimistic-lock bindings}
*/
default int getNumberOfOptimisticLockBindings() {
final List<ColumnValueBinding> bindings = getOptimisticLockBindings();
return bindings == null ? 0 : bindings.size();
}
/**
* Visit each {@linkplain #getOptimisticLockBindings() optimistic-lock binding}
*/
void forEachOptimisticLockBinding(BiConsumer<Integer,ColumnValueBinding> consumer);
}

View File

@ -7,6 +7,7 @@
package org.hibernate.sql.model.ast;
import java.util.List;
import java.util.function.Consumer;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.jdbc.Expectation;
@ -24,6 +25,10 @@ import org.hibernate.sql.model.ValuesAnalysis;
* Acts as a factory for {@link org.hibernate.sql.model.MutationOperation} instances,
* which are the forms used to "perform" the mutation using JDBC.
*
* @apiNote The parameter order returned from here is the expected order of binding
* to the {@link java.sql.PreparedStatement} - see {@link #getParameters()} and
* {@link #forEachParameter}
*
* @author Steve Ebersole
*/
public interface TableMutation<O extends MutationOperation> extends Statement {
@ -57,11 +62,30 @@ public interface TableMutation<O extends MutationOperation> extends Statement {
Expectation getExpectation();
/**
* The JDBC parameters associated with this mutation
* The JDBC parameters associated with this mutation.
*
* The order here is the expected binding order for the
* {@link java.sql.PreparedStatement}.
*
* @see #forEachParameter
*/
List<ColumnValueParameter> getParameters();
/**
* Visit the JDBC parameters associated with this mutation.
*
* The order here is the expected binding order for the
* {@link java.sql.PreparedStatement}.
*
* @see #getParameters
*/
void forEachParameter(Consumer<ColumnValueParameter> consumer);
O createMutationOperation(ValuesAnalysis valuesAnalysis, SessionFactoryImplementor sessionFactory);
/**
* A {@link org.hibernate.sql.ast.SqlAstTranslator} callback to create
* an appropriate mutation using the translated sql and parameter binders.
*/
O createMutationOperation(String sql, List<JdbcParameterBinder> parameterBinders);
}

View File

@ -15,6 +15,7 @@ import org.hibernate.sql.model.MutationTarget;
import org.hibernate.sql.model.MutationType;
import org.hibernate.sql.model.TableMapping;
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.TableInsert;
@ -30,15 +31,17 @@ public abstract class AbstractTableInsertBuilder
private final List<ColumnValueBinding> valueBindingList = new ArrayList<>();
private List<ColumnValueBinding> lobValueBindingList;
private final List<ColumnValueParameter> parameters = new ArrayList<>();
public AbstractTableInsertBuilder(
MutationTarget mutationTarget,
MutationTarget<?> mutationTarget,
TableMapping table,
SessionFactoryImplementor sessionFactory) {
super( MutationType.INSERT, mutationTarget, table, sessionFactory );
}
public AbstractTableInsertBuilder(
MutationTarget mutationTarget,
MutationTarget<?> mutationTarget,
MutatingTableReference tableReference,
SessionFactoryImplementor sessionFactory) {
super( MutationType.INSERT, mutationTarget, tableReference, sessionFactory );
@ -56,6 +59,10 @@ public abstract class AbstractTableInsertBuilder
return lobValueBindingList;
}
protected List<ColumnValueParameter> getParameters() {
return parameters;
}
@Override
public void addValueColumn(
String columnName,
@ -81,4 +88,9 @@ public abstract class AbstractTableInsertBuilder
JdbcMapping jdbcMapping) {
addColumn( columnName, columnWriteFragment, jdbcMapping, keyBindingList );
}
@Override
protected void handleParameterCreation(ColumnValueParameter parameter) {
parameters.add( parameter );
}
}

View File

@ -36,9 +36,6 @@ public abstract class AbstractTableMutationBuilder<M extends TableMutation<?>> i
private final MutatingTableReference mutatingTable;
private final List<ColumnValueParameter> parameters = new ArrayList<>();
public AbstractTableMutationBuilder(
MutationType mutationType,
MutationTarget<?> mutationTarget,
@ -76,10 +73,6 @@ public abstract class AbstractTableMutationBuilder<M extends TableMutation<?>> i
return sessionFactory.getJdbcServices();
}
protected List<ColumnValueParameter> getParameters() {
return parameters;
}
protected void addColumn(
String columnName,
String columnWriteFragment,
@ -116,7 +109,7 @@ public abstract class AbstractTableMutationBuilder<M extends TableMutation<?>> i
final ColumnValueParameter parameter;
if ( columnWriteFragment.contains( "?" ) ) {
parameter = new ColumnValueParameter( columnReference, parameterUsage );
parameters.add( parameter );
handleParameterCreation( parameter );
}
else {
parameter = null;
@ -125,6 +118,8 @@ public abstract class AbstractTableMutationBuilder<M extends TableMutation<?>> i
return new ColumnValueBinding( columnReference, new ColumnWriteFragment( columnWriteFragment, parameter, jdbcMapping ) );
}
protected abstract void handleParameterCreation(ColumnValueParameter parameter);
@SafeVarargs
protected final <T> List<T> combine(List<T> list1, List<T>... additionalLists) {
final ArrayList<T> combined = list1 == null

View File

@ -18,8 +18,11 @@ import org.hibernate.sql.model.TableMapping;
import org.hibernate.sql.model.ast.ColumnValueBinding;
import org.hibernate.sql.model.ast.MutatingTableReference;
import org.hibernate.sql.model.ast.RestrictedTableMutation;
import org.hibernate.sql.model.ast.TableUpdate;
/**
* Base support for TableUpdateBuilder implementations
*
* @author Steve Ebersole
*/
public abstract class AbstractTableUpdateBuilder<O extends MutationOperation>
@ -30,27 +33,44 @@ public abstract class AbstractTableUpdateBuilder<O extends MutationOperation>
private List<ColumnValueBinding> lobValueBindings;
public AbstractTableUpdateBuilder(
MutationTarget mutationTarget,
MutationTarget<?> mutationTarget,
TableMapping tableMapping,
SessionFactoryImplementor sessionFactory) {
super( MutationType.UPDATE, mutationTarget, tableMapping, sessionFactory );
}
public AbstractTableUpdateBuilder(
MutationTarget mutationTarget,
MutationTarget<?> mutationTarget,
MutatingTableReference tableReference,
SessionFactoryImplementor sessionFactory) {
super( MutationType.UPDATE, mutationTarget, tableReference, sessionFactory );
}
/**
* The bindings for each key restriction (WHERE clause).
*
* @see TableUpdate#getKeyBindings
*/
protected List<ColumnValueBinding> getKeyBindings() {
return keyBindings;
}
/**
* The (non-LOB) bindings for each column being updated (SET clause)
*
* @see TableUpdate#getValueBindings
*/
protected List<ColumnValueBinding> getValueBindings() {
return valueBindings;
}
/**
* @apiNote The distinction with {@link #getValueBindings} is to help
* in cases e.g. where a dialect needs to order all LOB bindings after
* all non-LOB bindings
*
* @see TableUpdate#getValueBindings
*/
protected List<ColumnValueBinding> getLobValueBindings() {
return lobValueBindings;
}

View File

@ -6,11 +6,15 @@
*/
package org.hibernate.sql.model.ast.builder;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.sql.model.MutationTarget;
import org.hibernate.sql.model.MutationType;
import org.hibernate.sql.model.TableMapping;
import org.hibernate.sql.model.ast.ColumnValueParameter;
import org.hibernate.sql.model.ast.MutatingTableReference;
import org.hibernate.sql.model.ast.TableDelete;
import org.hibernate.sql.model.internal.TableDeleteCustomSql;
@ -28,15 +32,17 @@ public class TableDeleteBuilderStandard
implements TableDeleteBuilder {
private final boolean isCustomSql;
private final List<ColumnValueParameter> parameters = new ArrayList<>();
public TableDeleteBuilderStandard(
MutationTarget mutationTarget,
MutationTarget<?> mutationTarget,
TableMapping table,
SessionFactoryImplementor sessionFactory) {
this( mutationTarget, new MutatingTableReference( table ), sessionFactory );
}
public TableDeleteBuilderStandard(
MutationTarget mutationTarget,
MutationTarget<?> mutationTarget,
MutatingTableReference tableReference,
SessionFactoryImplementor sessionFactory) {
super( MutationType.DELETE, mutationTarget, tableReference, sessionFactory );
@ -84,4 +90,13 @@ public class TableDeleteBuilderStandard
getParameters()
);
}
protected List<ColumnValueParameter> getParameters() {
return parameters;
}
@Override
protected void handleParameterCreation(ColumnValueParameter parameter) {
parameters.add( parameter );
}
}

View File

@ -13,6 +13,7 @@ import org.hibernate.sql.model.MutationOperation;
import org.hibernate.sql.model.MutationTarget;
import org.hibernate.sql.model.TableMapping;
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.RestrictedTableMutation;
import org.hibernate.sql.model.internal.TableUpdateCustomSql;
@ -21,18 +22,20 @@ import org.hibernate.sql.model.internal.TableUpdateStandard;
import org.hibernate.sql.model.internal.TableUpsert;
/**
* Standard TableUpdateBuilder implementation
*
* @author Steve Ebersole
*/
public class TableUpdateBuilderStandard<O extends MutationOperation> extends AbstractTableUpdateBuilder<O> {
public TableUpdateBuilderStandard(
MutationTarget mutationTarget,
MutationTarget<?> mutationTarget,
TableMapping tableMapping,
SessionFactoryImplementor sessionFactory) {
super( mutationTarget, tableMapping, sessionFactory );
}
public TableUpdateBuilderStandard(
MutationTarget mutationTarget,
MutationTarget<?> mutationTarget,
MutatingTableReference tableReference,
SessionFactoryImplementor sessionFactory) {
super( mutationTarget, tableReference, sessionFactory );
@ -50,7 +53,6 @@ public class TableUpdateBuilderStandard<O extends MutationOperation> extends Abs
return (RestrictedTableMutation<O>) new TableUpdateCustomSql(
getMutatingTable(),
getMutationTarget(),
getParameters(),
valueBindings,
getKeyRestrictionBindings(),
getOptimisticLockBindings()
@ -63,18 +65,22 @@ public class TableUpdateBuilderStandard<O extends MutationOperation> extends Abs
getMutationTarget(),
valueBindings,
getKeyRestrictionBindings(),
getOptimisticLockBindings(),
getParameters()
getOptimisticLockBindings()
);
}
return (RestrictedTableMutation<O>) new TableUpdateStandard(
getMutatingTable(),
getMutationTarget(),
getParameters(),
valueBindings,
getKeyRestrictionBindings(),
getOptimisticLockBindings()
);
}
@Override
protected void handleParameterCreation(ColumnValueParameter parameter) {
// nothing to do for updates... each TableUpdate impl collects
// the parameters from the bindings in a specific order
}
}

View File

@ -30,11 +30,20 @@ public class TableUpdateCustomSql
public TableUpdateCustomSql(
MutatingTableReference mutatingTable,
MutationTarget<?> mutationTarget,
List<ColumnValueParameter> parameters,
List<ColumnValueBinding> valueBindings,
List<ColumnValueBinding> keyRestrictionBindings,
List<ColumnValueBinding> optLockRestrictionBindings) {
super( mutatingTable, mutationTarget, parameters, valueBindings, keyRestrictionBindings, optLockRestrictionBindings );
super( mutatingTable, mutationTarget, valueBindings, keyRestrictionBindings, optLockRestrictionBindings );
}
public TableUpdateCustomSql(
MutatingTableReference mutatingTable,
MutationTarget<?> mutationTarget,
List<ColumnValueBinding> valueBindings,
List<ColumnValueBinding> keyRestrictionBindings,
List<ColumnValueBinding> optLockRestrictionBindings,
List<ColumnValueParameter> parameters) {
super( mutatingTable, mutationTarget, valueBindings, keyRestrictionBindings, optLockRestrictionBindings, parameters );
}
@Override

View File

@ -9,6 +9,7 @@ package org.hibernate.sql.model.internal;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.hibernate.jdbc.Expectation;
import org.hibernate.sql.ast.SqlAstWalker;
@ -18,6 +19,7 @@ import org.hibernate.sql.model.MutationTarget;
import org.hibernate.sql.model.TableMapping;
import org.hibernate.sql.model.ast.AbstractRestrictedTableMutation;
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.TableUpdate;
import org.hibernate.sql.model.jdbc.JdbcMutationOperation;
@ -84,4 +86,8 @@ public class TableUpdateNoSet
@Override
public void forEachValueBinding(BiConsumer<Integer, ColumnValueBinding> consumer) {
}
@Override
public void forEachParameter(Consumer<ColumnValueParameter> consumer) {
}
}

View File

@ -25,22 +25,32 @@ public class TableUpdateStandard extends AbstractTableUpdate<JdbcMutationOperati
public TableUpdateStandard(
MutatingTableReference mutatingTable,
MutationTarget<?> mutationTarget,
List<ColumnValueParameter> parameters,
List<ColumnValueBinding> valueBindings,
List<ColumnValueBinding> keyRestrictionBindings,
List<ColumnValueBinding> optLockRestrictionBindings) {
this( mutatingTable, mutationTarget, parameters, valueBindings, keyRestrictionBindings, optLockRestrictionBindings, null );
super( mutatingTable, mutationTarget, valueBindings, keyRestrictionBindings, optLockRestrictionBindings );
this.whereFragment = null;
}
public TableUpdateStandard(
MutatingTableReference mutatingTable,
MutatingTableReference tableReference,
MutationTarget<?> mutationTarget,
List<ColumnValueParameter> parameters,
List<ColumnValueBinding> valueBindings,
List<ColumnValueBinding> keyRestrictionBindings,
List<ColumnValueBinding> optLockRestrictionBindings,
List<ColumnValueParameter> parameters) {
this( tableReference, mutationTarget, valueBindings, keyRestrictionBindings, optLockRestrictionBindings, parameters, null );
}
public TableUpdateStandard(
MutatingTableReference tableReference,
MutationTarget<?> mutationTarget,
List<ColumnValueBinding> valueBindings,
List<ColumnValueBinding> keyRestrictionBindings,
List<ColumnValueBinding> optLockRestrictionBindings,
List<ColumnValueParameter> parameters,
String whereFragment) {
super( mutatingTable, mutationTarget, parameters, valueBindings, keyRestrictionBindings, optLockRestrictionBindings );
super( tableReference, mutationTarget, valueBindings, keyRestrictionBindings, optLockRestrictionBindings, parameters );
this.whereFragment = whereFragment;
}

View File

@ -8,6 +8,7 @@ package org.hibernate.sql.model.internal;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.jdbc.Expectation;
@ -25,6 +26,8 @@ import org.hibernate.sql.model.ast.MutatingTableReference;
import org.hibernate.sql.model.ast.RestrictedTableMutation;
import org.hibernate.sql.model.ast.TableUpdate;
import static org.hibernate.sql.model.ast.AbstractTableUpdate.collectParameters;
/**
* @apiNote Implements {@link TableUpdate} because it is fundamentally an update
*
@ -40,16 +43,14 @@ public class TableUpsert
MutationTarget<?> mutationTarget,
List<ColumnValueBinding> valueBindings,
List<ColumnValueBinding> keyRestrictionBindings,
List<ColumnValueBinding> optLockRestrictionBindings,
List<ColumnValueParameter> parameters) {
List<ColumnValueBinding> optLockRestrictionBindings) {
this(
mutatingTable,
mutationTarget,
"upsert for " + mutationTarget.getRolePath(),
valueBindings,
keyRestrictionBindings,
optLockRestrictionBindings,
parameters
optLockRestrictionBindings
);
}
@ -59,9 +60,15 @@ public class TableUpsert
String comment,
List<ColumnValueBinding> valueBindings,
List<ColumnValueBinding> keyRestrictionBindings,
List<ColumnValueBinding> optLockRestrictionBindings,
List<ColumnValueParameter> parameters) {
super( mutatingTable, mutationTarget, comment, keyRestrictionBindings, optLockRestrictionBindings, parameters );
List<ColumnValueBinding> optLockRestrictionBindings) {
super(
mutatingTable,
mutationTarget,
comment,
keyRestrictionBindings,
optLockRestrictionBindings,
collectParameters( valueBindings, keyRestrictionBindings, optLockRestrictionBindings )
);
this.valueBindings = valueBindings;
}
@ -90,6 +97,20 @@ public class TableUpsert
return getMutatingTable().getTableMapping().getUpdateDetails().getExpectation();
}
@Override
public void forEachParameter(Consumer<ColumnValueParameter> consumer) {
final BiConsumer<Integer,ColumnValueBinding> intermediateConsumer = (index, binding) -> {
final ColumnValueParameter parameter = binding.getValueExpression().getParameter();
if ( parameter != null ) {
consumer.accept( parameter );
}
};
forEachThing( getValueBindings(), intermediateConsumer );
forEachThing( getKeyBindings(), intermediateConsumer );
forEachThing( getOptimisticLockBindings(), intermediateConsumer );
}
public List<ColumnValueBinding> getValueBindings() {
return valueBindings;
}

View File

@ -140,8 +140,9 @@ public class OptionalTableUpdateOperation implements SelfExecutingUpdateOperatio
}
}
else {
// there are some non-null values for the table - we need to update or insert the values
// there are some non-null values for the table - we need to update or insert the values.
// first, try the update and see if any row was affected
final boolean wasUpdated;
if ( valuesAnalysis.getTablesWithPreviousNonNullValues().contains( tableMapping ) ) {
// either
@ -302,10 +303,7 @@ public class OptionalTableUpdateOperation implements SelfExecutingUpdateOperatio
private boolean performUpdate(
JdbcValueBindings jdbcValueBindings,
SharedSessionContractImplementor session) {
MODEL_MUTATION_LOGGER.tracef(
"#performUpdate(%s)",
tableMapping.getTableName()
);
MODEL_MUTATION_LOGGER.tracef( "#performUpdate(%s)", tableMapping.getTableName() );
final TableUpdate<JdbcMutationOperation> tableUpdate;
if ( tableMapping.getUpdateDetails() != null
@ -313,20 +311,20 @@ public class OptionalTableUpdateOperation implements SelfExecutingUpdateOperatio
tableUpdate = new TableUpdateCustomSql(
new MutatingTableReference( tableMapping ),
mutationTarget,
parameters,
valueBindings,
keyBindings,
optimisticLockBindings
optimisticLockBindings,
parameters
);
}
else {
tableUpdate = new TableUpdateStandard(
new MutatingTableReference( tableMapping ),
mutationTarget,
parameters,
valueBindings,
keyBindings,
optimisticLockBindings
optimisticLockBindings,
parameters
);
}

View File

@ -6,7 +6,6 @@ import org.hibernate.annotations.SQLInsert;
import org.hibernate.annotations.SQLUpdate;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.FailureExpected;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
@ -19,6 +18,7 @@ import jakarta.persistence.SecondaryTable;
import jakarta.persistence.Table;
import jakarta.persistence.Version;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.annotations.GenerationTime.ALWAYS;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -26,7 +26,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
@DomainModel(annotatedClasses = CustomSqlGeneratedTest.Custom.class)
public class CustomSqlGeneratedTest {
@Test
@FailureExpected( reason = "Change in expected order of columns in UPDATE SET clause causes problems with `@SQLUpdate`" )
public void testCustomSqlWithGenerated(SessionFactoryScope scope) {
Custom c = new Custom();
c.name = "name";
@ -41,8 +40,8 @@ public class CustomSqlGeneratedTest {
cc.text = "more text";
s.flush();
cc = s.find(Custom.class, c.id);
assertEquals(cc.text, "MORE TEXT");
assertEquals(cc.name, "EMAN");
assertThat(cc.text ).isEqualTo( "MORE TEXT");
assertThat( cc.name ).isEqualTo( "EMAN" );
s.remove(cc);
s.flush();
cc = s.find(Custom.class, c.id);

View File

@ -5,7 +5,6 @@ import org.hibernate.annotations.SQLInsert;
import org.hibernate.annotations.SQLUpdate;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.FailureExpected;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
@ -24,7 +23,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
@DomainModel(annotatedClasses = CustomSqlTest.Custom.class)
public class CustomSqlTest {
@Test
@FailureExpected( reason = "Change in expected order of columns in UPDATE SET clause causes problems with `@SQLUpdate`" )
public void testCustomSql(SessionFactoryScope scope) {
Custom c = new Custom();
c.name = "name";