Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
/**
* @author Jan Schatteman
*/
@Deprecated(forRemoval = true)
public class MySQLDeleteOrUpsertOperation extends DeleteOrUpsertOperation {

private Expectation customExpectation;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ INSERT INTO employees (id, name, salary, version)
salary = values(salary)
*/
@Override
protected void renderUpdatevalue(ColumnValueBinding columnValueBinding) {
protected void renderUpdateValue(ColumnValueBinding columnValueBinding) {
appendSql( "values(" );
appendSql( columnValueBinding.getColumnReference().getColumnExpression() );
appendSql( ")" );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ protected void renderNewRowAlias() {
}

@Override
protected void renderUpdatevalue(ColumnValueBinding columnValueBinding) {
protected void renderUpdateValue(ColumnValueBinding columnValueBinding) {
renderAlias();
appendSql( "." );
appendSql( columnValueBinding.getColumnReference().getColumnExpression() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@
package org.hibernate.dialect.sql.ast;


import org.hibernate.dialect.MySQLDeleteOrUpsertOperation;
import org.hibernate.StaleStateException;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.jdbc.Expectation;
import org.hibernate.persister.entity.mutation.EntityTableMapping;
import org.hibernate.sql.ast.spi.SqlAstTranslatorWithUpsert;
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;

import java.sql.PreparedStatement;
import java.util.List;

/**
Expand All @@ -37,17 +40,31 @@ public MutationOperation createMergeOperation(OptionalTableUpdate optionalTableU
optionalTableUpdate.getMutatingTable().getTableMapping(),
optionalTableUpdate.getMutationTarget(),
getSql(),
new MySQLRowCountExpectation(),
getParameterBinders()
);

return new MySQLDeleteOrUpsertOperation(
return new DeleteOrUpsertOperation(
optionalTableUpdate.getMutationTarget(),
(EntityTableMapping) optionalTableUpdate.getMutatingTable().getTableMapping(),
upsertOperation,
optionalTableUpdate
);
}

private static class MySQLRowCountExpectation implements Expectation {
@Override
public final void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition, String sql) {
if ( rowCount > 2 ) {
throw new StaleStateException(
"Unexpected row count"
+ " (the expected row count for an ON DUPLICATE KEY UPDATE statement should be either 0, 1 or 2 )"
+ " [" + sql + "]"
);
}
}
}

@Override
protected void renderUpsertStatement(OptionalTableUpdate optionalTableUpdate) {
renderInsertInto( optionalTableUpdate );
Expand All @@ -56,34 +73,39 @@ protected void renderUpsertStatement(OptionalTableUpdate optionalTableUpdate) {
}

protected void renderInsertInto(OptionalTableUpdate optionalTableUpdate) {
appendSql( "insert into " );
if ( optionalTableUpdate.getValueBindings().isEmpty() ) {
appendSql( "insert ignore into " );
}
else {
appendSql( "insert into " );
}
appendSql( optionalTableUpdate.getMutatingTable().getTableName() );
appendSql( " (" );
appendSql( " " );

final List<ColumnValueBinding> keyBindings = optionalTableUpdate.getKeyBindings();
char separator = '(';
for ( ColumnValueBinding keyBinding : keyBindings ) {
appendSql( separator );
appendSql( keyBinding.getColumnReference().getColumnExpression() );
appendSql( ',' );
separator = ',';
}

optionalTableUpdate.forEachValueBinding( (columnPosition, columnValueBinding) -> {
appendSql( columnValueBinding.getColumnReference().getColumnExpression() );
if ( columnPosition != optionalTableUpdate.getValueBindings().size() - 1 ) {
appendSql( ',' );
}
appendSql( ',' );
appendSql( columnValueBinding.getColumnReference().getColumnExpression() );
} );

appendSql( ") values (" );
appendSql( ") values " );

separator = '(';
for ( ColumnValueBinding keyBinding : keyBindings ) {
appendSql( separator );
keyBinding.getValueExpression().accept( this );
appendSql( ',' );
separator = ',';
}

optionalTableUpdate.forEachValueBinding( (columnPosition, columnValueBinding) -> {
if ( columnPosition > 0 ) {
appendSql( ',' );
}
appendSql( ',' );
columnValueBinding.getValueExpression().accept( this );
} );
appendSql(") ");
Expand All @@ -94,19 +116,21 @@ protected void renderNewRowAlias() {
}

protected void renderOnDuplicateKeyUpdate(OptionalTableUpdate optionalTableUpdate) {
appendSql( "on duplicate key update " );
optionalTableUpdate.forEachValueBinding( (columnPosition, columnValueBinding) -> {
final String columnName = columnValueBinding.getColumnReference().getColumnExpression();
if ( columnPosition > 0 ) {
appendSql( ',' );
}
appendSql( columnName );
append( " = " );
renderUpdatevalue( columnValueBinding );
} );
if ( !optionalTableUpdate.getValueBindings().isEmpty() ) {
appendSql( "on duplicate key update " );
optionalTableUpdate.forEachValueBinding( (columnPosition, columnValueBinding) -> {
final String columnName = columnValueBinding.getColumnReference().getColumnExpression();
if ( columnPosition > 0 ) {
appendSql( ',' );
}
appendSql( columnName );
append( " = " );
renderUpdateValue( columnValueBinding );
} );
}
}

protected void renderUpdatevalue(ColumnValueBinding columnValueBinding) {
protected void renderUpdateValue(ColumnValueBinding columnValueBinding) {
}

}
25 changes: 25 additions & 0 deletions hibernate-core/src/main/java/org/hibernate/jdbc/Expectation.java
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,31 @@ protected int expectedRowCount() {
}
}

/**
* Row count checking. A row count is an integer value returned by
* {@link java.sql.PreparedStatement#executeUpdate()} or
* {@link java.sql.Statement#executeBatch()}. The row count is checked
* against an expected value, but is also allowed to be 0.
* For example, the expected row count for an {@code UPSERT} statement is 0 or 1.
*/
class OptionalRowCount implements Expectation {
@Override
public final void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition, String sql) {
if ( rowCount != 0 ) {
if ( batchPosition < 0 ) {
checkNonBatched( expectedRowCount(), rowCount, sql );
}
else {
checkBatched( expectedRowCount(), rowCount, batchPosition, sql );
}
}
}

protected int expectedRowCount() {
return 1;
}
}

/**
* Essentially identical to {@link RowCount} except that the row count
* is obtained via an output parameter of a {@linkplain CallableStatement
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3452,15 +3452,7 @@ protected UpdateCoordinator buildUpdateCoordinator() {
}

protected UpdateCoordinator buildMergeCoordinator() {
// we only have updates to issue for entities with one or more singular attributes
for ( int i = 0; i < attributeMappings.size(); i++ ) {
final AttributeMapping attributeMapping = attributeMappings.get( i );
if ( attributeMapping instanceof SingularAttributeMapping ) {
return new MergeCoordinator( this, factory );
}
}
// otherwise, nothing to update
return new UpdateCoordinatorNoOp( this );
return new MergeCoordinator( this, factory );
}

protected DeleteCoordinator buildDeleteCoordinator() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package org.hibernate.persister.entity.mutation;

import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.sql.model.MutationOperation;
import org.hibernate.sql.model.ast.builder.AbstractTableUpdateBuilder;
Expand All @@ -26,4 +27,49 @@ protected <O extends MutationOperation> AbstractTableUpdateBuilder<O> newTableUp
return new TableMergeBuilder<>( entityPersister(), tableMapping, factory() );
}

@Override
protected UpdateValuesAnalysisImpl analyzeUpdateValues(
Object entity,
Object[] values,
Object oldVersion,
Object[] oldValues,
int[] dirtyAttributeIndexes,
InclusionChecker inclusionChecker,
InclusionChecker lockingChecker,
InclusionChecker dirtinessChecker,
Object rowId,
boolean forceDynamicUpdate,
SharedSessionContractImplementor session) {
final UpdateValuesAnalysisImpl updateValuesAnalysis = super.analyzeUpdateValues(
entity,
values,
oldVersion,
oldValues,
dirtyAttributeIndexes,
inclusionChecker,
lockingChecker,
dirtinessChecker,
rowId,
forceDynamicUpdate,
session
);
if ( oldValues == null ) {
final TableSet tablesNeedingUpdate = updateValuesAnalysis.getTablesNeedingUpdate();
final TableSet tablesWithNonNullValues = updateValuesAnalysis.getTablesWithNonNullValues();
final TableSet tablesWithPreviousNonNullValues = updateValuesAnalysis.getTablesWithPreviousNonNullValues();
for ( EntityTableMapping tableMapping : entityPersister().getTableMappings() ) {
// Need to upsert into all non-optional table mappings
if ( !tableMapping.isOptional() ) {
// If the table was previously not needing an update, remove it from tablesWithPreviousNonNullValues
// to avoid triggering a delete-statement for this operation
if ( !tablesNeedingUpdate.contains( tableMapping ) ) {
tablesWithPreviousNonNullValues.remove( tableMapping );
}
tablesNeedingUpdate.add( tableMapping );
tablesWithNonNullValues.add( tableMapping );
}
}
}
return updateValuesAnalysis;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ public void add(final TableMapping tableMapping) {
bits.set( tableMapping.getRelativePosition() );
}

public void remove(final TableMapping tableMapping) {
if ( bits != null ) {
assert addForChecks( tableMapping );
bits.set( tableMapping.getRelativePosition(), false );
}
}

public boolean isEmpty() {
return bits == null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@ && entityPersister().getVersionMapping().getVersionAttribute().isUpdateable() )
}
}

private UpdateValuesAnalysisImpl analyzeUpdateValues(
protected UpdateValuesAnalysisImpl analyzeUpdateValues(
Object entity,
Object[] values,
Object oldVersion,
Expand Down Expand Up @@ -1017,7 +1017,10 @@ protected BatchKey getVersionUpdateBatchkey(){
}

private boolean resultCheck(
Object id, PreparedStatementDetails statementDetails, int affectedRowCount, int batchPosition) {
Object id,
PreparedStatementDetails statementDetails,
int affectedRowCount,
int batchPosition) {
return identifiedResultsCheck(
statementDetails,
affectedRowCount,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.jdbc.Expectation;
import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.sql.model.ast.ColumnValueBinding;
Expand Down Expand Up @@ -44,6 +45,10 @@ public MergeOperation createMergeOperation(OptionalTableUpdate optionalTableUpda
optionalTableUpdate.getMutatingTable().getTableMapping(),
optionalTableUpdate.getMutationTarget(),
getSql(),
// Without value bindings, the upsert may have an update count of 0
optionalTableUpdate.getValueBindings().isEmpty()
? new Expectation.OptionalRowCount()
: new Expectation.RowCount(),
getParameterBinders()
);
}
Expand Down Expand Up @@ -232,16 +237,18 @@ protected void renderMergeUpdate(OptionalTableUpdate optionalTableUpdate) {
final List<ColumnValueBinding> valueBindings = optionalTableUpdate.getValueBindings();
final List<ColumnValueBinding> optimisticLockBindings = optionalTableUpdate.getOptimisticLockBindings();

renderWhenMatched( optimisticLockBindings );
appendSql( " then update set " );
for ( int i = 0; i < valueBindings.size(); i++ ) {
final ColumnValueBinding binding = valueBindings.get( i );
if ( i > 0 ) {
appendSql( ", " );
if ( !valueBindings.isEmpty() ) {
renderWhenMatched( optimisticLockBindings );
appendSql( " 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, null );
appendSql( "=" );
binding.getColumnReference().appendColumnForWrite( this, "s" );
}
binding.getColumnReference().appendColumnForWrite( this, null );
appendSql( "=" );
binding.getColumnReference().appendColumnForWrite( this, "s" );
}
}

Expand Down
Loading