前言:在开发过程中,如果在删除数据时,只是通过改变数据中某个字段的值,来达到逻辑删除的目的,但是又不想每次都手动对改字段进行设置,改如何实现;
1 MyBatis-Plus逻辑删除@TableLogic:
MyBatis-Plus 的 @TableLogic 注解是用于标记逻辑删除字段的注解,用于实现逻辑删除功能。并且开启之后使用MyBatis-Plus 自带的CRUD 在最终执行sql 时都会为其追加 一个数据正常的条件,
即: and 定义的逻辑删除字段 = 定义的正常值;
2 SpirngBoot MyBatis-Plus 实现:
2.1 引入 MyBatis-Plus 的包:
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
2.2 在表和实体类中需要增加逻辑删除字段 :
sql:
ALTER TABLE `user`
ADD COLUMN `deleted` tinyint(1) not NULL DEFAULT 0 COMMENT '是否删除(1-删除;0-未删除)';
实体:
/**
* 状态[0:未删除,1:删除]
*/
@TableLogic
private Integer deleted;
2.3 为了看到效果,对日志输出级别进行调整:
2.3.1 首先定义mybatisPlus 输出sql 到控制台的类:
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
2.3.2 然后调整日志输出级别为debug:
logging:
level:
root: debug
log-impl: 在 MyBatis Plus 中,可以通过配置日志实现类来控制 SQL 语句是否输出到控制台。可以使用 org.apache.ibatis.logging.stdout.StdOutImpl 作为日志实现类,这是 MyBatis 自带的一个日志实现类,用于将日志信息输出到标准输出流中(也就是控制台);
在 Spring Boot 中,logging.level.root 是用来配置整个应用程序的默认日志级别。如果没有为特定的日志类别定义日志级别,则该类别将使用默认的日志级别。默认情况下,logging.level.root 的值为 INFO,也就是说,如果没有为特定的日志类别指定日志级别,则该类别将使用 INFO 级别的日志输出。这意味着,只有 INFO 级别及以上的日志信息才会被输出,而低于 INFO 级别的日志信息将被忽略。
StdOutImpl 只会将日志输出到控制台而不输出到日志文件中;
控制台输出的日志级别和 MyBatis Plus 执行的 SQL 语句是否输出到控制台是分开控制的。即使开启了控制台日志输出,也需要设置对应的日志级别才能在控制台上看到 SQL 语句的输出;
需要注意的是,开启 SQL 输出会泄漏 SQL 语句,因此建议在生产环境中关闭 SQL 输出或者使用安全的日志配置方式。同时,SQL 输出的数量和内容可能会对性能和安全性产生影响,需要根据具体情况进行权衡和优化。
3 调试和验证:
3.1 定义好User 对应的Maper,service;
3.2 测试代码:
public User getUser(String userId) {
User user1 = this.getById(userId);
System.out.println("====this.getById===");
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getId,userId);
User user2 = this.getOne(wrapper);
System.out.println("====this.query one wrapper ===");
List<User>users = this.list(wrapper);
System.out.println("====this.query list wrapper ===");
LambdaUpdateWrapper<User> wrapper1 = new LambdaUpdateWrapper<>();
wrapper1.eq(User::getId,userId);
wrapper1.set(User::getName,"test");
this.update(wrapper1);
System.out.println("====this.update wrapper ===");
LambdaQueryWrapper<User> wrapperDel = new LambdaQueryWrapper<>();
wrapperDel.eq(User::getName,user1.getName());
this.remove(wrapperDel);
System.out.println("====this.delete wrapper ===");
// 自定义sql
User user3 = userMapper.getUserOne(userId);
System.out.println("====this.query one xml ===");
return user2;
}
自定义sql:
<select id="getUserOne" resultType="com.example.uidconsumer.entity.User">
select * from user where id =#{userId}
select>
3.3 测试结果:
c.e.u.mapper.UserMapper.selectById : ==> Preparing: SELECT id,name,age,create_by,create_time,update_by,update_time,status,deleted FROM user WHERE id=? AND deleted=0
c.e.u.mapper.UserMapper.selectById : ==> Parameters: 167081924395884544(String)
c.e.u.mapper.UserMapper.selectById : <== Total: 1
====this.getById===
c.e.u.mapper.UserMapper.selectOne : ==> Preparing: SELECT id,name,age,create_by,create_time,update_by,update_time,status,deleted FROM user WHERE deleted=0 AND (id = ?)
c.e.u.mapper.UserMapper.selectOne : ==> Parameters: 167081924395884544(String)
c.e.u.mapper.UserMapper.selectOne : <== Total: 1
====this.query one wrapper ===
c.e.u.mapper.UserMapper.selectList : ==> Preparing: SELECT id,name,age,create_by,create_time,update_by,update_time,status,deleted FROM user WHERE deleted=0 AND (id = ?)
c.e.u.mapper.UserMapper.selectList : ==> Parameters: 167081924395884544(String)
c.e.u.mapper.UserMapper.selectList : <== Total: 1
====this.query list wrapper ===
c.e.u.mapper.UserMapper.update : ==> Preparing: UPDATE user SET name=? WHERE deleted=0 AND (id = ?)
c.e.u.mapper.UserMapper.update : ==> Parameters: test(String), 167081924395884544(String)
c.e.u.mapper.UserMapper.update : <== Updates: 0
====this.update wrapper ===
c.e.u.mapper.UserMapper.delete : ==> Preparing: UPDATE user SET deleted=1 WHERE deleted=0 AND (name = ?)
c.e.u.mapper.UserMapper.delete : ==> Parameters: test(String)
c.e.u.mapper.UserMapper.delete : <== Updates: 1
====this.delete wrapper ===
###
c.e.u.mapper.UserMapper.getUserOne : ==> Preparing: select * from user where id =?
c.e.u.mapper.UserMapper.getUserOne : ==> Parameters: 167081924395884544(String)
c.e.u.mapper.UserMapper.getUserOne : <== Total: 1
====this.query one xml ===
从测试结果可以看到:
4 扩展:
4.1 日志系统的输出级别:
在日志系统中,常见的日志级别按照从高到低的顺序包括:
在配置日志输出时,可以为每个类别指定不同的日志级别,例如:
logging:
level:
com.example.service: DEBUG
com.example.dao: INFO
org.springframework: WARN
4.2 MyBatis Plus 自动注入的 sql :
MyBatis Plus 自动注入的 SQL 是指使用 MyBatis Plus 提供的通用方法进行数据库操作时,MyBatis Plus 会自动填充一些查询条件;也即我们不能手写sql,需要使用MyBatis Plus 提供的方法进行CRUD操作。
4.3 @TableLogic 注解的作用:
@TableLogic 注解是用于标记一个字段为逻辑删除字段,被标记的字段的值为逻辑删除字段的值时,会被视为空,表示该记录被逻辑删除。在使用 MyBatis Plus 进行操作时,可以通过设置全局的逻辑删除属性配置来开启逻辑删除功能。
但是,@TableLogic 注解只对 MyBatis Plus 自动注入的 SQL 生效,不针对手写 SQL 生效,这是因为逻辑删除的原理是在 MyBatis Plus 自动注入的 SQL 中做了特殊处理,实现了逻辑删除功能。而手写 SQL 是无法进行这种处理的,因此 @TableLogic 注解对手写 SQL 没有作用。
需要注意的是,通过手写 SQL 进行更新或删除操作时,需要手动设置逻辑删除字段的值为逻辑删除值,才能实现逻辑删除功能。具体实现上,可以通过在 SQL 中使用占位符的方式将逻辑删除字段的值设置为逻辑删除值,例如 UPDATE table SET deleted = ? where id = ?,然后在执行 SQL 语句时传入逻辑删除值和具体的 id 值,从而实现逻辑删除操作。
总的来说,@TableLogic 注解只能对 MyBatis Plus 自动注入的 SQL 生效,不能对手写 SQL 生效,如果需要使用逻辑删除功能,就需要选用 MyBatis Plus 的自动注入 SQL 功能来实现。同时,在使用手写 SQL 进行更新或删除操作时,需要手动设置逻辑删除字段的值为逻辑删除值,从而实现逻辑删除的功能。
5 参考:
5.1 Mybatis-plus 逻辑删除;