实体表
想让实体支持逻辑删除,需要添加一个被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 |
| true | false |
| false | true | |
| int |
| 1 | 0 |
| 枚举 |
| DELETED | INITIALIZED |
✩ long |
| 当前时钟毫秒数 | 0L |
✩ Nullable Long |
| 当前时钟毫秒数 | null |
✩ UUID |
| 随机UUID | 所有字节为0的UUID |
✩ Nullable UUID |
| 随机UUID | null |
Nullable LocalDateTime | ✩
| 当前时间 | null |
| 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类型逻辑删除为例
- Java
- Kotlin
@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();
}
@Entity
interface Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long
@Key
val name: String
@Key
val edition: String
@LogicalDeleted
val deletedMillis: Long
val price: BigDecimal
@ManyToOne
val store: BookStore
}
虽然Book对象的Key为name和edition,但是由于deletedMillis属性为逻辑删除标志,所以数据库层面的非主键UNIQUE约束应该为
alter table BOOK
add constraint uq_key_BOOK
unique(NAME, EDITION, DELETED_MILLIS);
假如表格输入如下
| ID | NAME | EDITION | PRICE | STORE_ID | DELETED_MILLIS |
|---|---|---|---|---|---|
| 1027 | SQL in Action | 1 | 49.99 | 23 | 0 |
| 1026 | SQL in Action | 1 | 55.99 | 22 | 1708796420956 |
| 1025 | SQL in Action | 1 | 47.99 | 23 | 1708234681901 |
| 3131 | SQL in Action | 2 | 59.99 | 23 | 0 |
| 3130 | SQL in Action | 2 | 53.99 | 22 | 1708722582793 |
| 3129 | SQL in Action | 2 | 58.99 | 23 | 1708664484823 |
其中有4条关联数据被隐藏,有效数据只有两条
| ID | NAME | EDITION | PRICE | STORE_ID | DELETED_MILLIS |
|---|---|---|---|---|---|
| 1027 | SQL in Action | 1 | 49.99 | 23 | 0 |
| 3131 | SQL in Action | 2 | 59.99 | 23 | 0 |
对中间表的影响
如果实体被逻辑删除,那么和它相关的基于中间表 (采用@JoinTable的注解) 的关联会受到何种影响呢?
-
如果中间表也支持逻辑删除,即
@JoinTable注解的logicalDeletedFilter参数被指定,与被逻辑删除实体相关的中间表记录也会被逻辑删除。在下一篇文档中,我们将会详细讨论中间表的逻辑删除。
-
如果
@JoinTable注解的deletedWhenEndpointIsLogicallyDeleted参数被设置为true,与被逻辑删除实体相关的中间表记录会被物理删除。 -
如果以上两个条件都不满足,那么与被逻辑删除实体相关的中间表记录将不会被做出任何处理。