MyBatis-Flex 逻辑删除的用法小结
作者:水w
一、@Column
MyBatis-Flex 提供了 @Column 用来对字段进行更多的配置,以下是 @Column 的代码定义:
public @interface Column { /** * 字段名称 */ String value() default ""; /** * 是否忽略该字段,可能只是业务字段,而非数据库对应字段 */ boolean ignore() default false; /** * insert 的时候默认值,这个值会直接被拼接到 sql 而不通过参数设置 */ String onInsertValue() default ""; /** * update 的时候自动赋值,这个值会直接被拼接到 sql 而不通过参数设置 */ String onUpdateValue() default ""; /** * 是否是大字段,大字段 APT 不会生成到 DEFAULT_COLUMNS 里 */ boolean isLarge() default false; /** * 是否是逻辑删除字段,一张表中只能存在 1 一个逻辑删除字段 * 逻辑删除的字段,被删除时,会设置为 1,正常状态为 0 */ boolean isLogicDelete() default false; /** * 是否为乐观锁字段,若是乐观锁字段的话,数据更新的时候会去检测当前版本号,若更新成功的话会设置当前版本号 +1 * 只能用于数值的字段 */ boolean version() default false; /** * 配置的 jdbcType */ JdbcType jdbcType() default JdbcType.UNDEFINED; /** * 自定义 TypeHandler */ Class<? extends TypeHandler> typeHandler() default UnknownTypeHandler.class; }
二、逻辑删除相关场景
通过@Column设置字段逻辑删除
逻辑删除指的是在删除数据的时候,并非真正的去删除,而是将表中列所对应的状态字段(status)做修改操作, 实际上并未删除目标数据。
我们可以进行表的字段设计时,用一个列标识该数据的 "删除状态",在 mybatis-flex 中,正常状态的值为 0, 已删除 的值为 1(可以通过设置 FlexGlobalConfig 来修改这个值)。
@Column(isLogicDelete = true) private int isDeleted; /* 对应数据库的表结构中为: is_deleted tinyint(1) default 0 numm comment '是否已逻辑删除 0-否 1-是'; */
当 "tb_account" 的数据被删除时( is_delete = 1 时),我们通过 MyBatis-Flex 的 selectOneById 去查找数据时,会查询不到数据。 原因是 selectOneById
会自动添加上 is_delete = 0
条件,执行的 sql 如下:
SELECT * FROM tb_account where id = ? and is_delete = 0
不仅仅是 selectOneById 方法会添加 is_delete = 0
条件,BaseMapper 的以下方法也都会添加该条件:
- selectOneBy**
- selectListBy**
- selectCountBy**
- paginate
同时,比如 Left Join 或者子查询等,若 子表也设置了逻辑删除字段, 那么子表也会添加相应的逻辑删除条件,例如:
QueryWrapper query1 = QueryWrapper.create() .select() .from(ACCOUNT) .leftJoin(ARTICLE).as("a").on(ACCOUNT.ID.eq(ARTICLE.ACCOUNT_ID)) .where(ACCOUNT.AGE.ge(10)); /* 其执行的 SQL 如下: SELECT * FROM `tb_account` LEFT JOIN `tb_article` AS `a` ON `a`.`is_delete` = 0 and `tb_account`.`id` = `a`.`account_id` WHERE `tb_account`.`age` >= 10 AND `tb_account`.`is_delete` = 0 */
在 left join on 条件自动添加a.is_delete = 0,并在 where 条件添加上 tb_account.is_delete = 0。
在逻辑删除的基础上,自动更新更新时间update_time
(1)数据填充指的是,当 Entity 数据被插入或者更新的时候,会为字段进行一些默认的数据设置。这个非常有用,比如当某个 entity 被插入时候 会设置一些数据插入的时间、数据插入的用户 id,多租户的场景下设置当前租户信息等等。
MyBatis-Flex 提供了两种方式,帮助开发者进行数据填充。
- 通过 @Table 注解的 onInsert 和 onUpdate配置进行操作。
- 通过 @Column 注解的 onInsertValue 和 onUpdateValue配置进行操作。
@Table的注解和@Column注解的填充有什么区别?
- @Table 注解的 onInsert 主要是在 Java 应用层面进行数据设置。
- @Column 注解的 onInsertValue 则是在数据库层面进行数据设置。
首先,需要在后端项目的model实体类中,通过注解配置将该类的对象属性createTime和updateTime,映射到数据库表中的特定字段create_time和update_time的默认值设置等信息。
@Column(onInsertValue = "now()") private Date createTime; @Column(onUpdateValue = "now()", onInsertValue = "now()") private Date updateTime; @Column(isLogicDelete = true) private int isDeleted; /* 对应数据库的表结构中为: create_time timestamp null comment '创建时间'; update_time timestamp null comment '更新时间'; is_deleted tinyint(1) default 0 numm comment '是否已逻辑删除 0-否 1-是'; */
其中,onInsertValue表示当数据被更新时,设置的默认值。onInsertValue配置的内容 "now()" 会直接参与 SQL 的赋值拼接。而onUpdateValue表示当数据被更新时,设置的默认值。onUpdateValue配置的内容 "now()" 会直接参与 SQL 的赋值拼接。
在 insert 中,onInsertValue 配置的内容会直接参与 SQL 拼接,而不是通过 JDBC 的 Statement 参数设置,需要开发者注意 onInsertValue 的内容,否则可能会造成 SQL 错误。
(2)UpdateChain 是一个对 UpdateEntity、UpdateWrapper 等进行封装的一个工具类,方便用户用于进行链式操作。
假设我们要更新 Account 的 userName 为 "张三",更新年龄在之前的基础上加 1,更新代码如下:
@Test public void testUpdateChain() { UpdateChain.of(Account.class) .set(Account::getUserName, "张三") .setRaw(Account::getAge, "age + 1") .where(Account::getId).eq(1) .update(); } /* 以上方法调用时,MyBatis-Flex 内部执行的 SQL 如下: sql UPDATE `tb_account` SET `user_name` = '张三' , `age` = age + 1 WHERE `id` = 1 */
经过测试之后,发现dataMapper.deleteBatchById(idsList)似乎不会自动更新时间update_time,不知道为什么。不是说当调用 deleteBatchById()方法时,MyBatis-Flex 会根据实体类信息和注解配置,动态生成一条更新语句,而不是删除语句吗??按道理来说,更新语句不应该会触发更新时间update_time字段的自动更新吗?(没搞明白。。)
因此,可以通过MyBatis-Flex 提供的链式操作方式,来替换掉基础的deletedById()方法。
// dataMapper.deleteBatchByIds(idsList); UpdateChain.of(dataMapper) .set(DataModel::getIsDeleted, 1) .where(DataModel::getIsDeleted, 0) .and(DataModel::getId).in(idsList) .update();
这样MyBatis-Flex框架就会走更新语句的流程,数据库表中就可以完成自动更新update_time更新时间字段了。
跳过逻辑删除处理
在某些场景下,比如说需要构建回收站功能,那我们在执行查询、更新或删除数据时,有必要跳过 MyBatis-Flex 自动添加的逻辑删除的相关条件。
此时,我们可以使用 LogicDeleteManager.execWithoutLogicDelete() 方法处理。这种方法在需要对所有数据进行操作时非常有用,比如批量导出数据、进行数据恢复等场景。
LogicDeleteManager 是一个用于处理逻辑删除的管理器。execWithoutLogicDelete() 方法的作用是在执行某些操作时忽略逻辑删除的规则。这意味着,当使用这个方法执行查询、插入、更新或删除操作时,系统不会考虑逻辑删除标志,即会处理所有数据,包括那些被标记为已删除的数据。
比如,
LogicDeleteManager.execWithoutLogicDelete(()-> accountMapper.deleteById(1) );
以上代码中,accountMapper 会直接对 Account 数据进行物理删除,忽略逻辑删除字段配置。
代码如下:
LogicDeleteManager.execWithoutLogicDelete(()->{ // 此处写逻辑 UpdateChain.of(dataMapper) .set(DataModel::getIsDeleted, 0) .where(DataModel::getIsDeleted, 1) .and(DataModel::getId).in(idsList) .update(); });
上述代码中,UpdateChain.of(dataMapper) 创建了一个数据映射器对象dataMapper的更新链对象。.set(DataModel::getIsDeleted, 0) 设置 DataModel 的 isDeleted 字段值为 0,表示未删除状态。.where(DataModel::getIsDeleted, 1) 指定更新条件之一是isDeleted 字段值为 1,即逻辑上已被删除的数据。.and(DataModel::getId).in(idsList) 添加额外的条件:id 字段的值必须在 idsList 列表中。.update() 执行更新操作。
这段代码的目的是在逻辑删除被忽略的情况下,将指定 id 的数据从逻辑删除状态恢复到未删除状态。具体来说,只有那些 isDeleted 字段值为 1 并且 id 在 idsList 中的数据才会被更新。更新后,这些数据的 isDeleted 字段值会被设置为 0,表示这些数据不再被视为已删除。
到此这篇关于MyBatis-Flex 逻辑删除的用法小结的文章就介绍到这了,更多相关MyBatis-Flex 逻辑删除内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!