如果想了解MyBatis-Flex相关知识,可以移步到我的另一篇博客MyBatis-Flex BaseMapper的接口基本用法-****博客
目录
一、@Column
二、逻辑删除相关场景
通过@Column设置字段逻辑删除
在逻辑删除的基础上,自动更新更新时间update_time
跳过逻辑删除处理
一、@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,表示这些数据不再被视为已删除。