Skip to content

Commit a72c2b2

Browse files
committed
[#1787] Add support for @SoftDelete
1 parent a8e757f commit a72c2b2

File tree

5 files changed

+217
-5
lines changed

5 files changed

+217
-5
lines changed

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

+7
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
1212
import org.hibernate.persister.entity.AbstractEntityPersister;
1313
import org.hibernate.reactive.persister.entity.mutation.ReactiveDeleteCoordinator;
14+
import org.hibernate.reactive.persister.entity.mutation.ReactiveDeleteCoordinatorSoft;
1415
import org.hibernate.reactive.persister.entity.mutation.ReactiveInsertCoordinatorStandard;
1516
import org.hibernate.reactive.persister.entity.mutation.ReactiveUpdateCoordinator;
1617
import org.hibernate.reactive.persister.entity.mutation.ReactiveUpdateCoordinatorNoOp;
@@ -45,6 +46,12 @@ public static ReactiveDeleteCoordinator buildDeleteCoordinator(
4546
return new ReactiveDeleteCoordinator( entityPersister, factory );
4647
}
4748

49+
public static ReactiveDeleteCoordinatorSoft buildSoftDeleteCoordinator(
50+
AbstractEntityPersister entityPersister,
51+
SessionFactoryImplementor factory) {
52+
return new ReactiveDeleteCoordinatorSoft( entityPersister, factory );
53+
}
54+
4855
public static ReactiveUpdateCoordinator buildMergeCoordinator(
4956
AbstractEntityPersister entityPersister,
5057
SessionFactoryImplementor factory) {

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

+8-4
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import java.util.List;
1010
import java.util.concurrent.CompletionStage;
1111

12-
1312
import org.hibernate.FetchMode;
1413
import org.hibernate.HibernateException;
1514
import org.hibernate.LockMode;
@@ -49,7 +48,7 @@
4948
import org.hibernate.reactive.generator.values.ReactiveInsertGeneratedIdentifierDelegate;
5049
import org.hibernate.reactive.loader.ast.internal.ReactiveSingleIdArrayLoadPlan;
5150
import org.hibernate.reactive.loader.ast.spi.ReactiveSingleUniqueKeyEntityLoader;
52-
import org.hibernate.reactive.persister.entity.mutation.ReactiveDeleteCoordinator;
51+
import org.hibernate.reactive.persister.entity.mutation.ReactiveAbstractDeleteCoordinator;
5352
import org.hibernate.reactive.persister.entity.mutation.ReactiveInsertCoordinatorStandard;
5453
import org.hibernate.reactive.persister.entity.mutation.ReactiveUpdateCoordinator;
5554
import org.hibernate.reactive.util.impl.CompletionStages;
@@ -115,7 +114,12 @@ protected InsertCoordinator buildInsertCoordinator() {
115114

116115
@Override
117116
protected DeleteCoordinator buildDeleteCoordinator() {
118-
return ReactiveCoordinatorFactory.buildDeleteCoordinator( this, getFactory() );
117+
if (super.getSoftDeleteMapping() != null) {
118+
return ReactiveCoordinatorFactory.buildSoftDeleteCoordinator( this, getFactory() );
119+
}
120+
else {
121+
return ReactiveCoordinatorFactory.buildDeleteCoordinator( this, getFactory() );
122+
}
119123
}
120124

121125
@Override
@@ -357,7 +361,7 @@ public CompletionStage<GeneratedValues> insertReactive(Object id, Object[] field
357361

358362
@Override
359363
public CompletionStage<Void> deleteReactive(Object id, Object version, Object entity, SharedSessionContractImplementor session) {
360-
return ( (ReactiveDeleteCoordinator) getDeleteCoordinator() ).reactiveDelete( entity, id, version, session );
364+
return ( (ReactiveAbstractDeleteCoordinator) getDeleteCoordinator() ).coordinateReactiveDelete( entity, id, version, session );
361365
}
362366

363367
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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 java.util.concurrent.CompletionStage;
9+
10+
import org.hibernate.engine.spi.SharedSessionContractImplementor;
11+
12+
public interface ReactiveAbstractDeleteCoordinator {
13+
14+
CompletionStage<Void> coordinateReactiveDelete(Object entity, Object id, Object version, SharedSessionContractImplementor session);
15+
}

hibernate-reactive-core/src/main/java/org/hibernate/reactive/persister/entity/mutation/ReactiveDeleteCoordinator.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import static org.hibernate.reactive.util.impl.CompletionStages.failedFuture;
3131
import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture;
3232

33-
public class ReactiveDeleteCoordinator extends DeleteCoordinatorStandard {
33+
public class ReactiveDeleteCoordinator extends DeleteCoordinatorStandard implements ReactiveAbstractDeleteCoordinator {
3434

3535
private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() );
3636

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
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 java.lang.invoke.MethodHandles;
9+
import java.util.concurrent.CompletableFuture;
10+
import java.util.concurrent.CompletionStage;
11+
12+
import org.hibernate.engine.jdbc.mutation.JdbcValueBindings;
13+
import org.hibernate.engine.jdbc.mutation.MutationExecutor;
14+
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
15+
import org.hibernate.engine.jdbc.mutation.spi.MutationExecutorService;
16+
import org.hibernate.engine.spi.SessionFactoryImplementor;
17+
import org.hibernate.engine.spi.SharedSessionContractImplementor;
18+
import org.hibernate.persister.entity.AbstractEntityPersister;
19+
import org.hibernate.persister.entity.mutation.DeleteCoordinatorSoft;
20+
import org.hibernate.persister.entity.mutation.EntityTableMapping;
21+
import org.hibernate.reactive.adaptor.impl.PrepareStatementDetailsAdaptor;
22+
import org.hibernate.reactive.adaptor.impl.PreparedStatementAdaptor;
23+
import org.hibernate.reactive.engine.jdbc.env.internal.ReactiveMutationExecutor;
24+
import org.hibernate.reactive.logging.impl.Log;
25+
import org.hibernate.reactive.logging.impl.LoggerFactory;
26+
import org.hibernate.sql.model.MutationOperation;
27+
import org.hibernate.sql.model.MutationOperationGroup;
28+
29+
import static org.hibernate.engine.jdbc.mutation.internal.ModelMutationHelper.identifiedResultsCheck;
30+
import static org.hibernate.reactive.util.impl.CompletionStages.failedFuture;
31+
import static org.hibernate.reactive.util.impl.CompletionStages.voidFuture;
32+
33+
public class ReactiveDeleteCoordinatorSoft extends DeleteCoordinatorSoft implements ReactiveAbstractDeleteCoordinator {
34+
35+
private static final Log LOG = LoggerFactory.make( Log.class, MethodHandles.lookup() );
36+
37+
private CompletionStage<Void> stage;
38+
39+
public ReactiveDeleteCoordinatorSoft(
40+
AbstractEntityPersister entityPersister,
41+
SessionFactoryImplementor factory) {
42+
super( entityPersister, factory );
43+
}
44+
45+
@Override
46+
public void coordinateDelete(Object entity, Object id, Object version, SharedSessionContractImplementor session) {
47+
throw LOG.nonReactiveMethodCall( "coordinateReactiveDelete" );
48+
}
49+
50+
public CompletionStage<Void> coordinateReactiveDelete(Object entity, Object id, Object version, SharedSessionContractImplementor session) {
51+
try {
52+
super.coordinateDelete( entity, id, version, session );
53+
return stage != null ? stage : voidFuture();
54+
}
55+
catch (Throwable t) {
56+
if ( stage == null ) {
57+
return failedFuture( t );
58+
}
59+
stage.toCompletableFuture().completeExceptionally( t );
60+
return stage;
61+
}
62+
}
63+
64+
@Override
65+
protected void doDynamicDelete(Object entity, Object id, Object rowId, Object[] loadedState, SharedSessionContractImplementor session) {
66+
stage = new CompletableFuture<>();
67+
final MutationOperationGroup operationGroup = generateOperationGroup( null, loadedState, true, session );
68+
final ReactiveMutationExecutor mutationExecutor = mutationExecutor( session, operationGroup );
69+
70+
for ( int i = 0; i < operationGroup.getNumberOfOperations(); i++ ) {
71+
final MutationOperation mutation = operationGroup.getOperation( i );
72+
if ( mutation != null ) {
73+
final String tableName = mutation.getTableDetails().getTableName();
74+
mutationExecutor.getPreparedStatementDetails( tableName );
75+
}
76+
}
77+
applyDynamicDeleteTableDetails( id, rowId, loadedState, mutationExecutor, operationGroup, session );
78+
mutationExecutor.executeReactive(
79+
entity,
80+
null,
81+
null,
82+
(statementDetails, affectedRowCount, batchPosition) -> identifiedResultsCheck(
83+
statementDetails,
84+
affectedRowCount,
85+
batchPosition,
86+
entityPersister(),
87+
id,
88+
factory()
89+
),
90+
session
91+
)
92+
.whenComplete( (o, t) -> mutationExecutor.release() )
93+
.whenComplete( this::complete );
94+
}
95+
96+
@Override
97+
protected void applyId(
98+
Object id,
99+
Object rowId,
100+
MutationExecutor mutationExecutor,
101+
MutationOperationGroup operationGroup,
102+
SharedSessionContractImplementor session) {
103+
final JdbcValueBindings jdbcValueBindings = mutationExecutor.getJdbcValueBindings();
104+
105+
for ( int position = 0; position < operationGroup.getNumberOfOperations(); position++ ) {
106+
final MutationOperation jdbcMutation = operationGroup.getOperation( position );
107+
final EntityTableMapping tableDetails = (EntityTableMapping) jdbcMutation.getTableDetails();
108+
breakDownKeyJdbcValues( id, rowId, session, jdbcValueBindings, tableDetails );
109+
final PreparedStatementDetails statementDetails = mutationExecutor.getPreparedStatementDetails( tableDetails.getTableName() );
110+
if ( statementDetails != null ) {
111+
PreparedStatementAdaptor.bind( statement -> {
112+
PrepareStatementDetailsAdaptor detailsAdaptor = new PrepareStatementDetailsAdaptor(
113+
statementDetails,
114+
statement,
115+
session.getJdbcServices()
116+
);
117+
// force creation of the PreparedStatement
118+
//noinspection resource
119+
detailsAdaptor.resolveStatement();
120+
} );
121+
}
122+
}
123+
}
124+
125+
@Override
126+
protected void doStaticDelete(Object entity, Object id, Object rowId, Object[] loadedState, Object version, SharedSessionContractImplementor session) {
127+
stage = new CompletableFuture<>();
128+
final boolean applyVersion = entity != null;
129+
final MutationOperationGroup operationGroupToUse = entity == null
130+
? resolveNoVersionDeleteGroup( session )
131+
: getStaticDeleteGroup();
132+
133+
final ReactiveMutationExecutor mutationExecutor = mutationExecutor( session, operationGroupToUse );
134+
for ( int position = 0; position < getStaticDeleteGroup().getNumberOfOperations(); position++ ) {
135+
final MutationOperation mutation = getStaticDeleteGroup().getOperation( position );
136+
if ( mutation != null ) {
137+
mutationExecutor.getPreparedStatementDetails( mutation.getTableDetails().getTableName() );
138+
}
139+
}
140+
141+
if ( applyVersion ) {
142+
applyLocking( version, null, mutationExecutor, session );
143+
}
144+
final JdbcValueBindings jdbcValueBindings = mutationExecutor.getJdbcValueBindings();
145+
bindPartitionColumnValueBindings( loadedState, session, jdbcValueBindings );
146+
applyId( id, rowId, mutationExecutor, getStaticDeleteGroup(), session );
147+
mutationExecutor.executeReactive(
148+
entity,
149+
null,
150+
null,
151+
(statementDetails, affectedRowCount, batchPosition) -> identifiedResultsCheck(
152+
statementDetails,
153+
affectedRowCount,
154+
batchPosition,
155+
entityPersister(),
156+
id,
157+
factory()
158+
),
159+
session
160+
)
161+
.thenAccept( v -> mutationExecutor.release() )
162+
.whenComplete( this::complete );
163+
}
164+
165+
private void complete(Object o, Throwable throwable) {
166+
if ( throwable != null ) {
167+
stage.toCompletableFuture().completeExceptionally( throwable );
168+
}
169+
else {
170+
stage.toCompletableFuture().complete( null );
171+
}
172+
}
173+
174+
private ReactiveMutationExecutor mutationExecutor(
175+
SharedSessionContractImplementor session,
176+
MutationOperationGroup operationGroup) {
177+
final MutationExecutorService mutationExecutorService = session
178+
.getFactory()
179+
.getServiceRegistry()
180+
.getService( MutationExecutorService.class );
181+
182+
return (ReactiveMutationExecutor) mutationExecutorService
183+
.createExecutor( this::getBatchKey, operationGroup, session );
184+
}
185+
186+
}

0 commit comments

Comments
 (0)