MyBatis-Flex 逻辑删除的用法小结

 更新时间:2025年02月14日 09:56:48   作者:水w  
本文主要介绍了MyBatis-Flex 逻辑删除的用法小结,主要介绍了@Column注解使用及UpdateChain工具类介绍,具有一定的参考价值,感兴趣的可以了解一下

一、@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 逻辑删除内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java 读取PDF中的文本和图片的方法

    Java 读取PDF中的文本和图片的方法

    本文将介绍通过Java程序来读取PDF文档中的文本和图片的方法。分别调用方法extractText()和extractImages()来读取,需要的朋友可以参考下
    2019-07-07
  • Java 调用Restful API接口的几种方式(HTTPS)

    Java 调用Restful API接口的几种方式(HTTPS)

    这篇文章主要介绍了Java 调用Restful API接口的几种方式(HTTPS),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-02-02
  • @SpringBootApplication注解的使用

    @SpringBootApplication注解的使用

    这篇文章主要介绍了@SpringBootApplication注解的使用,帮助大家更好的理解和学习使用springboot框架,感兴趣的朋友可以了解下
    2021-04-04
  • Spring探秘之如何妙用BeanPostProcessor

    Spring探秘之如何妙用BeanPostProcessor

    BeanPostProcessor也称为Bean后置处理器,它是Spring中定义的接口,在Spring容器的创建过程中会回调BeanPostProcessor中定义的两个方法,这篇文章主要给大家介绍了关于Spring探秘之如何妙用BeanPostProcessor的相关资料,需要的朋友可以参考下
    2022-01-01
  • 在SpringBoot中使用JWT的实现方法

    在SpringBoot中使用JWT的实现方法

    这篇文章主要介绍了在SpringBoot中使用JWT的实现方法,详细的介绍了什么是JWT和JWT实战,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-12-12
  • Spring Cloud微服务架构Sentinel数据双向同步

    Spring Cloud微服务架构Sentinel数据双向同步

    这篇文章主要为大家介绍了Spring Cloud微服务架构Sentinel数据双向同步示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • 简单谈谈Java中的栈和堆

    简单谈谈Java中的栈和堆

    堆和栈都是Java用来在RAM中存放数据的地方,下面这篇文章主要给大家介绍了关于Java中栈和堆的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2021-11-11
  • Mybatis 查询语句条件为枚举类型时报错的解决

    Mybatis 查询语句条件为枚举类型时报错的解决

    这篇文章主要介绍了Mybatis 查询语句条件为枚举类型时报错的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • Java Netty实现心跳机制过程解析

    Java Netty实现心跳机制过程解析

    这篇文章主要介绍了Java Netty实现心跳机制过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • MyBatis实现字段加解密的实践

    MyBatis实现字段加解密的实践

    为了数据安全问题,有时候需要将部分敏感字段加密后再入库,本文主要介绍了MyBatis实现字段加解密的实践,具有一定的参考价值,感兴趣的可以了解一下
    2023-11-11

最新评论