跳到主要内容

实体表

想让实体支持逻辑删除,需要添加一个被org.babyfish.jimmer.sql.LogicalDeleted标记的标志属性,以表示该数据是正常的还是已经被删除。

一旦为实体配置了逻辑删除属性

  • 默认情况下,开发人员调用API删除某个实体对象时,Jimmer并不会用SQL的delete语句真正删除数据,而是使用update语句把该实体的逻辑删除属性设置为“已经被删除“

    其他情况:即便实体具备逻辑删除属性,Jimmer也提供API让开发人员通过额外的参数强行真正删除对象。

  • 默认情况下,所有针对实体的SQL查询都会被自动加上where 软删除标志 <> 已经被删除的条件,从而营造出某些数据已经被删除的假象。

信息

本文只介绍逻辑删除标志字段的映射,至于逻辑删除功能的的使用,请参见全局过滤器/逻辑删除

用法

逻辑删除标志属性可以是如下类型之一

  • boolean:必须非null
  • int:必须非null
  • 枚举:必须非null
  • long/Long:null或非null皆可
  • UUID: null或非null皆可
  • 日期:必须可null
类型代码删除状态初始状态
boolean
@LogicalDeleted("true")
boolean deleted();
truefalse
@LogicalDeleted("false")
val active: Boolean;
falsetrue
int
@Default("0")
@LogicalDeleted("1")
int state();
10
枚举
@Default("INITIALIZED")
@LogicalDeleted("DELETED")
State state();
DELETEDINITIALIZED

long

@LogicalDeleted
long deletedMillis();
当前时钟毫秒数0L

Nullable Long

@LogicalDeleted
Long deletedMillis();
当前时钟毫秒数null

UUID

@LogicalDeleted
UUID deletedData();
随机UUID所有字节为0的UUID

Nullable UUID

@Nullable
@LogicalDeleted
UUID deletedData();
随机UUIDnull

Nullable LocalDateTime

@Nullable
@LogicalDeleted("now")
UUID deletedTime();
当前时间null
@Nullable
@LogicalDeleted("null")
UUID createdTime();
null当前时间

其中

  • 第一列或第二列中的 ✩ 表示当前配置方法支持下一节即将讨论的多版本数据。

    信息

    支持逻辑删除却不考虑多版本数据的问题,是一种成熟度不高的考虑,因此建议使用支持多版本的逻辑删除配置。

  • 当前时钟毫秒数,默认行为是System.currentMillis(),即是默认的org.babyfish.jimmer.sql.meta.LogicalDeletedLongGenerator的行为。

    如果对此行为不满意,可以自定义类实现LogicalDeletedValueGenerator<Long>接口,并用如下配置

    • @LogicalDeleted(generatedType = YourGenerator.class)

    • @LogicalDeleted(generatedRef = YourGenerator.class),这里generatorRef表示对象在IOC容器管理框架中的名称

  • 随机UUD,默认行为是UUID.randomUUID(),即是默认的org.babyfish.jimmer.sql.meta.LogicalDeletedUUIDGenerator的行为。

    如果对此行为不满意,可以自定义类实现LogicalDeletedValueGenerator<UUID>接口,并用如下配置

    • @LogicalDeleted(generatedType = YourGenerator.class)

    • @LogicalDeleted(generatedRef = YourGenerator.class),这里generatorRef表示对象在IOC容器管理框架中的名称

多版本数据

逻辑删除并不会导致数据被真正删除,只会导致数据被隐藏,这代表着数据出现多版本问题。此问题在Key/多版本支持中也有阐述。

以支持多版本的long类型逻辑删除为例

@Entity
public interface Book {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
long id();

@Key
String name();

@Key
int edition();

@LogicalDeleted
long deletedMillis();

BigDecimal price();

@ManyToOne
BookStore store();
}

虽然Book对象的Key为nameedition,但是由于deletedMillis属性为逻辑删除标志,所以数据库层面的非主键UNIQUE约束应该为

alter table BOOK
add constraint uq_key_BOOK
unique(NAME, EDITION, DELETED_MILLIS);

假如表格输入如下

IDNAMEEDITIONPRICESTORE_IDDELETED_MILLIS
1027SQL in Action149.99230
1026SQL in Action155.99221708796420956
1025SQL in Action147.99231708234681901
3131SQL in Action259.99230
3130SQL in Action253.99221708722582793
3129SQL in Action258.99231708664484823

其中有4条关联数据被隐藏,有效数据只有两条

IDNAMEEDITIONPRICESTORE_IDDELETED_MILLIS
1027SQL in Action149.99230
3131SQL in Action259.99230

对中间表的影响

如果实体被逻辑删除,那么和它相关的基于中间表 (采用@JoinTable的注解) 的关联会受到何种影响呢?

  • 如果中间表也支持逻辑删除,即@JoinTable注解的logicalDeletedFilter参数被指定,与被逻辑删除实体相关的中间表记录也会被逻辑删除。

    下一篇文档中,我们将会详细讨论中间表的逻辑删除。

  • 如果@JoinTable注解的deletedWhenEndpointIsLogicallyDeleted参数被设置为true,与被逻辑删除实体相关的中间表记录会被物理删除。

  • 如果以上两个条件都不满足,那么与被逻辑删除实体相关的中间表记录将不会被做出任何处理。