Skip to content

Commit b8f4839

Browse files
blafondDavideD
authored andcommitted
[#1702] implement upsert() in StatelessSession
1 parent a37a539 commit b8f4839

13 files changed

+341
-6
lines changed

hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/Mutiny.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1736,6 +1736,28 @@ default <T> Uni<T> get(Class<T> entityClass, Object id, LockModeType lockModeTyp
17361736
*/
17371737
Uni<Void> refresh(Object entity);
17381738

1739+
/**
1740+
* Use a SQL {@code merge into} statement to perform an upsert.
1741+
*
1742+
* @param entity a detached entity instance
1743+
*
1744+
* @see org.hibernate.StatelessSession#upsert(Object)
1745+
*/
1746+
@Incubating
1747+
Uni<Void> upsert(Object entity);
1748+
1749+
/**
1750+
* Use a SQL {@code merge into} statement to perform an upsert.
1751+
*
1752+
* @param entityName The entityName for the entity to be merged
1753+
* @param entity a detached entity instance
1754+
* @throws org.hibernate.TransientObjectException is the entity is transient
1755+
*
1756+
* @see org.hibernate.StatelessSession#upsert(String, Object)
1757+
*/
1758+
@Incubating
1759+
Uni<Void> upsert(String entityName, Object entity);
1760+
17391761
/**
17401762
* Refresh the entity instance state from the database.
17411763
*

hibernate-reactive-core/src/main/java/org/hibernate/reactive/mutiny/impl/MutinyStatelessSessionImpl.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,16 @@ public Uni<Void> refresh(Object entity) {
163163
return uni( () -> delegate.reactiveRefresh( entity ) );
164164
}
165165

166+
@Override
167+
public Uni<Void> upsert(Object entity) {
168+
return uni( () -> delegate.reactiveUpsert( entity ) );
169+
}
170+
171+
@Override
172+
public Uni<Void> upsert(String entityName, Object entity) {
173+
return uni( () -> delegate.reactiveUpsert( entityName, entity ) );
174+
}
175+
166176
@Override
167177
public Uni<Void> refreshAll(Object... entities) {
168178
return uni( () -> delegate.reactiveRefreshAll( entities ) );

hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveCoordinatorFactory.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,20 @@ public static ReactiveDeleteCoordinator buildDeleteCoordinator(
4444
SessionFactoryImplementor factory) {
4545
return new ReactiveDeleteCoordinator( entityPersister, factory );
4646
}
47+
48+
public static ReactiveUpdateCoordinator buildMergeCoordinator(
49+
AbstractEntityPersister entityPersister,
50+
SessionFactoryImplementor factory) {
51+
// we only have updates to issue for entities with one or more singular attributes
52+
final AttributeMappingsList attributeMappings = entityPersister.getAttributeMappings();
53+
for ( int i = 0; i < attributeMappings.size(); i++ ) {
54+
AttributeMapping attributeMapping = attributeMappings.get( i );
55+
if ( attributeMapping instanceof SingularAttributeMapping ) {
56+
return new ReactiveMergeCoordinatorStandardScopeFactory( entityPersister, factory );
57+
}
58+
}
59+
60+
// otherwise, nothing to update
61+
return new ReactiveUpdateCoordinatorNoOp( entityPersister );
62+
}
4763
}

hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveEntityPersister.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,22 @@ CompletionStage<Void> updateReactive(
6363
final Object rowId,
6464
final SharedSessionContractImplementor session);
6565

66+
/**
67+
* Update the given instance state without blocking.
68+
*
69+
* @see EntityPersister#merge(Object, Object[], int[], boolean, Object[], Object, Object, Object, SharedSessionContractImplementor)
70+
*/
71+
CompletionStage<Void> mergeReactive(
72+
final Object id,
73+
final Object[] fields,
74+
final int[] dirtyFields,
75+
final boolean hasDirtyCollection,
76+
final Object[] oldFields,
77+
final Object oldVersion,
78+
final Object object,
79+
final Object rowId,
80+
final SharedSessionContractImplementor session);
81+
6682
/**
6783
* Obtain a pessimistic lock without blocking
6884
*/

hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveJoinedSubclassEntityPersister.java

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
*/
66
package org.hibernate.reactive.persister.entity.impl;
77

8+
import java.sql.PreparedStatement;
9+
import java.util.List;
10+
import java.util.concurrent.CompletionStage;
11+
812
import org.hibernate.FetchMode;
913
import org.hibernate.HibernateException;
1014
import org.hibernate.LockMode;
@@ -36,8 +40,8 @@
3640
import org.hibernate.persister.entity.mutation.DeleteCoordinator;
3741
import org.hibernate.persister.entity.mutation.InsertCoordinator;
3842
import org.hibernate.persister.entity.mutation.UpdateCoordinator;
39-
import org.hibernate.reactive.loader.ast.internal.ReactiveSingleIdArrayLoadPlan;
4043
import org.hibernate.property.access.spi.PropertyAccess;
44+
import org.hibernate.reactive.loader.ast.internal.ReactiveSingleIdArrayLoadPlan;
4145
import org.hibernate.reactive.loader.ast.spi.ReactiveSingleUniqueKeyEntityLoader;
4246
import org.hibernate.reactive.persister.entity.mutation.ReactiveDeleteCoordinator;
4347
import org.hibernate.reactive.persister.entity.mutation.ReactiveInsertCoordinator;
@@ -51,11 +55,6 @@
5155
import org.hibernate.sql.results.graph.entity.internal.EntityResultJoinedSubclassImpl;
5256
import org.hibernate.type.EntityType;
5357

54-
import java.sql.PreparedStatement;
55-
import java.util.List;
56-
import java.util.concurrent.CompletionStage;
57-
58-
5958
/**
6059
* An {@link ReactiveEntityPersister} backed by {@link JoinedSubclassEntityPersister}
6160
* and {@link ReactiveAbstractEntityPersister}.
@@ -223,6 +222,29 @@ public CompletionStage<Void> updateReactive(
223222
.coordinateReactiveUpdate( object, id, rowId, values, oldVersion, oldValues, dirtyAttributeIndexes, hasDirtyCollection, session );
224223
}
225224

225+
/**
226+
* Merge an Object
227+
*
228+
* @see #merge(Object, Object[], int[], boolean, Object[], Object, Object, Object, SharedSessionContractImplementor)
229+
*/
230+
@Override
231+
public CompletionStage<Void> mergeReactive(
232+
Object id,
233+
Object[] values,
234+
int[] dirtyAttributeIndexes,
235+
boolean hasDirtyCollection,
236+
Object[] oldValues,
237+
Object oldVersion,
238+
Object object,
239+
Object rowId,
240+
SharedSessionContractImplementor session) {
241+
// This is different from Hibernate ORM because our reactive update coordinator cannot be share among
242+
// multiple update operations
243+
return ( (ReactiveUpdateCoordinator) getMergeCoordinator() )
244+
.makeScopedCoordinator()
245+
.coordinateReactiveUpdate( object, id, rowId, values, oldVersion, oldValues, dirtyAttributeIndexes, hasDirtyCollection, session );
246+
}
247+
226248
@Override
227249
public <K> CompletionStage<? extends List<?>> reactiveMultiLoad(K[] ids, EventSource session, MultiIdLoadOptions loadOptions) {
228250
return reactiveDelegate.multiLoad( ids, session, loadOptions );
@@ -248,6 +270,23 @@ public void update(
248270
throw LOG.nonReactiveMethodCall( "updateReactive" );
249271
}
250272

273+
/**
274+
* @see #mergeReactive(Object, Object[], int[], boolean, Object[], Object, Object, Object, SharedSessionContractImplementor)
275+
*/
276+
@Override
277+
public void merge(
278+
Object id,
279+
Object[] values,
280+
int[] dirtyAttributeIndexes,
281+
boolean hasDirtyCollection,
282+
Object[] oldValues,
283+
Object oldVersion,
284+
Object object,
285+
Object rowId,
286+
SharedSessionContractImplementor session) throws HibernateException {
287+
throw LOG.nonReactiveMethodCall( "mergeReactive" );
288+
}
289+
251290
@Override
252291
public boolean check(int rows, Object id, int tableNumber, Expectation expectation, PreparedStatement statement, String sql) throws HibernateException {
253292
return super.check(rows, id, tableNumber, expectation, statement, sql);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/* Hibernate, Relational Persistence for Idiomatic Java
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
* Copyright: Red Hat Inc. and Hibernate Authors
5+
*/
6+
package org.hibernate.reactive.persister.entity.impl;
7+
8+
import org.hibernate.engine.spi.SessionFactoryImplementor;
9+
import org.hibernate.persister.entity.AbstractEntityPersister;
10+
import org.hibernate.persister.entity.mutation.MergeCoordinator;
11+
import org.hibernate.reactive.persister.entity.mutation.ReactiveMergeCoordinator;
12+
import org.hibernate.reactive.persister.entity.mutation.ReactiveScopedUpdateCoordinator;
13+
import org.hibernate.reactive.persister.entity.mutation.ReactiveUpdateCoordinator;
14+
15+
public class ReactiveMergeCoordinatorStandardScopeFactory extends MergeCoordinator
16+
implements ReactiveUpdateCoordinator {
17+
18+
public ReactiveMergeCoordinatorStandardScopeFactory(AbstractEntityPersister entityPersister, SessionFactoryImplementor factory) {
19+
super( entityPersister, factory );
20+
}
21+
22+
@Override
23+
public ReactiveScopedUpdateCoordinator makeScopedCoordinator() {
24+
return new ReactiveMergeCoordinator(
25+
entityPersister(),
26+
factory(),
27+
this.getStaticUpdateGroup(),
28+
this.getBatchKey(),
29+
this.getVersionUpdateGroup(),
30+
this.getVersionUpdateBatchkey()
31+
);
32+
}
33+
}

hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveSingleTableEntityPersister.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ protected DeleteCoordinator buildDeleteCoordinator() {
102102
return ReactiveCoordinatorFactory.buildDeleteCoordinator( this, getFactory() );
103103
}
104104

105+
@Override
106+
protected UpdateCoordinator buildMergeCoordinator() {
107+
return ReactiveCoordinatorFactory.buildMergeCoordinator( this, getFactory() );
108+
}
109+
105110
@Override
106111
public Generator getGenerator() throws HibernateException {
107112
return reactiveDelegate.reactive( super.getGenerator() );
@@ -218,6 +223,20 @@ public void update(
218223
throw LOG.nonReactiveMethodCall( "updateReactive" );
219224
}
220225

226+
@Override
227+
public void merge(
228+
Object id,
229+
Object[] values,
230+
int[] dirtyAttributeIndexes,
231+
boolean hasDirtyCollection,
232+
Object[] oldValues,
233+
Object oldVersion,
234+
Object object,
235+
Object rowId,
236+
SharedSessionContractImplementor session) throws HibernateException {
237+
throw LOG.nonReactiveMethodCall( "mergeReactive" );
238+
}
239+
221240
/**
222241
* Process properties generated with an insert
223242
*
@@ -315,6 +334,29 @@ public CompletionStage<Void> updateReactive(
315334
.coordinateReactiveUpdate( object, id, rowId, values, oldVersion, oldValues, dirtyAttributeIndexes, hasDirtyCollection, session );
316335
}
317336

337+
/**
338+
* Merge an object
339+
*
340+
* @see SingleTableEntityPersister#merge(Object, Object[], int[], boolean, Object[], Object, Object, Object, SharedSessionContractImplementor)
341+
*/
342+
@Override
343+
public CompletionStage<Void> mergeReactive(
344+
final Object id,
345+
final Object[] values,
346+
int[] dirtyAttributeIndexes,
347+
final boolean hasDirtyCollection,
348+
final Object[] oldValues,
349+
final Object oldVersion,
350+
final Object object,
351+
final Object rowId,
352+
SharedSessionContractImplementor session) {
353+
return ( (ReactiveUpdateCoordinator) getMergeCoordinator() )
354+
// This is different from Hibernate ORM because our reactive update coordinator cannot be share among
355+
// multiple update operations
356+
.makeScopedCoordinator()
357+
.coordinateReactiveUpdate( object, id, rowId, values, oldVersion, oldValues, dirtyAttributeIndexes, hasDirtyCollection, session );
358+
}
359+
318360
@Override
319361
public <K> CompletionStage<? extends List<?>> reactiveMultiLoad(K[] ids, EventSource session, MultiIdLoadOptions loadOptions) {
320362
return reactiveDelegate.multiLoad( ids, session, loadOptions );

hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/impl/ReactiveUnionSubclassEntityPersister.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,23 @@ public void update(
245245
throw LOG.nonReactiveMethodCall( "updateReactive" );
246246
}
247247

248+
/**
249+
* @see #mergeReactive(Object, Object[], int[], boolean, Object[], Object, Object, Object, SharedSessionContractImplementor)
250+
*/
251+
@Override
252+
public void merge(
253+
Object id,
254+
Object[] values,
255+
int[] dirtyAttributeIndexes,
256+
boolean hasDirtyCollection,
257+
Object[] oldValues,
258+
Object oldVersion,
259+
Object object,
260+
Object rowId,
261+
SharedSessionContractImplementor session) throws HibernateException {
262+
throw LOG.nonReactiveMethodCall( "mergeReactive" );
263+
}
264+
248265
/**
249266
* Process properties generated with an insert
250267
*
@@ -338,6 +355,27 @@ public CompletionStage<Void> updateReactive(
338355
.coordinateReactiveUpdate( object, id, rowId, values, oldVersion, oldValues, dirtyAttributeIndexes, hasDirtyCollection, session );
339356
}
340357

358+
/**
359+
* @see #merge(Object, Object[], int[], boolean, Object[], Object, Object, Object, SharedSessionContractImplementor)
360+
*/
361+
@Override
362+
public CompletionStage<Void> mergeReactive(
363+
Object id,
364+
Object[] values,
365+
int[] dirtyAttributeIndexes,
366+
boolean hasDirtyCollection,
367+
Object[] oldValues,
368+
Object oldVersion,
369+
Object object,
370+
Object rowId,
371+
SharedSessionContractImplementor session) {
372+
// This is different from Hibernate ORM because our reactive update coordinator cannot be share among
373+
// multiple update operations
374+
return ( (ReactiveUpdateCoordinator) getMergeCoordinator() )
375+
.makeScopedCoordinator()
376+
.coordinateReactiveUpdate( object, id, rowId, values, oldVersion, oldValues, dirtyAttributeIndexes, hasDirtyCollection, session );
377+
}
378+
341379
@Override
342380
public <K> CompletionStage<? extends List<?>> reactiveMultiLoad(K[] ids, EventSource session, MultiIdLoadOptions loadOptions) {
343381
return reactiveDelegate.multiLoad( ids, session, loadOptions );
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/* Hibernate, Relational Persistence for Idiomatic Java
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
* Copyright: Red Hat Inc. and Hibernate Authors
5+
*/
6+
package org.hibernate.reactive.persister.entity.mutation;
7+
8+
import org.hibernate.engine.jdbc.batch.spi.BatchKey;
9+
import org.hibernate.engine.spi.SessionFactoryImplementor;
10+
import org.hibernate.persister.entity.AbstractEntityPersister;
11+
import org.hibernate.persister.entity.mutation.EntityTableMapping;
12+
import org.hibernate.sql.model.MutationOperation;
13+
import org.hibernate.sql.model.MutationOperationGroup;
14+
import org.hibernate.sql.model.ast.builder.AbstractTableUpdateBuilder;
15+
import org.hibernate.sql.model.ast.builder.TableMergeBuilder;
16+
17+
/**
18+
* @see org.hibernate.persister.entity.mutation.MergeCoordinator
19+
* @see org.hibernate.reactive.persister.entity.impl.ReactiveMergeCoordinatorStandardScopeFactory
20+
*/
21+
public class ReactiveMergeCoordinator extends ReactiveUpdateCoordinatorStandard {
22+
public ReactiveMergeCoordinator(
23+
AbstractEntityPersister entityPersister,
24+
SessionFactoryImplementor factory,
25+
MutationOperationGroup staticUpdateGroup,
26+
BatchKey batchKey,
27+
MutationOperationGroup versionUpdateGroup,
28+
BatchKey versionUpdateBatchkey) {
29+
super( entityPersister, factory, staticUpdateGroup, batchKey, versionUpdateGroup, versionUpdateBatchkey );
30+
}
31+
32+
@Override
33+
protected <O extends MutationOperation> AbstractTableUpdateBuilder<O> newTableUpdateBuilder(EntityTableMapping tableMapping) {
34+
return new TableMergeBuilder<>( entityPersister(), tableMapping, factory() );
35+
}
36+
}

hibernate-reactive-core/src/main/java/org/hibernate/reactive/session/ReactiveStatelessSession.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ public interface ReactiveStatelessSession extends ReactiveQueryProducer, Reactiv
4040

4141
CompletionStage<Void> reactiveUpdate(Object entity);
4242

43+
CompletionStage<Void> reactiveUpsert(Object entity);
44+
45+
CompletionStage<Void> reactiveUpsert(String entityName, Object entity);
46+
4347
CompletionStage<Void> reactiveRefresh(Object entity);
4448

4549
CompletionStage<Void> reactiveRefresh(Object entity, LockMode lockMode);

0 commit comments

Comments
 (0)