大纲 前言 版本说明 本文的教程内容是基于 MyBatis-Plus 3.5.2
版本编写的,若你使用的是 2.x
或其他版本,可能会有部分知识点、案例代码不兼容,一切以 MyBatis-Plus 官方文档为准。
MyBatis-Plus 逻辑删除 逻辑删除介绍 只对自动注入的 SQL 生效
插入:没有任何限制 删除:会转变为 SQL 更新语句,例如:update user set deleted = 1 where id = 1 and deleted = 0
查找:会追加 where
条件过滤掉已删除的数据,且使用 wrapper.entity
生成的 where
条件会忽略该字段,例如:select id, name, deleted from user where deleted = 0
更新:会追加 where
条件防止更新到已删除的数据,且使用 wrapper.entity
生成的 where
条件会忽略该字段,例如:update user set age = 23 where id = 1 and deleted = 0
字段类型支持说明
支持所有数据类型(推荐使用 Integer
、Boolean
、LocalDateTime
数据类型) 如果数据库表字段使用 datetime
,逻辑未删除值和已删除值支持配置为字符串 null
,另一个值支持配置为函数来获取值(例如 now()
) 提示
逻辑删除是为了方便数据恢复和保护数据本身价值的一种方案,但实际就是删除 如果业务上需要频繁将数据查出来显示,那么就不应使用逻辑删除,而是应该以一个状态去表示数据记录的状态 逻辑删除使用案例 本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-plus-lesson-10
。
逻辑删除的配置步骤
1、在实体类的属性上添加 @TableLogic
注解 2、使用 Spring XML 配置文件或者 SpringBoot 配置文件的任意一种方式来配置 MyBatis-Plus 的逻辑删除
创建数据库表 1 2 3 4 5 6 7 8 9 10 CREATE TABLE `t_department` ( `id` int (11 ) NOT NULL AUTO_INCREMENT, `name` varchar (255 ) DEFAULT NULL , `deleted` int DEFAULT 0 , PRIMARY KEY (`id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8; insert into t_department(id, name, deleted) values (1 , '开发部门' , 0 ), (2 , '测试部门' , 0 ), (3 , '产品部' , 1 );
添加 @TableLogic
注解 在实体类的属性上添加 @TableLogic
注解
1 2 3 4 5 6 7 8 9 10 11 12 public class Department { private Long id; private String name; @TableLogic private Integer deleted; ... }
更改项目的配置文件 Spring XML 配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <bean id ="sqlSessionFactory" class ="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean" > <property name ="globalConfig" ref ="globalConfig" > </property > </bean > <bean id ="globalConfig" class ="com.baomidou.mybatisplus.core.config.GlobalConfig" > <property name ="dbConfig" ref ="dbConfig" /> </bean > <bean id ="dbConfig" class ="com.baomidou.mybatisplus.core.config.GlobalConfig.DbConfig" > <property name ="logicDeleteValue" value ="1" /> <property name ="logicNotDeleteValue" value ="0" /> </bean >
SpringBoot 配置文件 1 2 3 4 5 6 mybatis-plus: global-config: db-config: logic-delete-value: 1 logic-not-delete-value: 0
Junit 单元测试代码 1 2 3 4 5 6 public interface DepartmentMapper extends BaseMapper <Department > {}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 @SpringBootTest public class MyBatisPlusApplicationTest { @Autowired private DepartmentMapper deptMapper; @Test public void select () { List<Department> list = deptMapper.selectList(null ); list.forEach(System.out::println); } @Test public void selectByWrapper () { QueryWrapper<Department> wrapper = new QueryWrapper<>(); wrapper.like("name" , "测试" ); List<Department> list = deptMapper.selectList(wrapper); list.forEach(System.out::println); } @Test public void delete () { int deleteResult = deptMapper.deleteById(1L ); System.out.println("deleteResult: " + (deleteResult > 0 )); } @Test public void update () { Department department = new Department(); department.setName("行政部" ); department.setId(1L ); int updateResult = deptMapper.updateById(department); System.out.println("updateResult: " + (updateResult > 0 )); } }
执行上面的测试代码后,控制台输出的日志信息如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ==> Preparing: SELECT id,name,deleted FROM t_department WHERE deleted=0 ==> Parameters: <== Columns: id, name, deleted <== Row: 1, 开发部门, 0 <== Row: 2, 测试部门, 0 <== Total: 2 ==> Preparing: SELECT id,name,deleted FROM t_department WHERE deleted=0 AND (name LIKE ?) ==> Parameters: %测试%(String) <== Columns: id, name, deleted <== Row: 2, 测试部门, 0 <== Total: 1 ==> Preparing: UPDATE t_department SET deleted=1 WHERE id=? AND deleted=0 ==> Parameters: 1(Long) <== Updates: 1 ==> Preparing: UPDATE t_department SET name=? WHERE id=? AND deleted=0 ==> Parameters: 行政部(String), 1(Long) <== Updates: 0
逻辑删除常见问题 如何插入数据?
字段在数据库定义默认值(推荐) insert
前自己 set
值使用 自动填充处理器 删除接口自动填充处理器失效
1、使用 deleteById
方法(推荐) 2、使用 update
方法,即 UpdateWrapper.set(column, value)
(推荐) 3、使用 update
方法,即 UpdateWrapper.setSql("column=value")
4、使用 Sql 注入器 注入 com.baomidou.mybatisplus.extension.injector.methods.LogicDeleteByIdWithFill
(3.5.0 版本已废弃,推荐使用 deleteById
) MyBatis-Plus 通用枚举 通用枚举介绍 数据库表中有些字段的值是固定的,例如性别(男或女),此时可以使用 MyBatis-Plus 的通用枚举来实现,这样可以让 MyBatis 更优雅地使用枚举属性。
通用枚举使用案例 本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-plus-lesson-11
。
通用枚举的配置步骤
1、在枚举类的属性上添加 @EnumValue
注解,或者让枚举类实现 IEnum
接口 2、MyBatis-Plus 3.5.2
以下的版本,需要配置枚举包扫描(局部方式),或者更改 MyBatis 默认使用的 EnumTypeHandler
枚举类型处理器(全局方式)
创建数据库表 1 2 3 4 5 6 7 8 9 10 CREATE TABLE `t_admin` ( `id` int (11 ) NOT NULL AUTO_INCREMENT, `name` varchar (255 ) DEFAULT NULL , `type` int DEFAULT NULL , PRIMARY KEY (`id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8; insert into t_admin(id, name, type) values (1 , 'Alex' , 1 );
添加 @EnumValue 注解 在枚举类的属性上添加 @EnumValue
注解,MyBatis-Plus 在插入数据时,会将拥有 @EnumValue
注解的属性的值保存到数据库表里。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public enum AdminType { ROOT(0 , "Root" ), ADMIN(1 , "Admin" ); @EnumValue private Integer value; private String description; public Integer getValue () { return this .value; } public String getDescription () { return this .description; } private AdminType (Integer value, String description) { this .value = value; this .description = description; } }
或者让枚举类实现 IEnum
接口,然后重写 getValue()
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public enum AdminType implements IEnum <Integer > { ROOT(0 , "Root" ), ADMIN(1 , "Admin" ); private Integer value; private String description; @Override public Integer getValue () { return this .value; } public String getDescription () { return this .description; } private AdminType (Integer value, String description) { this .value = value; this .description = description; } }
配置枚举包扫描 值得一提的是,从 MyBatis-Plus 3.5.2
版本开始,无需手动配置枚举包扫描。
局部方式 当使用以下的方式配置枚举包扫描后,MyBatis-Plus 提供的 MybatisSqlSessionFactoryBean
会自动扫描包内合法的枚举类(使用了 @EnumValue
注解或者实现了 IEnum
接口),分别为这些枚举类注册使用 MybatisEnumTypeHandler
。换句话说,只有指定包下的枚举类会使用新的 TypeHandler
。其他包下,或者包内没有做相关改造的枚举类,仍然会使用 MyBatis 默认提供的 DefaultEnumTypeHandler
。
Spring XML 配置文件 1 2 3 4 <bean id ="sqlSessionFactory" class ="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean" > <property name ="typeEnumsPackage" value ="com.clay.mybatis.enums" /> </bean >
SpringBoot 配置文件 1 2 3 mybatis-plus: typeEnumsPackage: com.clay.mybatis.enums
全局方式 此方式用来全局更改 MyBatis 默认使用的 EnumTypeHandler
枚举类型处理器。
Spring XML 配置文件 1 2 3 4 5 6 7 8 <bean id ="sqlSessionFactory" class ="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean" > <property name ="configuration" ref ="configuration" /> </bean > <bean id ="configuration" class ="com.baomidou.mybatisplus.core.MybatisConfiguration" > <property name ="defaultEnumTypeHandler" value ="com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler" > </bean >
SpringBoot 配置文件 1 2 3 4 mybatis-plus: configuration: default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
通过 MybatisPlusPropertiesCustomizer 自定义 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Configuration @MapperScan("com.clay.mybatis.dao") public class MybatisPlusConfig { @Bean public MybatisPlusPropertiesCustomizer mybatisPlusPropertiesCustomizer () { return properties -> { GlobalConfig globalConfig = properties.getGlobalConfig(); globalConfig.setBanner(false ); MybatisConfiguration configuration = new MybatisConfiguration(); configuration.setDefaultEnumTypeHandler(MybatisEnumTypeHandler.class); properties.setConfiguration(configuration); }; } }
Junit 单元测试代码 1 2 3 4 5 6 7 8 9 10 11 public class Admin { private Long id; private String name; private AdminType type; ... }
1 2 3 public interface AdminMapper extends BaseMapper <Admin > {}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @SpringBootTest public class MyBatisPlusApplicationTest { @Autowired private AdminMapper adminMapper; @Test public void insert () { Admin admin = new Admin(); admin.setName("David" ); admin.setType(AdminType.ROOT); adminMapper.insert(admin); System.out.println(admin); } @Test public void select () { Admin admin = adminMapper.selectById(1L ); System.out.println(admin); } }
执行上面的测试代码后,MyBatis-Plus 发出的 SQL 语句如下:
1 2 3 4 5 6 7 8 9 ==> Preparing: INSERT INTO t_admin ( name, type ) VALUES ( ?, ? ) ==> Parameters: David(String), 0(Integer) <== Updates: 1 ==> Preparing: SELECT id,name,type FROM t_admin WHERE id=? ==> Parameters: 1(Long) <== Columns: id, name, type <== Row: 1, Alex, 1 <== Total: 1
序列化枚举值为前端返回值 MyBatis-Plus SQL 注入器 SQL 注入器介绍 Mybatis-Plus 的 SQL 注入器(SqlInjector
)可以自定义各种 SQL 语句,并注入到 MyBatis 全局中,这相当于自定义 Mybatis-Plus 自动注入的方法(例如通用的 CRUD 方法)。之前需要在 SQL 映射文件中配置的 SQL 语句,现在可以通过扩展 SqlInjector
来加载到 MyBatis 环境。在 MyBatis-Plus 中自定义自己的通用方法,可以实现接口 ISqlInjector
,也可以继承抽象类 AbstractSqlInjector
,或者继承默认实现类 DefaultSqlInjector
来注入通用方法。SQL 注入器的作用是可以在 Mapper 层自定义通用方法,然后将 SQL 模板自动注入到 MyBatis-Plus 中,这样就可以不用在 SQL 映射文件中重复编写 SQL 语句。
注入器和自定义 Mapper 方法 + 编写 SQL 映射文件的区别
使用 SQL 注入器,通常是使用自定义 Mapper(如 CustomBaseMapper)接口继承 BaseMapper
接口,我们自定义的通用方法就写在 CustomBaseMapper 里。然后我们业务相关的 Mapper 继承 CustomBaseMapper 后,就可以直接使用自定义的通用方法了。这一切只需要我们自定义一个 SQL 模板,注入到 MyBatis-Plus 即可,而不是换一张表就得重新编写一次 SQL 映射文件。
SQL 注入器使用案例 提示
本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-plus-lesson-14
。
定义 Mapper 接口 定义 Mapper 接口(继承自 BaseMapper
),新增自定义的通用方法,以后其他业务 Mapper 都可以继承自这个 CustomBaseMapper
接口
1 2 3 4 5 public interface CustomBaseMapper <T > extends BaseMapper <T > { List<T> findAll () ; }
定义方法类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class FindAll extends AbstractMethod { @Override public MappedStatement injectMappedStatement (Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) { String sql = "select * from " + tableInfo.getTableName(); String method = "findAll" ; SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass); MappedStatement mappedStatement = this .addSelectMappedStatementForOther(mapperClass, method, sqlSource, modelClass); return mappedStatement; } }
定义 SQL 注入器 定义 SQL 注入器,将自定义的通用方法注入到 MyBatis-Plus 全局中
1 2 3 4 5 6 7 8 9 10 11 12 public class CustomSqlInjector extends DefaultSqlInjector { @Override public List<AbstractMethod> getMethodList (Class<?> mapperClass, TableInfo tableInfo) { List<AbstractMethod> methodList = super .getMethodList(mapperClass, tableInfo); methodList.add(new FindAll()); return methodList; } }
注入 SQL 注入器 Spring XML 配置文件 1 2 3 4 5 6 7 8 9 10 11 <bean id ="sqlSessionFactory" class ="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean" > <property name ="globalConfig" ref ="globalConfig" > </property > </bean > <bean id ="globalConfig" class ="com.baomidou.mybatisplus.core.config.GlobalConfig" > <property name ="sqlInjector" ref ="customSqlInjector" /> </bean > <bean id ="customSqlInjector" class ="com.clay.mybatis.injector.CustomSqlInjector" />
SpringBoot 配置类 定义 SpringBoot 配置类,将 SQL 注入器注入 Spring 容器中
1 2 3 4 5 6 7 8 9 10 @Configuration @MapperScan("com.clay.mybatis.dao") public class MybatisPlusConfig { @Bean public CustomSqlInjector customSqlInjector () { return new CustomSqlInjector(); } }
Junit 单元测试代码 定义业务 Mapper 接口,继承自 CustomBaseMapper
1 2 3 public interface EmployeeMapper extends CustomBaseMapper <Employee > {}
1 2 3 4 5 6 7 8 9 10 11 12 13 @SpringBootTest public class MyBatisPlusApplicationTest { @Autowired private EmployeeMapper employeeMapper; @Test public void findAll () { List<Employee> list = employeeMapper.findAll(); list.forEach(System.out::println); } }
执行上面的测试代码,控制台输出的日志信息如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 ==> Preparing: select * from t_employee ==> Parameters: <== Columns: id, last_name, gender, email, age <== Row: 1, Jim, 1, jim@gmail.com, 26 <== Row: 2, Peter, 1, peter@gmail.com, 29 <== Row: 3, David, 1, david@gmail.com, 28 <== Row: 4, Tom, 1, tom@gmail.com, 25 <== Total: 4 Employee [id=1, lastName=Jim, gender=1, email=jim@gmail.com, age=26] Employee [id=2, lastName=Peter, gender=1, email=peter@gmail.com, age=29] Employee [id=3, lastName=David, gender=1, email=david@gmail.com, age=28] Employee [id=4, lastName=Tom, gender=1, email=tom@gmail.com, age=25]
EmployeeMapper
接口继承自 CustomBaseMapper
接口,拥有了 findAll
方法;由于 CustomSqlInjector
SQL 注入器的存在,会自动将 SQL 模板注入到 MyBatis-Plus 中,这样即使不在 SQL 映射文件中编写 SQL 语句,也能执行对应的 SQL 语句。
MyBatis-Plus 自动填充处理器 自动填充处理器介绍 MyBatis-Plus 自动填充处理器的工作原理:在插入或者更新数据库表数据之前,直接给 Entity
的属性设置值。 实现自动填充功能的核心对象是 MetaObjectHandler
接口和 MetaObject
类,其中的 MetaObject
是 MyBatis 提供的一个用于更加方便、更加优雅地访问对象的属性,并给对象的属性设置值的一个对象。MetaObject
还会用于包装对象,支持对 Object 、Map、Collection 等对象进行包装。本质上 MetaObject
获取对象的属性值或者是给对象的属性设置值,最终是要通过 Reflector(反射器)
获取到属性的对应方法的 Invoker
对象,然后执行 invoke
方法。
自动填充处理器的常用用途
自动填充处理器通常用于对公共字段进行填充,例如数据库表中的创建时间(create_time
)以及修改时间(update_time
)字段,使用它的好处是可以统一对这些公共字段进行处理,避免编写重复的代码。
自动填充策略说明 使用方法:@TableField(fill = FieldFill.INSERT)
值 描述 DEFAULT 默认不处理 INSERT 插入时填充字段 UPDATE 更新时填充字段 INSERT_UPDATE 插入和更新时填充字段
自动填充处理器使用案例 本节所需的案例代码,可以直接从 GitHub 下载对应章节 mybatis-plus-lesson-15
。
自动填充处理器的实现步骤
1、在需要填充的实体类属性上添加注解,例如 @TableFile(fill = FieldFill.INSERT)
2、实现 MetaObjectHandler
接口,自定义自动填充处理器 3、将自定义填充处理器注入到 MyBatis-Plus 全局中 添加 @TableField 注解 在实体类的属性上添加 @TableField
注解,并通过注解的 fill
属性指定填充策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Employee { private Long id; private String lastName; @TableField(fill = FieldFill.INSERT_UPDATE) private String gender; private String email; private Integer age; ...
定义自动填充处理器 实现 MetaObjectHandler
接口,定义自动填充处理器类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class CustomMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill (MetaObject metaObject) { Object fieldValue = getFieldValByName("gender" , metaObject); if (fieldValue == null ) { System.out.println("******* 插入操作满足自动填充条件 *******" ); this .setFieldValByName("gender" , "1" , metaObject); } } @Override public void updateFill (MetaObject metaObject) { Object fieldValue = getFieldValByName("gender" , metaObject); if (fieldValue == null ) { System.out.println("******* 更新操作满足自动填充条件 *******" ); this .setFieldValByName("gender" , "1" , metaObject); } } }
注入自动填充处理器 Spring XML 配置文件 1 2 3 4 5 6 7 8 9 10 11 <bean id ="sqlSessionFactory" class ="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean" > <property name ="globalConfig" ref ="globalConfig" > </property > </bean > <bean id ="globalConfig" class ="com.baomidou.mybatisplus.core.config.GlobalConfig" > <property name ="metaObjectHandler" ref ="customMetaObjectHandler" /> </bean > <bean id ="customMetaObjectHandler" class ="com.clay.mybatis.handler.CustomMetaObjectHandler" />
SpringBoot 配置类 定义 SpringBoot 配置类,将自动填充处理器注入 Spring 容器中
1 2 3 4 5 6 7 8 9 10 @Configuration @MapperScan("com.clay.mybatis.dao") public class MybatisPlusConfig { @Bean public CustomMetaObjectHandler customMetaObjectHandler () { return new CustomMetaObjectHandler(); } }
Junit 单元测试代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @SpringBootTest public class MyBatisPlusApplicationTest { @Autowired private EmployeeMapper employeeMapper; @Test public void insert () { Employee employee = new Employee(); employee.setEmail("clion@gmail.com" ); employee.setLastName("Clion" ); employee.setAge(26 ); employeeMapper.insert(employee); } @Test public void update () { Employee employee = new Employee(); employee.setId(1L ); employee.setEmail("empty@gmai.com" ); employeeMapper.updateById(employee); } }
执行上面的测试代码,控制台输出的日志信息如下:
1 2 3 4 5 6 7 8 9 ******* 插入操作满足自动填充条件 ******* ==> Preparing: INSERT INTO t_employee ( last_name, gender, email, age ) VALUES ( ?, ?, ?, ? ) ==> Parameters: Clion(String), 1 (String), clion@gmail .com(String), 26 (Integer)<== Updates: 1 ******* 更新操作满足自动填充条件 ******* ==> Preparing: UPDATE t_employee SET gender=?, email=? WHERE id=? ==> Parameters: 1 (String), empty@gmai .com(String), 1 (Long) <== Updates: 1
观察上面的输出结果,可以发现执行插入和更新操作之前,当实体类的 gender
属性为 NULL
时,其值会被自动填充。