HHH-16101 - MERGE for optional table updates on Oracle
This commit is contained in:
parent
ee8d80a8bd
commit
1d62d2d66e
|
@ -50,6 +50,7 @@ import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
|||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
||||
import org.hibernate.procedure.internal.StandardCallableStatementSupport;
|
||||
import org.hibernate.procedure.spi.CallableStatementSupport;
|
||||
import org.hibernate.query.SemanticException;
|
||||
|
@ -70,6 +71,8 @@ import org.hibernate.sql.ast.spi.SqlAppender;
|
|||
import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
|
||||
import org.hibernate.sql.ast.tree.Statement;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
import org.hibernate.sql.model.MutationOperation;
|
||||
import org.hibernate.sql.model.internal.OptionalTableUpdate;
|
||||
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorOracleDatabaseImpl;
|
||||
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
||||
import org.hibernate.type.JavaObjectType;
|
||||
|
@ -79,9 +82,9 @@ import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
|||
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.OracleJsonBlobJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.NullJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.ObjectNullAsNullTypeJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.OracleJsonBlobJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
||||
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
||||
|
@ -1441,4 +1444,13 @@ public class OracleDialect extends Dialect {
|
|||
public String rowId(String rowId) {
|
||||
return "rowid";
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutationOperation createOptionalTableUpdateOperation(
|
||||
EntityMutationTarget mutationTarget,
|
||||
OptionalTableUpdate optionalTableUpdate,
|
||||
SessionFactoryImplementor factory) {
|
||||
final OracleSqlAstTranslator<?> translator = new OracleSqlAstTranslator<>( factory, optionalTableUpdate );
|
||||
return translator.createMergeOperation( optionalTableUpdate );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,12 +18,11 @@ import org.hibernate.query.sqm.FetchClauseType;
|
|||
import org.hibernate.query.sqm.FrameExclusion;
|
||||
import org.hibernate.query.sqm.FrameKind;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.ast.tree.Statement;
|
||||
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.expression.AggregateColumnWriteExpression;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.expression.FunctionExpression;
|
||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||
|
@ -45,6 +44,8 @@ import org.hibernate.sql.ast.tree.select.SelectClause;
|
|||
import org.hibernate.sql.ast.tree.select.SortSpecification;
|
||||
import org.hibernate.sql.ast.tree.update.Assignment;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
import org.hibernate.sql.model.ast.ColumnValueBinding;
|
||||
import org.hibernate.sql.model.internal.OptionalTableUpdate;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
|
||||
/**
|
||||
|
@ -52,7 +53,7 @@ import org.hibernate.type.SqlTypes;
|
|||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class OracleSqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAstTranslator<T> {
|
||||
public class OracleSqlAstTranslator<T extends JdbcOperation> extends SqlAstTranslatorWithUpsert<T> {
|
||||
|
||||
public OracleSqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) {
|
||||
super( sessionFactory, statement );
|
||||
|
@ -542,4 +543,42 @@ public class OracleSqlAstTranslator<T extends JdbcOperation> extends AbstractSql
|
|||
public void visitAggregateColumnWriteExpression(AggregateColumnWriteExpression aggregateColumnWriteExpression) {
|
||||
aggregateColumnWriteExpression.appendWriteExpression( this, this );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderMergeTargetAlias() {
|
||||
appendSql( " t" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderMergeSourceAlias() {
|
||||
appendSql( " s" );
|
||||
}
|
||||
|
||||
protected void renderMergeSource(OptionalTableUpdate optionalTableUpdate) {
|
||||
final List<ColumnValueBinding> valueBindings = optionalTableUpdate.getValueBindings();
|
||||
final List<ColumnValueBinding> keyBindings = optionalTableUpdate.getKeyBindings();
|
||||
|
||||
appendSql( "(select " );
|
||||
|
||||
for ( int i = 0; i < keyBindings.size(); i++ ) {
|
||||
final ColumnValueBinding keyBinding = keyBindings.get( i );
|
||||
if ( i > 0 ) {
|
||||
appendSql( ", " );
|
||||
}
|
||||
renderCasted( keyBinding.getValueExpression() );
|
||||
appendSql( " " );
|
||||
appendSql( keyBinding.getColumnReference().getColumnExpression() );
|
||||
}
|
||||
for ( int i = 0; i < valueBindings.size(); i++ ) {
|
||||
appendSql( ", " );
|
||||
final ColumnValueBinding valueBinding = valueBindings.get( i );
|
||||
renderCasted( valueBinding.getValueExpression() );
|
||||
appendSql( " " );
|
||||
appendSql( valueBinding.getColumnReference().getColumnExpression() );
|
||||
}
|
||||
|
||||
appendSql( " from dual)" );
|
||||
|
||||
renderMergeSourceAlias();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ import org.hibernate.sql.model.internal.OptionalTableUpdate;
|
|||
import org.hibernate.sql.model.jdbc.MergeOperation;
|
||||
|
||||
/**
|
||||
* Base SqlAstTranslator for translators which support a full insert/update/delete MERGE statement
|
||||
* Base for translators which support a full insert-or-update-or-delete (MERGE) command
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
|
@ -53,7 +53,7 @@ public abstract class SqlAstTranslatorWithMerge<T extends JdbcOperation> extends
|
|||
// and s.[columns] is null
|
||||
// then delete
|
||||
// when matched
|
||||
// then update set ...
|
||||
// then update ...
|
||||
|
||||
renderMergeInto( optionalTableUpdate );
|
||||
appendSql( " " );
|
||||
|
|
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
|
||||
*/
|
||||
package org.hibernate.dialect;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.persister.entity.mutation.EntityTableMapping;
|
||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||
import org.hibernate.sql.ast.tree.Statement;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
import org.hibernate.sql.model.MutationOperation;
|
||||
import org.hibernate.sql.model.ast.ColumnValueBinding;
|
||||
import org.hibernate.sql.model.internal.OptionalTableUpdate;
|
||||
import org.hibernate.sql.model.jdbc.DeleteOrUpsertOperation;
|
||||
import org.hibernate.sql.model.jdbc.UpsertOperation;
|
||||
|
||||
/**
|
||||
* Base SqlAstTranslator for translators which support an insert-or-update (UPSERT) command
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SqlAstTranslatorWithUpsert<T extends JdbcOperation> extends AbstractSqlAstTranslator<T> {
|
||||
protected SqlAstTranslatorWithUpsert(SessionFactoryImplementor sessionFactory, Statement statement) {
|
||||
super( sessionFactory, statement );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the MutationOperation for performing the DELETE or UPSERT
|
||||
*/
|
||||
public MutationOperation createMergeOperation(OptionalTableUpdate optionalTableUpdate) {
|
||||
renderUpsertStatement( optionalTableUpdate );
|
||||
|
||||
final UpsertOperation upsertOperation = new UpsertOperation(
|
||||
optionalTableUpdate.getMutatingTable().getTableMapping(),
|
||||
optionalTableUpdate.getMutationTarget(),
|
||||
getSql(),
|
||||
getParameterBinders()
|
||||
);
|
||||
|
||||
return new DeleteOrUpsertOperation(
|
||||
optionalTableUpdate.getMutationTarget(),
|
||||
(EntityTableMapping) optionalTableUpdate.getMutatingTable().getTableMapping(),
|
||||
upsertOperation,
|
||||
optionalTableUpdate
|
||||
);
|
||||
}
|
||||
|
||||
private void renderUpsertStatement(OptionalTableUpdate optionalTableUpdate) {
|
||||
// template:
|
||||
//
|
||||
// merge into [table] as t
|
||||
// using values([bindings]) as s ([column-names])
|
||||
// on t.[key] = s.[key]
|
||||
// when not matched
|
||||
// then insert ...
|
||||
// when matched
|
||||
// then update ...
|
||||
|
||||
renderMergeInto( optionalTableUpdate );
|
||||
appendSql( " " );
|
||||
renderMergeUsing( optionalTableUpdate );
|
||||
appendSql( " " );
|
||||
renderMergeOn( optionalTableUpdate );
|
||||
appendSql( " " );
|
||||
renderMergeInsert( optionalTableUpdate );
|
||||
appendSql( " " );
|
||||
renderMergeUpdate( optionalTableUpdate );
|
||||
}
|
||||
|
||||
protected void renderMergeInto(OptionalTableUpdate optionalTableUpdate) {
|
||||
appendSql( "merge into " );
|
||||
appendSql( optionalTableUpdate.getMutatingTable().getTableName() );
|
||||
renderMergeTargetAlias();
|
||||
}
|
||||
|
||||
protected void renderMergeTargetAlias() {
|
||||
appendSql( " as t" );
|
||||
}
|
||||
|
||||
protected void renderMergeUsing(OptionalTableUpdate optionalTableUpdate) {
|
||||
appendSql( "using " );
|
||||
|
||||
renderMergeSource( optionalTableUpdate );
|
||||
}
|
||||
|
||||
protected boolean wrapMergeSourceExpression() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void renderMergeSource(OptionalTableUpdate optionalTableUpdate) {
|
||||
if ( wrapMergeSourceExpression() ) {
|
||||
appendSql( " (" );
|
||||
}
|
||||
|
||||
final List<ColumnValueBinding> valueBindings = optionalTableUpdate.getValueBindings();
|
||||
final List<ColumnValueBinding> keyBindings = optionalTableUpdate.getKeyBindings();
|
||||
|
||||
final StringBuilder columnList = new StringBuilder();
|
||||
|
||||
appendSql( " values (" );
|
||||
|
||||
for ( int i = 0; i < keyBindings.size(); i++ ) {
|
||||
final ColumnValueBinding keyBinding = keyBindings.get( i );
|
||||
if ( i > 0 ) {
|
||||
appendSql( ", " );
|
||||
columnList.append( ", " );
|
||||
}
|
||||
columnList.append( keyBinding.getColumnReference().getColumnExpression() );
|
||||
renderCasted( keyBinding.getValueExpression() );
|
||||
}
|
||||
for ( int i = 0; i < valueBindings.size(); i++ ) {
|
||||
appendSql( ", " );
|
||||
columnList.append( ", " );
|
||||
final ColumnValueBinding valueBinding = valueBindings.get( i );
|
||||
columnList.append( valueBinding.getColumnReference().getColumnExpression() );
|
||||
renderCasted( valueBinding.getValueExpression() );
|
||||
}
|
||||
|
||||
appendSql( ") " );
|
||||
|
||||
if ( wrapMergeSourceExpression() ) {
|
||||
appendSql( ") " );
|
||||
}
|
||||
|
||||
renderMergeSourceAlias();
|
||||
|
||||
appendSql( "(" );
|
||||
appendSql( columnList.toString() );
|
||||
appendSql( ")" );
|
||||
}
|
||||
|
||||
protected void renderMergeSourceAlias() {
|
||||
appendSql( " as s" );
|
||||
}
|
||||
|
||||
protected void renderMergeOn(OptionalTableUpdate optionalTableUpdate) {
|
||||
appendSql( "on (" );
|
||||
|
||||
final List<ColumnValueBinding> keyBindings = optionalTableUpdate.getKeyBindings();
|
||||
for ( int i = 0; i < keyBindings.size(); i++ ) {
|
||||
final ColumnValueBinding keyBinding = keyBindings.get( i );
|
||||
if ( i > 0 ) {
|
||||
appendSql( " and " );
|
||||
}
|
||||
keyBinding.getColumnReference().appendReadExpression( this, "t" );
|
||||
appendSql( "=" );
|
||||
keyBinding.getColumnReference().appendReadExpression( this, "s" );
|
||||
}
|
||||
// todo : optimistic locks?
|
||||
|
||||
appendSql( ")" );
|
||||
}
|
||||
|
||||
protected void renderMergeInsert(OptionalTableUpdate optionalTableUpdate) {
|
||||
final List<ColumnValueBinding> valueBindings = optionalTableUpdate.getValueBindings();
|
||||
final List<ColumnValueBinding> keyBindings = optionalTableUpdate.getKeyBindings();
|
||||
|
||||
final StringBuilder valuesList = new StringBuilder();
|
||||
|
||||
appendSql( "when not matched then insert (" );
|
||||
for ( int i = 0; i < keyBindings.size(); i++ ) {
|
||||
if ( i > 0 ) {
|
||||
appendSql( ", " );
|
||||
valuesList.append( ", " );
|
||||
}
|
||||
final ColumnValueBinding keyBinding = keyBindings.get( i );
|
||||
appendSql( keyBinding.getColumnReference().getColumnExpression() );
|
||||
keyBinding.getColumnReference().appendReadExpression( "s", valuesList::append );
|
||||
}
|
||||
for ( int i = 0; i < valueBindings.size(); i++ ) {
|
||||
appendSql( ", " );
|
||||
valuesList.append( ", " );
|
||||
final ColumnValueBinding valueBinding = valueBindings.get( i );
|
||||
appendSql( valueBinding.getColumnReference().getColumnExpression() );
|
||||
valueBinding.getColumnReference().appendReadExpression( "s", valuesList::append );
|
||||
}
|
||||
|
||||
appendSql( ") values (" );
|
||||
appendSql( valuesList.toString() );
|
||||
appendSql( ")" );
|
||||
}
|
||||
|
||||
protected void renderMergeUpdate(OptionalTableUpdate optionalTableUpdate) {
|
||||
final List<ColumnValueBinding> valueBindings = optionalTableUpdate.getValueBindings();
|
||||
|
||||
appendSql( " when matched then update set " );
|
||||
for ( int i = 0; i < valueBindings.size(); i++ ) {
|
||||
final ColumnValueBinding binding = valueBindings.get( i );
|
||||
if ( i > 0 ) {
|
||||
appendSql( ", " );
|
||||
}
|
||||
binding.getColumnReference().appendColumnForWrite( this, "t" );
|
||||
appendSql( "=" );
|
||||
binding.getColumnReference().appendColumnForWrite( this, "s" );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
|
||||
*/
|
||||
package org.hibernate.sql.model.jdbc;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.engine.jdbc.mutation.JdbcValueBindings;
|
||||
import org.hibernate.engine.jdbc.mutation.ParameterUsage;
|
||||
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
|
||||
import org.hibernate.engine.jdbc.mutation.internal.MutationQueryOptions;
|
||||
import org.hibernate.engine.jdbc.mutation.internal.PreparedStatementGroupSingleTable;
|
||||
import org.hibernate.engine.jdbc.mutation.spi.Binding;
|
||||
import org.hibernate.engine.jdbc.mutation.spi.BindingGroup;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
||||
import org.hibernate.persister.entity.mutation.EntityTableMapping;
|
||||
import org.hibernate.persister.entity.mutation.UpdateValuesAnalysis;
|
||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||
import org.hibernate.sql.model.MutationTarget;
|
||||
import org.hibernate.sql.model.MutationType;
|
||||
import org.hibernate.sql.model.SelfExecutingUpdateOperation;
|
||||
import org.hibernate.sql.model.TableMapping;
|
||||
import org.hibernate.sql.model.ValuesAnalysis;
|
||||
import org.hibernate.sql.model.ast.ColumnValueParameter;
|
||||
import org.hibernate.sql.model.internal.OptionalTableUpdate;
|
||||
import org.hibernate.sql.model.internal.TableDeleteStandard;
|
||||
|
||||
import static org.hibernate.sql.model.ModelMutationLogging.MODEL_MUTATION_LOGGER;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class DeleteOrUpsertOperation implements SelfExecutingUpdateOperation {
|
||||
private final EntityMutationTarget mutationTarget;
|
||||
private final EntityTableMapping tableMapping;
|
||||
private final UpsertOperation upsertOperation;
|
||||
|
||||
private final OptionalTableUpdate optionalTableUpdate;
|
||||
|
||||
|
||||
public DeleteOrUpsertOperation(
|
||||
EntityMutationTarget mutationTarget,
|
||||
EntityTableMapping tableMapping,
|
||||
UpsertOperation upsertOperation,
|
||||
OptionalTableUpdate optionalTableUpdate) {
|
||||
this.mutationTarget = mutationTarget;
|
||||
this.tableMapping = tableMapping;
|
||||
this.upsertOperation = upsertOperation;
|
||||
this.optionalTableUpdate = optionalTableUpdate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutationType getMutationType() {
|
||||
return MutationType.UPDATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutationTarget<?> getMutationTarget() {
|
||||
return mutationTarget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableMapping getTableDetails() {
|
||||
return tableMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcValueDescriptor findValueDescriptor(String columnName, ParameterUsage usage) {
|
||||
return upsertOperation.findValueDescriptor( columnName, usage );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performMutation(
|
||||
JdbcValueBindings jdbcValueBindings,
|
||||
ValuesAnalysis valuesAnalysis,
|
||||
SharedSessionContractImplementor session) {
|
||||
final UpdateValuesAnalysis analysis = (UpdateValuesAnalysis) valuesAnalysis;
|
||||
|
||||
if ( !analysis.getTablesWithNonNullValues().contains( tableMapping ) ) {
|
||||
// all the new values are null - delete
|
||||
performDelete( jdbcValueBindings, session );
|
||||
}
|
||||
else {
|
||||
performUpsert( jdbcValueBindings, session );
|
||||
}
|
||||
}
|
||||
|
||||
private void performDelete(JdbcValueBindings jdbcValueBindings, SharedSessionContractImplementor session) {
|
||||
MODEL_MUTATION_LOGGER.tracef( "#performDelete(%s)", tableMapping.getTableName() );
|
||||
|
||||
final TableDeleteStandard upsertDeleteAst = new TableDeleteStandard(
|
||||
optionalTableUpdate.getMutatingTable(),
|
||||
mutationTarget,
|
||||
"upsert delete",
|
||||
optionalTableUpdate.getKeyBindings(),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList()
|
||||
);
|
||||
|
||||
final SqlAstTranslator<JdbcDeleteMutation> translator = session
|
||||
.getJdbcServices()
|
||||
.getJdbcEnvironment()
|
||||
.getSqlAstTranslatorFactory()
|
||||
.buildModelMutationTranslator( upsertDeleteAst, session.getFactory() );
|
||||
final JdbcDeleteMutation upsertDelete = translator.translate( null, MutationQueryOptions.INSTANCE );
|
||||
|
||||
final PreparedStatementGroupSingleTable statementGroup = new PreparedStatementGroupSingleTable( upsertDelete, session );
|
||||
final PreparedStatementDetails statementDetails = statementGroup.resolvePreparedStatementDetails( tableMapping.getTableName() );
|
||||
final PreparedStatement upsertDeleteStatement = statementDetails.resolveStatement();
|
||||
session.getJdbcServices().getSqlStatementLogger().logStatement( statementDetails.getSqlString() );
|
||||
|
||||
bindDeleteKeyValues(
|
||||
jdbcValueBindings,
|
||||
optionalTableUpdate.getParameters(),
|
||||
statementDetails,
|
||||
session
|
||||
);
|
||||
|
||||
final int rowCount = session.getJdbcCoordinator().getResultSetReturn()
|
||||
.executeUpdate( upsertDeleteStatement, statementDetails.getSqlString() );
|
||||
MODEL_MUTATION_LOGGER.tracef( "`%s` rows upsert-deleted from `%s`", rowCount, tableMapping.getTableName() );
|
||||
}
|
||||
|
||||
private void bindDeleteKeyValues(
|
||||
JdbcValueBindings jdbcValueBindings,
|
||||
List<ColumnValueParameter> parameters,
|
||||
PreparedStatementDetails statementDetails,
|
||||
SharedSessionContractImplementor session) {
|
||||
final PreparedStatement statement = statementDetails.resolveStatement();
|
||||
|
||||
final BindingGroup bindingGroup = jdbcValueBindings.getBindingGroup( tableMapping.getTableName() );
|
||||
final Set<Binding> bindings = bindingGroup.getBindings();
|
||||
|
||||
int jdbcBindingPosition = 1;
|
||||
for ( Binding binding : bindings ) {
|
||||
if ( binding.getValueDescriptor().getUsage() != ParameterUsage.RESTRICT ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bindKeyValue(
|
||||
jdbcBindingPosition++,
|
||||
binding,
|
||||
binding.getValueDescriptor(),
|
||||
statement,
|
||||
statementDetails.getSqlString(),
|
||||
tableMapping,
|
||||
session
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static void bindKeyValue(
|
||||
int jdbcPosition,
|
||||
Binding binding,
|
||||
JdbcValueDescriptor valueDescriptor,
|
||||
PreparedStatement statement,
|
||||
String sql,
|
||||
EntityTableMapping tableMapping,
|
||||
SharedSessionContractImplementor session) {
|
||||
try {
|
||||
binding.getValueBinder().bind( statement, binding.getValue(), jdbcPosition, session );
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw session.getJdbcServices().getSqlExceptionHelper().convert(
|
||||
e,
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"Unable to bind parameter for upsert insert : %s.%s",
|
||||
tableMapping.getTableName(),
|
||||
valueDescriptor.getColumnName()
|
||||
),
|
||||
sql
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void performUpsert(JdbcValueBindings jdbcValueBindings, SharedSessionContractImplementor session) {
|
||||
MODEL_MUTATION_LOGGER.tracef( "#performUpsert(%s)", tableMapping.getTableName() );
|
||||
|
||||
final PreparedStatementGroupSingleTable statementGroup = new PreparedStatementGroupSingleTable( upsertOperation, session );
|
||||
final PreparedStatementDetails statementDetails = statementGroup.resolvePreparedStatementDetails( tableMapping.getTableName() );
|
||||
|
||||
final PreparedStatement updateStatement = statementDetails.resolveStatement();
|
||||
session.getJdbcServices().getSqlStatementLogger().logStatement( statementDetails.getSqlString() );
|
||||
|
||||
jdbcValueBindings.beforeStatement( statementDetails );
|
||||
|
||||
final int rowCount = session.getJdbcCoordinator().getResultSetReturn()
|
||||
.executeUpdate( updateStatement, statementDetails.getSqlString() );
|
||||
|
||||
MODEL_MUTATION_LOGGER.tracef( "`%s` rows upserted into `%s`", rowCount, tableMapping.getTableName() );
|
||||
}
|
||||
}
|
|
@ -72,7 +72,7 @@ public class OptionalTableUpdateOperation implements SelfExecutingUpdateOperatio
|
|||
private final List<ColumnValueBinding> optimisticLockBindings;
|
||||
private final List<ColumnValueParameter> parameters;
|
||||
|
||||
private final List<JdbcValueDescriptorImpl> jdbcValueDescriptors;
|
||||
private final List<JdbcValueDescriptor> jdbcValueDescriptors;
|
||||
|
||||
public OptionalTableUpdateOperation(
|
||||
MutationTarget<?> mutationTarget,
|
||||
|
@ -205,7 +205,7 @@ public class OptionalTableUpdateOperation implements SelfExecutingUpdateOperatio
|
|||
|
||||
bindings: for ( Binding binding : bindings ) {
|
||||
// binding-position here is 1-based (JDBC)
|
||||
final JdbcValueDescriptorImpl valueDescriptor = jdbcValueDescriptors.get( binding.getPosition() - 1 );
|
||||
final JdbcValueDescriptor valueDescriptor = jdbcValueDescriptors.get( binding.getPosition() - 1 );
|
||||
|
||||
// key bindings would have a usage of RESTRICT relative to the UPDATE
|
||||
if ( valueDescriptor.getUsage() != ParameterUsage.RESTRICT ) {
|
||||
|
@ -224,6 +224,7 @@ public class OptionalTableUpdateOperation implements SelfExecutingUpdateOperatio
|
|||
valueDescriptor,
|
||||
statement,
|
||||
jdbcDelete.getSqlString(),
|
||||
tableMapping,
|
||||
session
|
||||
);
|
||||
break;
|
||||
|
@ -238,12 +239,13 @@ public class OptionalTableUpdateOperation implements SelfExecutingUpdateOperatio
|
|||
}
|
||||
}
|
||||
|
||||
private void bindKeyValue(
|
||||
private static void bindKeyValue(
|
||||
int jdbcPosition,
|
||||
Binding binding,
|
||||
JdbcValueDescriptorImpl valueDescriptor,
|
||||
JdbcValueDescriptor valueDescriptor,
|
||||
PreparedStatement statement,
|
||||
String sql,
|
||||
EntityTableMapping tableMapping,
|
||||
SharedSessionContractImplementor session) {
|
||||
try {
|
||||
binding.getValueBinder().bind( statement, binding.getValue(), jdbcPosition, session );
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
|
||||
*/
|
||||
package org.hibernate.sql.model.jdbc;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.jdbc.Expectations;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBinder;
|
||||
import org.hibernate.sql.model.MutationTarget;
|
||||
import org.hibernate.sql.model.MutationType;
|
||||
import org.hibernate.sql.model.TableMapping;
|
||||
|
||||
/**
|
||||
* JdbcMutation implementation for UPSERT handling
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class UpsertOperation extends AbstractJdbcMutation {
|
||||
public UpsertOperation(
|
||||
TableMapping tableDetails,
|
||||
MutationTarget<?> mutationTarget,
|
||||
String sql,
|
||||
List<? extends JdbcParameterBinder> parameterBinders) {
|
||||
super( tableDetails, mutationTarget, sql, false, Expectations.NONE, parameterBinders );
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutationType getMutationType() {
|
||||
return MutationType.UPDATE;
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue