Skip to content

Commit 8436326

Browse files
committed
HHH-17949 Fix upsert handling when optimistic locking is involved
1 parent 0ce4f3c commit 8436326

File tree

3 files changed

+38
-25
lines changed

3 files changed

+38
-25
lines changed

hibernate-core/src/main/java/org/hibernate/dialect/SqlAstTranslatorWithUpsert.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ protected void renderMergeInsert(OptionalTableUpdate optionalTableUpdate) {
191191

192192
protected void renderMergeUpdate(OptionalTableUpdate optionalTableUpdate) {
193193
final List<ColumnValueBinding> valueBindings = optionalTableUpdate.getValueBindings();
194+
final List<ColumnValueBinding> optimisticLockBindings = optionalTableUpdate.getOptimisticLockBindings();
194195

195196
appendSql( " when matched then update set " );
196197
for ( int i = 0; i < valueBindings.size(); i++ ) {
@@ -202,5 +203,21 @@ protected void renderMergeUpdate(OptionalTableUpdate optionalTableUpdate) {
202203
appendSql( "=" );
203204
binding.getColumnReference().appendColumnForWrite( this, "s" );
204205
}
206+
renderMatchedWhere( optimisticLockBindings );
207+
}
208+
209+
private void renderMatchedWhere(List<ColumnValueBinding> optimisticLockBindings) {
210+
if ( !optimisticLockBindings.isEmpty() ) {
211+
appendSql( " where " );
212+
for (int i = 0; i < optimisticLockBindings.size(); i++) {
213+
final ColumnValueBinding binding = optimisticLockBindings.get( i );
214+
if ( i>0 ) {
215+
appendSql(" and ");
216+
}
217+
binding.getColumnReference().appendColumnForWrite( this, "t" );
218+
appendSql("=");
219+
binding.getValueExpression().accept( this );
220+
}
221+
}
205222
}
206223
}

hibernate-core/src/main/java/org/hibernate/sql/model/jdbc/OptionalTableUpdateOperation.java

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -386,21 +386,24 @@ private void performInsert(JdbcValueBindings jdbcValueBindings, SharedSessionCon
386386

387387
final BindingGroup bindingGroup = jdbcValueBindings.getBindingGroup( tableMapping.getTableName() );
388388
if ( bindingGroup != null ) {
389-
bindingGroup.forEachBinding( (binding) -> {
390-
try {
391-
binding.getValueBinder().bind(
392-
insertStatement,
393-
binding.getValue(),
394-
binding.getPosition(),
395-
session
396-
);
397-
}
398-
catch (SQLException e) {
399-
throw session.getJdbcServices().getSqlExceptionHelper().convert(
400-
e,
401-
"Unable to bind parameter for upsert insert",
402-
jdbcInsert.getSqlString()
403-
);
389+
bindingGroup.forEachBinding( binding -> {
390+
// Skip parameter bindings for e.g. optimistic version check
391+
if ( binding.getPosition() <= jdbcInsert.getParameterBinders().size() ) {
392+
try {
393+
binding.getValueBinder().bind(
394+
insertStatement,
395+
binding.getValue(),
396+
binding.getPosition(),
397+
session
398+
);
399+
}
400+
catch (SQLException e) {
401+
throw session.getJdbcServices().getSqlExceptionHelper().convert(
402+
e,
403+
"Unable to bind parameter for upsert insert",
404+
jdbcInsert.getSqlString()
405+
);
406+
}
404407
}
405408
} );
406409
}

hibernate-core/src/test/java/org/hibernate/orm/test/stateless/UpsertVersionedTest.java

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@
33
import jakarta.persistence.Entity;
44
import jakarta.persistence.Id;
55
import jakarta.persistence.Version;
6-
import org.hibernate.dialect.H2Dialect;
7-
import org.hibernate.dialect.PostgreSQLDialect;
8-
import org.hibernate.dialect.SQLServerDialect;
96
import org.hibernate.testing.orm.junit.DomainModel;
10-
import org.hibernate.testing.orm.junit.RequiresDialect;
117
import org.hibernate.testing.orm.junit.SessionFactory;
128
import org.hibernate.testing.orm.junit.SessionFactoryScope;
139
import org.junit.jupiter.api.Test;
@@ -17,9 +13,6 @@
1713
@SessionFactory
1814
@DomainModel(annotatedClasses = UpsertVersionedTest.Record.class)
1915
public class UpsertVersionedTest {
20-
@RequiresDialect(H2Dialect.class)
21-
@RequiresDialect(SQLServerDialect.class)
22-
@RequiresDialect(value = PostgreSQLDialect.class, matchSubTypes = false)
2316
@Test void test(SessionFactoryScope scope) {
2417
scope.inStatelessTransaction(s-> {
2518
s.upsert(new Record(123L,null,"hello earth"));
@@ -30,21 +23,21 @@ public class UpsertVersionedTest {
3023
assertEquals("hello mars",s.get(Record.class,456L).message);
3124
});
3225
scope.inStatelessTransaction(s-> {
33-
s.upsert(new Record(123L,1L,"goodbye earth"));
26+
s.upsert(new Record(123L,0L,"goodbye earth"));
3427
});
3528
scope.inStatelessTransaction(s-> {
3629
assertEquals("goodbye earth",s.get(Record.class,123L).message);
3730
assertEquals("hello mars",s.get(Record.class,456L).message);
3831
});
3932
scope.inStatelessTransaction(s-> {
40-
s.upsert(new Record(456L,4L,"goodbye mars"));
33+
s.upsert(new Record(456L,3L,"goodbye mars"));
4134
});
4235
scope.inStatelessTransaction(s-> {
4336
assertEquals("goodbye earth",s.get(Record.class,123L).message);
4437
assertEquals("goodbye mars",s.get(Record.class,456L).message);
4538
});
4639
}
47-
@Entity
40+
@Entity(name = "Record")
4841
static class Record {
4942
@Id Long id;
5043
@Version Long version;

0 commit comments

Comments
 (0)