HHH-17164 - Proper, first-class soft-delete support
HHH-17311 - Reversed soft delete support https://hibernate.atlassian.net/browse/HHH-17164 https://hibernate.atlassian.net/browse/HHH-17311
This commit is contained in:
parent
348217c899
commit
9d515dd182
|
@ -64,27 +64,17 @@ public @interface SoftDelete {
|
|||
* <p/>
|
||||
* Default depends on {@linkplain #trackActive()} - {@code deleted} if {@code false} and
|
||||
* {@code active} if {@code true}.
|
||||
*
|
||||
* @see SoftDeleteType#getDefaultColumnName()
|
||||
*/
|
||||
String columnName() default "";
|
||||
|
||||
/**
|
||||
* Whether the database value indicates active/inactive, as opposed to the
|
||||
* default of tracking deleted/not-deleted
|
||||
* The strategy to use for storing/reading values to/from the database.
|
||||
* <p/>
|
||||
* By default, the database values are interpreted as <ul>
|
||||
* <li>{@code true} means the row is considered deleted</li>
|
||||
* <li>{@code false} means the row is considered NOT deleted</li>
|
||||
* </ul>
|
||||
* <p/>
|
||||
* Setting this {@code true} reverses the interpretation of the database value <ul>
|
||||
* <li>{@code true} means the row is active (NOT deleted)</li>
|
||||
* <li>{@code false} means the row is inactive (deleted)</li>
|
||||
* </ul>
|
||||
*
|
||||
* @implNote Causes the {@linkplain #converter() conversion} to be wrapped in
|
||||
* a negated conversion.
|
||||
* The strategy also affects the default {@linkplain #columnName() column name}.
|
||||
*/
|
||||
boolean trackActive() default false;
|
||||
SoftDeleteType strategy() default SoftDeleteType.DELETED;
|
||||
|
||||
/**
|
||||
* (Optional) Conversion to apply to determine the appropriate value to
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
|
||||
*/
|
||||
package org.hibernate.annotations;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Enumeration of defines styles of soft-delete
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public enum SoftDeleteType {
|
||||
/**
|
||||
* Tracks rows which are active. The values stored in the database:<dl>
|
||||
* <dt>{@code true}</dt>
|
||||
* <dd>indicates that the row is active (non-deleted)</dd>
|
||||
* <dt>{@code false}</dt>
|
||||
* <dd>indicates that the row is inactive (deleted)</dd>
|
||||
* </dl>
|
||||
*
|
||||
* @implNote Causes the {@linkplain SoftDelete#converter() conversion} to be wrapped in a negation.
|
||||
*/
|
||||
ACTIVE,
|
||||
|
||||
/**
|
||||
* Tracks rows which are deleted. The values stored in the database:<dl>
|
||||
* <dt>{@code true}</dt>
|
||||
* <dd>indicates that the row is deleted</dd>
|
||||
* <dt>{@code false}</dt>
|
||||
* <dd>indicates that the row is non-deleted</dd>
|
||||
* </dl>
|
||||
*/
|
||||
DELETED;
|
||||
|
||||
private final String defaultColumnName;
|
||||
|
||||
SoftDeleteType() {
|
||||
this.defaultColumnName = name().toLowerCase( Locale.ROOT );
|
||||
}
|
||||
|
||||
public String getDefaultColumnName() {
|
||||
return defaultColumnName;
|
||||
}
|
||||
}
|
|
@ -41,10 +41,6 @@ import static org.hibernate.query.sqm.ComparisonOperator.EQUAL;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SoftDeleteHelper {
|
||||
|
||||
public static final String DEFAULT_COLUMN_NAME = "deleted";
|
||||
public static final String DEFAULT_REVERSED_COLUMN_NAME = "active";
|
||||
|
||||
/**
|
||||
* Creates and binds the column and value for modeling the soft-delete in the database
|
||||
*
|
||||
|
@ -80,7 +76,7 @@ public class SoftDeleteHelper {
|
|||
);
|
||||
|
||||
final BasicValue softDeleteIndicatorValue = new BasicValue( context, table );
|
||||
softDeleteIndicatorValue.makeSoftDelete( softDeleteConfig.trackActive() );
|
||||
softDeleteIndicatorValue.makeSoftDelete( softDeleteConfig.strategy() );
|
||||
softDeleteIndicatorValue.setJpaAttributeConverterDescriptor( converterDescriptor );
|
||||
softDeleteIndicatorValue.setImplicitJavaTypeAccess( (typeConfiguration) -> converterDescriptor.getRelationalValueResolvedType().getErasedType() );
|
||||
return softDeleteIndicatorValue;
|
||||
|
@ -112,7 +108,7 @@ public class SoftDeleteHelper {
|
|||
final Database database = context.getMetadataCollector().getDatabase();
|
||||
final PhysicalNamingStrategy namingStrategy = context.getBuildingOptions().getPhysicalNamingStrategy();
|
||||
final String logicalColumnName = coalesce(
|
||||
softDeleteConfig.trackActive() ? DEFAULT_REVERSED_COLUMN_NAME : DEFAULT_COLUMN_NAME,
|
||||
softDeleteConfig.strategy().getDefaultColumnName(),
|
||||
softDeleteConfig.columnName()
|
||||
);
|
||||
final Identifier physicalColumnName = namingStrategy.toPhysicalColumnName(
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.hibernate.Internal;
|
|||
import org.hibernate.MappingException;
|
||||
import org.hibernate.TimeZoneStorageStrategy;
|
||||
import org.hibernate.annotations.SoftDelete;
|
||||
import org.hibernate.annotations.SoftDeleteType;
|
||||
import org.hibernate.annotations.TimeZoneStorageType;
|
||||
import org.hibernate.boot.model.TypeDefinition;
|
||||
import org.hibernate.boot.model.convert.internal.AutoApplicableConverterDescriptorBypassedImpl;
|
||||
|
@ -100,7 +101,7 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
|
|||
private TemporalType temporalPrecision;
|
||||
private TimeZoneStorageType timeZoneStorageType;
|
||||
private boolean isSoftDelete;
|
||||
private boolean isSoftDeleteReversed;
|
||||
private SoftDeleteType softDeleteStrategy;
|
||||
|
||||
private java.lang.reflect.Type resolvedJavaType;
|
||||
|
||||
|
@ -150,13 +151,13 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
|
|||
return isSoftDelete;
|
||||
}
|
||||
|
||||
public boolean isSoftDeleteReversed() {
|
||||
return isSoftDeleteReversed;
|
||||
public SoftDeleteType getSoftDeleteStrategy() {
|
||||
return softDeleteStrategy;
|
||||
}
|
||||
|
||||
public void makeSoftDelete(boolean reversed) {
|
||||
public void makeSoftDelete(SoftDeleteType strategy) {
|
||||
isSoftDelete = true;
|
||||
isSoftDeleteReversed = reversed;
|
||||
softDeleteStrategy = strategy;
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -476,7 +477,7 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
|
|||
}
|
||||
}
|
||||
|
||||
if ( isSoftDeleteReversed() ) {
|
||||
if ( getSoftDeleteStrategy() == SoftDeleteType.ACTIVE ) {
|
||||
attributeConverterDescriptor = new ReversedConverterDescriptor<>( attributeConverterDescriptor );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.orm.test.softdelete;
|
||||
|
||||
import org.hibernate.annotations.SoftDelete;
|
||||
import org.hibernate.annotations.SoftDeleteType;
|
||||
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
|
||||
import org.hibernate.type.NumericBooleanConverter;
|
||||
import org.hibernate.type.TrueFalseConverter;
|
||||
|
@ -115,7 +116,7 @@ public class MappingTests {
|
|||
|
||||
@Entity(name="ReversedYesNoEntity")
|
||||
@Table(name="reversed_yes_no_entity")
|
||||
@SoftDelete(converter = YesNoConverter.class, trackActive = true)
|
||||
@SoftDelete(converter = YesNoConverter.class, strategy = SoftDeleteType.ACTIVE)
|
||||
public static class ReversedYesNoEntity {
|
||||
@Id
|
||||
private Integer id;
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.hibernate.Hibernate;
|
|||
import org.hibernate.ObjectNotFoundException;
|
||||
import org.hibernate.annotations.BatchSize;
|
||||
import org.hibernate.annotations.SoftDelete;
|
||||
import org.hibernate.annotations.SoftDeleteType;
|
||||
import org.hibernate.type.YesNoConverter;
|
||||
|
||||
import org.hibernate.testing.jdbc.SQLStatementInspector;
|
||||
|
@ -214,7 +215,7 @@ public class SimpleSoftDeleteTests {
|
|||
@Entity(name="BatchLoadable")
|
||||
@Table(name="batch_loadable")
|
||||
@BatchSize(size = 5)
|
||||
@SoftDelete(converter = YesNoConverter.class, trackActive = true)
|
||||
@SoftDelete(converter = YesNoConverter.class, strategy = SoftDeleteType.ACTIVE)
|
||||
public static class BatchLoadable {
|
||||
@Id
|
||||
private Integer id;
|
||||
|
|
|
@ -11,6 +11,7 @@ import java.sql.Statement;
|
|||
import org.hibernate.annotations.Fetch;
|
||||
import org.hibernate.annotations.FetchMode;
|
||||
import org.hibernate.annotations.SoftDelete;
|
||||
import org.hibernate.annotations.SoftDeleteType;
|
||||
import org.hibernate.type.YesNoConverter;
|
||||
|
||||
import org.hibernate.testing.jdbc.SQLStatementInspector;
|
||||
|
@ -144,7 +145,7 @@ public class ToOneTests {
|
|||
|
||||
@Entity(name="User")
|
||||
@Table(name="users")
|
||||
@SoftDelete(converter = YesNoConverter.class, trackActive = true)
|
||||
@SoftDelete(converter = YesNoConverter.class, strategy = SoftDeleteType.ACTIVE)
|
||||
public static class User {
|
||||
@Id
|
||||
private Integer id;
|
||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.orm.test.softdelete;
|
|||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.annotations.SQLDelete;
|
||||
import org.hibernate.annotations.SoftDelete;
|
||||
import org.hibernate.annotations.SoftDeleteType;
|
||||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.metamodel.UnsupportedMappingException;
|
||||
|
@ -65,7 +66,7 @@ public class ValidationTests {
|
|||
|
||||
@Entity(name="Address")
|
||||
@Table(name="addresses")
|
||||
@SoftDelete(converter = YesNoConverter.class, trackActive = true)
|
||||
@SoftDelete(converter = YesNoConverter.class, strategy = SoftDeleteType.ACTIVE)
|
||||
public static class Address {
|
||||
@Id
|
||||
private Integer id;
|
||||
|
@ -74,7 +75,7 @@ public class ValidationTests {
|
|||
|
||||
@Entity(name="NoNo")
|
||||
@Table(name="nonos")
|
||||
@SoftDelete(converter = YesNoConverter.class, trackActive = true)
|
||||
@SoftDelete(converter = YesNoConverter.class, strategy = SoftDeleteType.ACTIVE)
|
||||
@SQLDelete( sql = "delete from nonos" )
|
||||
public static class NoNo {
|
||||
@Id
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.hibernate.annotations.BatchSize;
|
|||
import org.hibernate.annotations.Fetch;
|
||||
import org.hibernate.annotations.FetchMode;
|
||||
import org.hibernate.annotations.SoftDelete;
|
||||
import org.hibernate.annotations.SoftDeleteType;
|
||||
import org.hibernate.type.NumericBooleanConverter;
|
||||
import org.hibernate.type.YesNoConverter;
|
||||
|
||||
|
@ -35,13 +36,13 @@ public class CollectionOwner2 {
|
|||
@ElementCollection
|
||||
@CollectionTable(name="batch_loadables", joinColumns = @JoinColumn(name="owner_fk"))
|
||||
@BatchSize(size = 5)
|
||||
@SoftDelete(converter = YesNoConverter.class, trackActive = true)
|
||||
@SoftDelete(converter = YesNoConverter.class, strategy = SoftDeleteType.ACTIVE)
|
||||
private Set<String> batchLoadable;
|
||||
|
||||
@ElementCollection
|
||||
@CollectionTable(name="subselect_loadables", joinColumns = @JoinColumn(name="owner_fk"))
|
||||
@Fetch(FetchMode.SUBSELECT)
|
||||
@SoftDelete(converter = NumericBooleanConverter.class, trackActive = true)
|
||||
@SoftDelete(converter = NumericBooleanConverter.class, strategy = SoftDeleteType.ACTIVE)
|
||||
private Set<String> subSelectLoadable;
|
||||
|
||||
public CollectionOwner2() {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.orm.test.softdelete.converter.reversed;
|
||||
|
||||
import org.hibernate.annotations.SoftDelete;
|
||||
import org.hibernate.annotations.SoftDeleteType;
|
||||
import org.hibernate.type.YesNoConverter;
|
||||
|
||||
import jakarta.persistence.Basic;
|
||||
|
@ -20,7 +21,7 @@ import jakarta.persistence.Table;
|
|||
@Table(name = "the_entity")
|
||||
//tag::example-soft-delete-reverse[]
|
||||
@Entity
|
||||
@SoftDelete(converter = YesNoConverter.class, trackActive = true)
|
||||
@SoftDelete(converter = YesNoConverter.class, strategy = SoftDeleteType.ACTIVE)
|
||||
public class TheEntity {
|
||||
// ...
|
||||
//end::example-soft-delete-reverse[]
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.orm.test.softdelete.converter.reversed;
|
||||
|
||||
import org.hibernate.annotations.SoftDelete;
|
||||
import org.hibernate.annotations.SoftDeleteType;
|
||||
|
||||
import jakarta.persistence.Basic;
|
||||
import jakarta.persistence.Entity;
|
||||
|
@ -19,7 +20,7 @@ import jakarta.persistence.Table;
|
|||
@Table(name = "the_entity2")
|
||||
//tag::example-soft-delete-reverse[]
|
||||
@Entity
|
||||
@SoftDelete(trackActive = true)
|
||||
@SoftDelete(strategy = SoftDeleteType.ACTIVE)
|
||||
public class TheEntity2 {
|
||||
// ...
|
||||
//end::example-soft-delete-reverse[]
|
||||
|
|
Loading…
Reference in New Issue