Skip to content

Commit 3098d32

Browse files
committed
[#1787] Add support for @SoftDelete
1 parent 836b286 commit 3098d32

7 files changed

+222
-10
lines changed

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

+8-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@
99
import org.hibernate.metamodel.mapping.AttributeMapping;
1010
import org.hibernate.metamodel.mapping.AttributeMappingsList;
1111
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
12+
import org.hibernate.metamodel.mapping.SoftDeleteMapping;
1213
import org.hibernate.persister.entity.AbstractEntityPersister;
14+
import org.hibernate.persister.entity.mutation.DeleteCoordinator;
1315
import org.hibernate.reactive.persister.entity.mutation.ReactiveDeleteCoordinator;
16+
import org.hibernate.reactive.persister.entity.mutation.ReactiveDeleteCoordinatorSoft;
1417
import org.hibernate.reactive.persister.entity.mutation.ReactiveInsertCoordinatorStandard;
1518
import org.hibernate.reactive.persister.entity.mutation.ReactiveUpdateCoordinator;
1619
import org.hibernate.reactive.persister.entity.mutation.ReactiveUpdateCoordinatorNoOp;
@@ -39,10 +42,13 @@ public static ReactiveUpdateCoordinator buildUpdateCoordinator(
3942
return new ReactiveUpdateCoordinatorNoOp( entityPersister );
4043
}
4144

42-
public static ReactiveDeleteCoordinator buildDeleteCoordinator(
45+
public static DeleteCoordinator buildDeleteCoordinator(
46+
SoftDeleteMapping softDeleteMapping,
4347
AbstractEntityPersister entityPersister,
4448
SessionFactoryImplementor factory) {
45-
return new ReactiveDeleteCoordinator( entityPersister, factory );
49+
return softDeleteMapping != null
50+
? new ReactiveDeleteCoordinatorSoft( entityPersister, factory )
51+
: new ReactiveDeleteCoordinator( entityPersister, factory );
4652
}
4753

4854
public static ReactiveUpdateCoordinator buildMergeCoordinator(

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ protected UpdateCoordinator buildUpdateCoordinator() {
152152

153153
@Override
154154
protected DeleteCoordinator buildDeleteCoordinator() {
155-
return ReactiveCoordinatorFactory.buildDeleteCoordinator( this, getFactory() );
155+
return ReactiveCoordinatorFactory.buildDeleteCoordinator( super.getSoftDeleteMapping(), this, getFactory() );
156156
}
157157

158158
@Override

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

+3-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,7 @@ protected InsertCoordinator buildInsertCoordinator() {
115114

116115
@Override
117116
protected DeleteCoordinator buildDeleteCoordinator() {
118-
return ReactiveCoordinatorFactory.buildDeleteCoordinator( this, getFactory() );
117+
return ReactiveCoordinatorFactory.buildDeleteCoordinator( super.getSoftDeleteMapping(), this, getFactory() );
119118
}
120119

121120
@Override
@@ -357,7 +356,7 @@ public CompletionStage<GeneratedValues> insertReactive(Object id, Object[] field
357356

358357
@Override
359358
public CompletionStage<Void> deleteReactive(Object id, Object version, Object entity, SharedSessionContractImplementor session) {
360-
return ( (ReactiveDeleteCoordinator) getDeleteCoordinator() ).reactiveDelete( entity, id, version, session );
359+
return ( (ReactiveAbstractDeleteCoordinator) getDeleteCoordinator() ).reactiveDelete( entity, id, version, session );
361360
}
362361

363362
/**

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ protected UpdateCoordinator buildUpdateCoordinator() {
170170

171171
@Override
172172
protected DeleteCoordinator buildDeleteCoordinator() {
173-
return ReactiveCoordinatorFactory.buildDeleteCoordinator( this, getFactory() );
173+
return ReactiveCoordinatorFactory.buildDeleteCoordinator( super.getSoftDeleteMapping(), this, getFactory() );
174174
}
175175

176176
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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+
/**
13+
* With this interface we can have multiple delete coordinators that extend {@link org.hibernate.persister.entity.mutation.AbstractDeleteCoordinator}.
14+
*
15+
* @see ReactiveDeleteCoordinatorSoft
16+
* @see ReactiveDeleteCoordinator
17+
*/
18+
public interface ReactiveAbstractDeleteCoordinator {
19+
20+
CompletionStage<Void> reactiveDelete(Object entity, Object id, Object version, SharedSessionContractImplementor session);
21+
}

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

+2-2
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 );
@@ -142,7 +143,6 @@ protected void doStaticDelete(Object entity, Object id, Object rowId, Object[] l
142143
final JdbcValueBindings jdbcValueBindings = mutationExecutor.getJdbcValueBindings();
143144
bindPartitionColumnValueBindings( loadedState, session, jdbcValueBindings );
144145
applyId( id, rowId, mutationExecutor, getStaticMutationOperationGroup(), session );
145-
String[] identifierColumnNames = entityPersister().getIdentifierColumnNames();
146146
mutationExecutor.executeReactive(
147147
entity,
148148
null,
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 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+
mutationExecutor.executeReactive(
149+
entity,
150+
null,
151+
null,
152+
(statementDetails, affectedRowCount, batchPosition) -> identifiedResultsCheck(
153+
statementDetails,
154+
affectedRowCount,
155+
batchPosition,
156+
entityPersister(),
157+
id,
158+
factory()
159+
),
160+
session
161+
)
162+
.thenAccept( v -> mutationExecutor.release() )
163+
.whenComplete( this::complete );
164+
}
165+
166+
private void complete(Object o, Throwable throwable) {
167+
if ( throwable != null ) {
168+
stage.toCompletableFuture().completeExceptionally( throwable );
169+
}
170+
else {
171+
stage.toCompletableFuture().complete( null );
172+
}
173+
}
174+
175+
private ReactiveMutationExecutor mutationExecutor(
176+
SharedSessionContractImplementor session,
177+
MutationOperationGroup operationGroup) {
178+
final MutationExecutorService mutationExecutorService = session
179+
.getFactory()
180+
.getServiceRegistry()
181+
.getService( MutationExecutorService.class );
182+
183+
return (ReactiveMutationExecutor) mutationExecutorService
184+
.createExecutor( this::getBatchKey, operationGroup, session );
185+
}
186+
}

0 commit comments

Comments
 (0)