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:
Steve Ebersole 2023-10-25 15:29:13 -05:00
parent ae3c88ab66
commit 5b49d5efba
4 changed files with 71 additions and 54 deletions

View File

@ -23,7 +23,7 @@ Soft delete support is defined by 3 main parts -
1. The <<soft-delete-column,column>> which contains the indicator. 1. The <<soft-delete-column,column>> which contains the indicator.
2. A <<soft-delete-conversion,conversion>> from `Boolean` indicator value to the proper database type 2. A <<soft-delete-conversion,conversion>> from `Boolean` indicator value to the proper database type
3. Whether to <<soft-delete-reverse,reverse>> the indicator values, tracking active/inactive instead 3. A <<soft-delete-type,strategy>> for interpreting the stored indicator values.
[[soft-delete-column]] [[soft-delete-column]]
@ -31,8 +31,12 @@ Soft delete support is defined by 3 main parts -
The column where the indicator value is stored is defined using `@SoftDelete#columnName` attribute. The column where the indicator value is stored is defined using `@SoftDelete#columnName` attribute.
When using <<soft-delete-reverse,reversed>> mappings, the column name defaults to `active`; otherwise, it The default column name depends on the <<soft-delete-type,strategy>> being used -
defaults to the name `deleted`.
ACTIVE::
The default column name is `active`.
DELETED::
The default column name is `deleted`.
See <<soft-delete-basic-example>> for an example of customizing the column name. See <<soft-delete-basic-example>> for an example of customizing the column name.
@ -42,11 +46,11 @@ Depending on the conversion type, an appropriate check constraint may be applied
[[soft-delete-conversion]] [[soft-delete-conversion]]
==== Indicator conversion ==== Indicator conversion
The conversion is defined using a JPA <<basic-jpa-convert,AttributeConverter>>. The "domain type" is always The conversion is defined using a Jakarta Persistence <<basic-jpa-convert,AttributeConverter>>. The domain-type is always
`boolean`. The "relational type" can be any type, as defined by the converter; generally `BOOLEAN`, `BIT`, `INTEGER` or `CHAR`. `boolean`. The relational-type can be any type, as defined by the converter; generally `BOOLEAN`, `BIT`, `INTEGER` or `CHAR`.
An explicit conversion can be specified using `@SoftDelete#converter`. See <<soft-delete-basic-example>> An explicit conversion can be specified using `@SoftDelete#converter`. See <<soft-delete-basic-example>>
for an example of specifying an explicit conversion. Explicit conversions can specify a custom converter or leverage the 3 for an example of specifying an explicit conversion. Explicit conversions can specify a custom converter or leverage
Hibernate-provided converters for the 3 most common cases - Hibernate-provided converters for the 3 most common cases -
`NumericBooleanConverter`:: Defines conversion using `0` for `false` and `1` for `true` `NumericBooleanConverter`:: Defines conversion using `0` for `false` and `1` for `true`
@ -60,6 +64,8 @@ boolean (and bit):: the underlying type is boolean / bit and no conversion is ap
numeric:: the underlying type is integer and values are converted according to `NumericBooleanConverter` numeric:: the underlying type is integer and values are converted according to `NumericBooleanConverter`
character:: the underlying type is char and values are converted according to `TrueFalseConverter` character:: the underlying type is char and values are converted according to `TrueFalseConverter`
IMPORTANT: The converter should simply convert the `true` and `false`, irrespective of the <<soft-delete-type,strategy>> used. Hibernate will handle applying the strategy.
[[soft-delete-entity]] [[soft-delete-entity]]
==== Entity soft delete ==== Entity soft delete
@ -133,35 +139,17 @@ The `@SoftDelete` annotation may also be placed at the package level, in which c
entities and collections defined within the package. entities and collections defined within the package.
[[soft-delete-reverse]] [[soft-delete-type]]
==== Reversed soft delete ==== Strategy - SoftDeleteType
A common requirement in applications using soft delete is to track rows which are active as opposed to removed, Given truth values, there are 2 valid ways to interpret the values stored in the database. This
reversing the boolean value. For example: interpretation is defined by the SoftDeleteType enumeration and can be configured per-usage using
`@SoftDelete(..., strategy=ACTIVE)` or `@SoftDelete(..., strategy=DELETED)` -
[[soft-delete-reverse-example]] ACTIVE::
.Reversed soft-delete Tracks rows which are active. A `true` value in the database indicates that the row is active
==== (non-deleted); a `false` value indicates inactive (deleted).
[source,java] DELETED::
---- Tracks rows which are deleted. A `true` value in the database indicates that the row is deleted;
include::{testing-dir}/converter/reversed/TheEntity.java[tag=example-soft-delete-reverse, indent=0] a `false` value indicates that the row is non-deleted.
----
====
When an instance of `TheEntity` is persisted, the value `'Y'` will be inserted into the
`active` column. When an instance of `TheEntity` is removed, the column's value is updated to `'N'`.
This example explicitly specifies the built-in `YesNoConverter`, but reversal works with any conversion
even implicit conversions -
[[soft-delete-reverse-example-2]]
.Reversed soft-delete with implicit conversion
====
[source,java]
----
include::{testing-dir}/converter/reversed/TheEntity2.java[tag=example-soft-delete-reverse, indent=0]
----
====
The important thing to remember is that the stored values are reversed from the "normal" soft delete state.
`active == true` is the same as `deleted == false` - both describe the same state.

View File

@ -62,8 +62,7 @@ public @interface SoftDelete {
/** /**
* (Optional) The name of the column. * (Optional) The name of the column.
* <p/> * <p/>
* Default depends on {@linkplain #trackActive()} - {@code deleted} if {@code false} and * Default depends on the {@linkplain #strategy() strategy} being used.
* {@code active} if {@code true}.
* *
* @see SoftDeleteType#getDefaultColumnName() * @see SoftDeleteType#getDefaultColumnName()
*/ */

View File

@ -19,20 +19,14 @@ earlier versions, see any other pertinent migration guides as well.
[[soft-delete]] [[soft-delete]]
== Soft Delete == Soft Delete
6.4 adds support for soft deletes against an entity's primary table and collection tables, using the 6.4 adds support for soft deletes, using the new `@SoftDelete` annotation.
new `@SoftDelete` annotation.
[source,java]
----
@Entity
@SoftDelete
class Account {
...
}
----
See the link:{userGuideBase}#soft-delete[User Guide] for details. See the link:{userGuideBase}#soft-delete[User Guide] for details.
In previous versions, support for soft-deletes was somewhat implementable using
a combination of any or all of event-listeners, filters, `@Where`, etc.
Applications using such implementations are encouraged to switch.
[[custom-tenant-identifier-type]] [[custom-tenant-identifier-type]]
== Custom tenant identifier type == Custom tenant identifier type

View File

@ -1,20 +1,56 @@
:family: 6.3
:version: 6.3.0.Final
= Hibernate {version} = Hibernate {version}
Steve Ebersole Steve Ebersole
:awestruct-tags: ["Hibernate ORM", "Releases"] :awestruct-tags: ["Hibernate ORM", "Releases"]
:awestruct-layout: blog-post :awestruct-layout: blog-post
:version: 6.4.0.CR1
:family: 6.4
:docs-url: https://docs.jboss.org/hibernate/orm/{family} :docs-url: https://docs.jboss.org/hibernate/orm/{family}
:javadocs-url: {docs-url}/javadocs :javadocs-url: {docs-url}/javadocs
:migration-guide-url: {docs-url}/migration-guide/migration-guide.html :migration-guide-url: {docs-url}/migration-guide/migration-guide.html
:intro-guide-url: {docs-url}/introduction/html_single/Hibernate_Introduction.html :intro-guide-url: {docs-url}/introduction/html_single/Hibernate_Introduction.html
:user-guide-url: {docs-url}/userguide/html_single/Hibernate_User_Guide.html :user-guide-url: {docs-url}/userguide/html_single/Hibernate_User_Guide.html
// Text ... 6.4 adds some cool new features, in addition to many improvements and fixes.
[[soft-delete]]
== Soft Delete
== Conclusion 6.4 adds support for soft deletes using the new `@SoftDelete` annotation.
[source,java]
----
@Entity
@SoftDelete
class Account {
...
}
----
Dealing with values as deleted/non-deleted versus active/inactive (reversed) is simple using an annotation attribute:
This has the ability to easily handle active v. deleted tracking using a simple annotation attribute:
[source,java]
----
@Entity
@SoftDelete(strategy=ACTIVE)
class Account {
...
}
----
It even supports pluggable converters for storing the indicator value into the database.strategy
See the link:{userGuideBase}#soft-delete[User Guide] for details.
== Finally,
For additional details, see: For additional details, see: