Skip to content

Commit d3cb87e

Browse files
committed
HHH-17325 - @softdelete with timestamp
1 parent e1808bd commit d3cb87e

File tree

12 files changed

+99
-72
lines changed

12 files changed

+99
-72
lines changed

hibernate-core/src/main/java/org/hibernate/annotations/SoftDelete.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.hibernate.dialect.Dialect;
1414

1515
import jakarta.persistence.AttributeConverter;
16+
import org.hibernate.metamodel.UnsupportedMappingException;
1617

1718
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
1819
import static java.lang.annotation.ElementType.FIELD;
@@ -106,9 +107,9 @@
106107
* the {@linkplain Dialect#getPreferredSqlTypeCodeForBoolean() dialect}
107108
* and {@linkplain org.hibernate.cfg.MappingSettings#PREFERRED_BOOLEAN_JDBC_TYPE settings}
108109
*
109-
* @apiNote Only relevant when {@linkplain #strategy} is {@linkplain SoftDeleteType#DELETED}
110-
* or {@linkplain SoftDeleteType#ACTIVE}. Ignored when {@linkplain #strategy} is
111-
* {@linkplain SoftDeleteType#TIMESTAMP}.
110+
* @apiNote Only valid when {@linkplain #strategy} is {@linkplain SoftDeleteType#DELETED}
111+
* or {@linkplain SoftDeleteType#ACTIVE}. Will lead to a {@linkplain UnsupportedMappingException}
112+
* when combined with {@linkplain SoftDeleteType#TIMESTAMP}.
112113
*
113114
* @implSpec The specified converter should never return {@code null}
114115
*/

hibernate-core/src/main/java/org/hibernate/metamodel/mapping/SoftDeleteMapping.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
import org.hibernate.sql.ast.tree.predicate.Predicate;
1212
import org.hibernate.sql.ast.tree.update.Assignment;
1313
import org.hibernate.sql.model.ast.ColumnValueBinding;
14-
import org.hibernate.sql.model.ast.builder.ColumnValuesTableMutationBuilder;
15-
import org.hibernate.sql.model.ast.builder.RestrictedTableMutationBuilder;
1614

1715
/**
1816
*
@@ -81,10 +79,6 @@ public interface SoftDeleteMapping extends SelectableMapping, VirtualModelPart,
8179
*/
8280
ColumnValueBinding createDeletedValueBinding(ColumnReference softDeleteColumnReference);
8381

84-
void applyNonDeletedRestriction(RestrictedTableMutationBuilder<?, ?> tableMutationBuilder);
85-
void applyDeletedAssignment(ColumnValuesTableMutationBuilder<?> tableMutationBuilder);
86-
void applyNonDeletedAssignment(ColumnValuesTableMutationBuilder<?> tableMutationBuilder);
87-
8882

8983
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9084
// SelectableMapping

hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SoftDeleteMappingImpl.java

Lines changed: 29 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import org.hibernate.metamodel.mapping.SoftDeleteMapping;
2121
import org.hibernate.metamodel.mapping.TableDetails;
2222
import org.hibernate.metamodel.model.domain.NavigableRole;
23-
import org.hibernate.query.sqm.function.FunctionRenderer;
2423
import org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression;
2524
import org.hibernate.spi.NavigablePath;
2625
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
@@ -36,8 +35,6 @@
3635
import org.hibernate.sql.ast.tree.update.Assignment;
3736
import org.hibernate.sql.model.ast.ColumnValueBinding;
3837
import org.hibernate.sql.model.ast.ColumnWriteFragment;
39-
import org.hibernate.sql.model.ast.builder.ColumnValuesTableMutationBuilder;
40-
import org.hibernate.sql.model.ast.builder.RestrictedTableMutationBuilder;
4138
import org.hibernate.sql.results.graph.DomainResult;
4239
import org.hibernate.sql.results.graph.DomainResultCreationState;
4340
import org.hibernate.sql.results.graph.basic.BasicResult;
@@ -69,8 +66,7 @@ public class SoftDeleteMappingImpl implements SoftDeleteMapping {
6966

7067
// TIMESTAMP
7168
private final String currentTimestampFunctionName;
72-
private final BasicType<?> currentTimestampFunctionType;
73-
private final FunctionRenderer currentTimestampFunction;
69+
private final SelfRenderingFunctionSqlAstExpression<?> currentTimestampFunctionExpression;
7470

7571
// ACTIVE/DELETED
7672
private final Object deletedLiteralValue;
@@ -101,12 +97,22 @@ public SoftDeleteMappingImpl(
10197

10298
if ( bootMapping.getSoftDeleteStrategy() == SoftDeleteType.TIMESTAMP ) {
10399
this.currentTimestampFunctionName = dialect.currentTimestamp();
104-
this.currentTimestampFunctionType = modelCreationProcess.getCreationContext().getTypeConfiguration().getBasicTypeForJavaType( Instant.class );
105-
this.currentTimestampFunction = new CurrentFunction(
100+
final BasicType<?> currentTimestampFunctionType = modelCreationProcess
101+
.getCreationContext()
102+
.getTypeConfiguration()
103+
.getBasicTypeForJavaType( Instant.class );
104+
final CurrentFunction currentTimestampFunction = new CurrentFunction(
106105
currentTimestampFunctionName,
107106
currentTimestampFunctionName,
108107
currentTimestampFunctionType
109108
);
109+
this.currentTimestampFunctionExpression = new SelfRenderingFunctionSqlAstExpression<>(
110+
currentTimestampFunctionName,
111+
currentTimestampFunction,
112+
Collections.emptyList(),
113+
currentTimestampFunctionType,
114+
softDeletable
115+
);
110116

111117
this.deletionIndicator = currentTimestampFunctionName;
112118

@@ -138,11 +144,24 @@ public SoftDeleteMappingImpl(
138144
this.deletionIndicator = deletedLiteralValue;
139145

140146
this.currentTimestampFunctionName = null;
141-
this.currentTimestampFunctionType = null;
142-
this.currentTimestampFunction = null;
147+
this.currentTimestampFunctionExpression = null;
143148
}
144149
}
145150

151+
private static String renderedCurrentTimestampCall(
152+
SelfRenderingFunctionSqlAstExpression<?> functionExpression,
153+
Dialect dialect,
154+
MappingModelCreationProcess modelCreationProcess) {
155+
final StringBuilder buffer = new StringBuilder();
156+
157+
functionExpression.renderToSql(
158+
buffer::append,
159+
null,
160+
modelCreationProcess.getCreationContext().getSessionFactory()
161+
);
162+
return buffer.toString();
163+
}
164+
146165
@Override
147166
public SoftDeleteType getSoftDeleteStrategy() {
148167
return strategy;
@@ -173,13 +192,7 @@ public Assignment createSoftDeleteAssignment(TableReference tableReference) {
173192
final Expression valueExpression;
174193

175194
if ( strategy == SoftDeleteType.TIMESTAMP ) {
176-
valueExpression = new SelfRenderingFunctionSqlAstExpression<>(
177-
currentTimestampFunctionName,
178-
currentTimestampFunction,
179-
Collections.emptyList(),
180-
currentTimestampFunctionType,
181-
softDeletable
182-
);
195+
valueExpression = currentTimestampFunctionExpression;
183196
}
184197
else {
185198
//noinspection rawtypes,unchecked
@@ -256,45 +269,6 @@ public ColumnValueBinding createDeletedValueBinding(ColumnReference softDeleteCo
256269
return new ColumnValueBinding( softDeleteColumnReference, deletedFragment );
257270
}
258271

259-
@Override
260-
public void applyNonDeletedRestriction(RestrictedTableMutationBuilder<?, ?> tableMutationBuilder) {
261-
if ( strategy == SoftDeleteType.TIMESTAMP ) {
262-
tableMutationBuilder.addNullRestriction( this );
263-
}
264-
else {
265-
tableMutationBuilder.addLiteralRestriction(
266-
getSelectionExpression(),
267-
nonDeletedLiteralText,
268-
getJdbcMapping()
269-
);
270-
}
271-
}
272-
273-
@Override
274-
public void applyDeletedAssignment(ColumnValuesTableMutationBuilder<?> tableMutationBuilder) {
275-
if ( strategy == SoftDeleteType.TIMESTAMP ) {
276-
tableMutationBuilder.addValueColumn( getColumnName(), currentTimestampFunctionName, getJdbcMapping() );
277-
}
278-
else {
279-
tableMutationBuilder.addValueColumn( getColumnName(), deletedLiteralText, getJdbcMapping() );
280-
}
281-
}
282-
283-
@Override
284-
public void applyNonDeletedAssignment(ColumnValuesTableMutationBuilder<?> tableMutationBuilder) {
285-
if ( strategy == SoftDeleteType.TIMESTAMP ) {
286-
final ColumnReference columnReference = new ColumnReference(
287-
tableMutationBuilder.getMutatingTable(),
288-
this
289-
);
290-
final ColumnValueBinding nonDeletedValueBinding = createNonDeletedValueBinding( columnReference );
291-
tableMutationBuilder.addValueColumn( nonDeletedValueBinding );
292-
}
293-
else {
294-
tableMutationBuilder.addValueColumn( getColumnName(), nonDeletedLiteralText, getJdbcMapping() );
295-
}
296-
}
297-
298272
@Override
299273
public JdbcMapping getJdbcMapping() {
300274
return jdbcMapping;

hibernate-core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,8 @@ else if ( indexDescriptor != null ) {
327327

328328
final SoftDeleteMapping softDeleteMapping = getAttributeMapping().getSoftDeleteMapping();
329329
if ( softDeleteMapping != null ) {
330-
softDeleteMapping.applyNonDeletedAssignment( insertBuilder );
330+
final ColumnReference columnReference = new ColumnReference( insertBuilder.getMutatingTable(), softDeleteMapping );
331+
insertBuilder.addValueColumn( softDeleteMapping.createNonDeletedValueBinding( columnReference ) );
331332
}
332333
}
333334

@@ -659,8 +660,11 @@ protected RestrictedTableMutation<JdbcMutationOperation> generateSoftDeleteRowsA
659660
}
660661
}
661662

662-
softDeleteMapping.applyDeletedAssignment( updateBuilder );
663-
softDeleteMapping.applyNonDeletedRestriction( updateBuilder );
663+
final ColumnReference softDeleteColumnReference = new ColumnReference( tableReference, softDeleteMapping );
664+
// apply the assignment
665+
updateBuilder.addValueColumn( softDeleteMapping.createDeletedValueBinding( softDeleteColumnReference ) );
666+
// apply the restriction
667+
updateBuilder.addNonKeyRestriction( softDeleteMapping.createNonDeletedValueBinding( softDeleteColumnReference ) );
664668

665669
return updateBuilder.buildMutation();
666670
}

hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@
226226
import org.hibernate.sql.exec.spi.JdbcParametersList;
227227
import org.hibernate.sql.model.MutationOperation;
228228
import org.hibernate.sql.model.MutationOperationGroup;
229+
import org.hibernate.sql.model.ast.ColumnValueBinding;
230+
import org.hibernate.sql.model.ast.MutatingTableReference;
229231
import org.hibernate.sql.model.ast.builder.MutationGroupBuilder;
230232
import org.hibernate.sql.model.ast.builder.TableInsertBuilder;
231233
import org.hibernate.sql.results.graph.DomainResult;
@@ -3458,7 +3460,10 @@ public void addDiscriminatorToInsertGroup(MutationGroupBuilder insertGroupBuilde
34583460
public void addSoftDeleteToInsertGroup(MutationGroupBuilder insertGroupBuilder) {
34593461
if ( softDeleteMapping != null ) {
34603462
final TableInsertBuilder insertBuilder = insertGroupBuilder.getTableDetailsBuilder( getIdentifierTableName() );
3461-
softDeleteMapping.applyNonDeletedAssignment( insertBuilder );
3463+
final MutatingTableReference mutatingTable = insertBuilder.getMutatingTable();
3464+
final ColumnReference columnReference = new ColumnReference( mutatingTable, softDeleteMapping );
3465+
final ColumnValueBinding nonDeletedValueBinding = softDeleteMapping.createNonDeletedValueBinding( columnReference );
3466+
insertBuilder.addValueColumn( nonDeletedValueBinding );
34623467
}
34633468
}
34643469

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.hibernate.metamodel.mapping.SelectableMapping;
1313
import org.hibernate.metamodel.mapping.SoftDeleteMapping;
1414
import org.hibernate.persister.entity.EntityPersister;
15+
import org.hibernate.sql.ast.tree.expression.ColumnReference;
1516
import org.hibernate.sql.model.MutationOperation;
1617
import org.hibernate.sql.model.MutationOperationGroup;
1718
import org.hibernate.sql.model.MutationType;
@@ -81,8 +82,12 @@ private void applyPartitionKeyRestriction(TableUpdateBuilder<?> tableUpdateBuild
8182
private void applySoftDelete(
8283
SoftDeleteMapping softDeleteMapping,
8384
TableUpdateBuilderStandard<MutationOperation> tableUpdateBuilder) {
84-
softDeleteMapping.applyDeletedAssignment( tableUpdateBuilder );
85-
softDeleteMapping.applyNonDeletedRestriction( tableUpdateBuilder );
85+
final ColumnReference softDeleteColumnReference = new ColumnReference( tableUpdateBuilder.getMutatingTable(), softDeleteMapping );
86+
87+
// apply the assignment
88+
tableUpdateBuilder.addValueColumn( softDeleteMapping.createDeletedValueBinding( softDeleteColumnReference ) );
89+
// apply the restriction
90+
tableUpdateBuilder.addNonKeyRestriction( softDeleteMapping.createNonDeletedValueBinding( softDeleteColumnReference ) );
8691
}
8792

8893
protected void applyOptimisticLocking(

hibernate-core/src/main/java/org/hibernate/sql/model/ast/ColumnValueBindingList.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import java.util.ArrayList;
88

9+
import org.hibernate.Incubating;
910
import org.hibernate.Internal;
1011
import org.hibernate.engine.jdbc.mutation.ParameterUsage;
1112
import org.hibernate.metamodel.mapping.JdbcMapping;
@@ -45,6 +46,11 @@ public void consume(int valueIndex, Object value, SelectableMapping jdbcValueMap
4546
add( columnValueBinding );
4647
}
4748

49+
@Internal @Incubating
50+
public void addRestriction(ColumnValueBinding valueBinding) {
51+
add( valueBinding );
52+
}
53+
4854
public void addNullRestriction(SelectableMapping column) {
4955
add( createValueBinding( column.getSelectionExpression(), null, column.getJdbcMapping() ) );
5056
}

hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/AbstractRestrictedTableMutationBuilder.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.hibernate.sql.model.MutationTarget;
1313
import org.hibernate.sql.model.MutationType;
1414
import org.hibernate.sql.model.TableMapping;
15+
import org.hibernate.sql.model.ast.ColumnValueBinding;
1516
import org.hibernate.sql.model.ast.ColumnValueBindingList;
1617
import org.hibernate.sql.model.ast.MutatingTableReference;
1718
import org.hibernate.sql.model.ast.RestrictedTableMutation;
@@ -59,6 +60,11 @@ public ColumnValueBindingList getOptimisticLockBindings() {
5960
return optimisticLockBindings;
6061
}
6162

63+
@Override
64+
public void addNonKeyRestriction(ColumnValueBinding valueBinding) {
65+
optimisticLockBindings.addRestriction( valueBinding );
66+
}
67+
6268
@Override
6369
public void addKeyRestriction(String columnName, String columnWriteFragment, JdbcMapping jdbcMapping) {
6470
keyRestrictionBindings.addRestriction( columnName, columnWriteFragment, jdbcMapping );

hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/ColumnValuesTableMutationBuilder.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
*/
55
package org.hibernate.sql.model.ast.builder;
66

7+
import org.hibernate.Incubating;
8+
import org.hibernate.Internal;
79
import org.hibernate.metamodel.mapping.JdbcMapping;
810
import org.hibernate.metamodel.mapping.SelectableMapping;
911
import org.hibernate.sql.model.ast.ColumnValueBinding;
@@ -16,6 +18,16 @@
1618
* @author Gavin King
1719
*/
1820
public interface ColumnValuesTableMutationBuilder<M extends TableMutation<?>> extends TableMutationBuilder<M> {
21+
/**
22+
* Adds a restriction, which is assumed to be based on a table key.
23+
*
24+
* @apiNote Be sure you know what you are doing before using this method. Generally
25+
* prefer any of the other methods here for adding key restrictions.
26+
*/
27+
@Internal
28+
@Incubating
29+
void addValueColumn(ColumnValueBinding valueBinding);
30+
1931
/**
2032
* Add a column as part of the values list
2133
*/
@@ -39,8 +51,6 @@ default void addValueColumn(SelectableMapping selectableMapping) {
3951
);
4052
}
4153

42-
void addValueColumn(ColumnValueBinding valueBinding);
43-
4454
/**
4555
* Add a key column
4656
*/

hibernate-core/src/main/java/org/hibernate/sql/model/ast/builder/RestrictedTableMutationBuilder.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
*/
55
package org.hibernate.sql.model.ast.builder;
66

7+
import org.hibernate.Incubating;
8+
import org.hibernate.Internal;
79
import org.hibernate.metamodel.mapping.JdbcMapping;
810
import org.hibernate.metamodel.mapping.SelectableMapping;
911
import org.hibernate.metamodel.mapping.SelectableMappings;
1012
import org.hibernate.sql.model.MutationOperation;
13+
import org.hibernate.sql.model.ast.ColumnValueBinding;
1114
import org.hibernate.sql.model.ast.ColumnValueBindingList;
1215
import org.hibernate.sql.model.ast.RestrictedTableMutation;
1316

@@ -20,6 +23,15 @@
2023
* @author Steve Ebersole
2124
*/
2225
public interface RestrictedTableMutationBuilder<O extends MutationOperation, M extends RestrictedTableMutation<O>> extends TableMutationBuilder<M> {
26+
/**
27+
* Adds a restriction, which is assumed to be based on a selectable that is NOT
28+
* a table key, e.g. optimistic locking.
29+
*
30+
* @apiNote Be sure you know what you are doing before using this method. Generally
31+
* prefer any of the other methods here for adding non-key restrictions.
32+
*/
33+
@Internal @Incubating
34+
void addNonKeyRestriction(ColumnValueBinding valueBinding);
2335

2436
/**
2537
* Add a restriction as long as the selectable is not a formula and is not nullable

0 commit comments

Comments
 (0)