Skip to content

Commit ff1f6ab

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

File tree

5 files changed

+219
-5
lines changed

5 files changed

+219
-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() ).reactiveDelete( 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> reactiveDelete(Object entity, Object id, Object version, SharedSessionContractImplementor session);
15+
}

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

+2-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

@@ -45,6 +45,7 @@ public void delete(Object entity, Object id, Object version, SharedSessionContra
4545
throw LOG.nonReactiveMethodCall( "coordinateReactiveDelete" );
4646
}
4747

48+
@Override
4849
public CompletionStage<Void> reactiveDelete(Object entity, Object id, Object version, SharedSessionContractImplementor session) {
4950
try {
5051
super.delete( entity, id, version, session );
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
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 delete(Object entity, Object id, Object version, SharedSessionContractImplementor session) {
47+
throw LOG.nonReactiveMethodCall( "coordinateReactiveDelete" );
48+
}
49+
50+
@Override
51+
public CompletionStage<Void> reactiveDelete(Object entity, Object id, Object version, SharedSessionContractImplementor session) {
52+
try {
53+
super.delete( entity, id, version, session );
54+
return stage != null ? stage : voidFuture();
55+
}
56+
catch (Throwable t) {
57+
if ( stage == null ) {
58+
return failedFuture( t );
59+
}
60+
stage.toCompletableFuture().completeExceptionally( t );
61+
return stage;
62+
}
63+
}
64+
65+
@Override
66+
protected void doDynamicDelete(Object entity, Object id, Object rowId, Object[] loadedState, SharedSessionContractImplementor session) {
67+
stage = new CompletableFuture<>();
68+
final MutationOperationGroup operationGroup = generateOperationGroup( null, loadedState, true, session );
69+
final ReactiveMutationExecutor mutationExecutor = mutationExecutor( session, operationGroup );
70+
71+
for ( int i = 0; i < operationGroup.getNumberOfOperations(); i++ ) {
72+
final MutationOperation mutation = operationGroup.getOperation( i );
73+
if ( mutation != null ) {
74+
final String tableName = mutation.getTableDetails().getTableName();
75+
mutationExecutor.getPreparedStatementDetails( tableName );
76+
}
77+
}
78+
applyDynamicDeleteTableDetails( id, rowId, loadedState, mutationExecutor, operationGroup, session );
79+
mutationExecutor.executeReactive(
80+
entity,
81+
null,
82+
null,
83+
(statementDetails, affectedRowCount, batchPosition) -> identifiedResultsCheck(
84+
statementDetails,
85+
affectedRowCount,
86+
batchPosition,
87+
entityPersister(),
88+
id,
89+
factory()
90+
),
91+
session
92+
)
93+
.whenComplete( (o, t) -> mutationExecutor.release() )
94+
.whenComplete( this::complete );
95+
}
96+
97+
@Override
98+
protected void applyId(
99+
Object id,
100+
Object rowId,
101+
MutationExecutor mutationExecutor,
102+
MutationOperationGroup operationGroup,
103+
SharedSessionContractImplementor session) {
104+
final JdbcValueBindings jdbcValueBindings = mutationExecutor.getJdbcValueBindings();
105+
106+
for ( int position = 0; position < operationGroup.getNumberOfOperations(); position++ ) {
107+
final MutationOperation jdbcMutation = operationGroup.getOperation( position );
108+
final EntityTableMapping tableDetails = (EntityTableMapping) jdbcMutation.getTableDetails();
109+
breakDownKeyJdbcValues( id, rowId, session, jdbcValueBindings, tableDetails );
110+
final PreparedStatementDetails statementDetails = mutationExecutor.getPreparedStatementDetails( tableDetails.getTableName() );
111+
if ( statementDetails != null ) {
112+
PreparedStatementAdaptor.bind( statement -> {
113+
PrepareStatementDetailsAdaptor detailsAdaptor = new PrepareStatementDetailsAdaptor(
114+
statementDetails,
115+
statement,
116+
session.getJdbcServices()
117+
);
118+
// force creation of the PreparedStatement
119+
//noinspection resource
120+
detailsAdaptor.resolveStatement();
121+
} );
122+
}
123+
}
124+
}
125+
126+
@Override
127+
protected void doStaticDelete(Object entity, Object id, Object rowId, Object[] loadedState, Object version, SharedSessionContractImplementor session) {
128+
stage = new CompletableFuture<>();
129+
final boolean applyVersion = entity != null;
130+
final MutationOperationGroup operationGroupToUse = entity == null
131+
? resolveNoVersionDeleteGroup( session )
132+
: getStaticMutationOperationGroup();
133+
134+
final ReactiveMutationExecutor mutationExecutor = mutationExecutor( session, operationGroupToUse );
135+
for ( int position = 0; position < getStaticMutationOperationGroup().getNumberOfOperations(); position++ ) {
136+
final MutationOperation mutation = getStaticMutationOperationGroup().getOperation( position );
137+
if ( mutation != null ) {
138+
mutationExecutor.getPreparedStatementDetails( mutation.getTableDetails().getTableName() );
139+
}
140+
}
141+
142+
if ( applyVersion ) {
143+
applyLocking( version, null, mutationExecutor, session );
144+
}
145+
final JdbcValueBindings jdbcValueBindings = mutationExecutor.getJdbcValueBindings();
146+
bindPartitionColumnValueBindings( loadedState, session, jdbcValueBindings );
147+
applyId( id, rowId, mutationExecutor, getStaticMutationOperationGroup(), session );
148+
String[] identifierColumnNames = entityPersister().getIdentifierColumnNames();
149+
mutationExecutor.executeReactive(
150+
entity,
151+
null,
152+
null,
153+
(statementDetails, affectedRowCount, batchPosition) -> identifiedResultsCheck(
154+
statementDetails,
155+
affectedRowCount,
156+
batchPosition,
157+
entityPersister(),
158+
id,
159+
factory()
160+
),
161+
session
162+
)
163+
.thenAccept( v -> mutationExecutor.release() )
164+
.whenComplete( this::complete );
165+
}
166+
167+
private void complete(Object o, Throwable throwable) {
168+
if ( throwable != null ) {
169+
stage.toCompletableFuture().completeExceptionally( throwable );
170+
}
171+
else {
172+
stage.toCompletableFuture().complete( null );
173+
}
174+
}
175+
176+
private ReactiveMutationExecutor mutationExecutor(
177+
SharedSessionContractImplementor session,
178+
MutationOperationGroup operationGroup) {
179+
final MutationExecutorService mutationExecutorService = session
180+
.getFactory()
181+
.getServiceRegistry()
182+
.getService( MutationExecutorService.class );
183+
184+
return (ReactiveMutationExecutor) mutationExecutorService
185+
.createExecutor( this::getBatchKey, operationGroup, session );
186+
}
187+
}

0 commit comments

Comments
 (0)