# mybatis-plus示例-自带插件用法 **Repository Path**: litxie/mp-example ## Basic Information - **Project Name**: mybatis-plus示例-自带插件用法 - **Description**: Mybatis-Plus自带插件使用教程 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2022-10-17 - **Last Updated**: 2025-06-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### mybatis-plus入门 一、mybatis-plus引入maven ```xml com.baomidou mybatis-plus-boot-starter ${mybatis-plus-boot-starter.version} ``` 二、编写配置文件,配置数据源以及其他参数 ```yaml spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 262799 mybatis-plus: mapper-locations: classpth*:/mapper/**/*.xml #mappper文件地址 configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #标准控制台输出 ``` 三、新建mapper包,编写mapper类,实现BaseMapper接口 ```java public interface CoreMapper extends BaseMapper public interface CoreMapper extends BaseMapper<实体类T> ``` 四、新建service包,编写service抽象类实现,Iservice接口。 ```java public interface CoreService extends IService<实体类T> ``` // 1.配置类添加@MapperScan
// 2.或者mapper类上添加@Mapper
// 3.或者注入@Bean
1.@Autowired方式使用mapper,此类方法才可以实现使用子类方法。 ```java @Autowired private CoreMapper mappper; ``` 2.实现ServiceImpl类,不需要手动注入mapper**甚至写不需要Mapper**,ServiceImpl中包含了baseMapper,在实现时自动注入。只包含BaseMapper。非自己重写的mapper。 ```java public class CoreServiceImpl extends ServiceImpl //extends使得basemapper不需要主动注入 implements CoreService ``` 五、mybatis-plus可以直接使用。
缺点也很明显,部分逻辑移动到了Controller层。 ### myabtis-plus注解@TableFilled()的处理MetaObjectHandler 1.实现MetaObjectHandler。实现自己的自动填充逻辑 ```java public class MetadataHandler implements MetaObjectHandler { //插入填充 @Override public void insertFill(MetaObject metaObject) { setFieldValByName("create_time",new Date(),metaObject);//在插入记录时,将create_time字段设置为new date(),即当前时间 } //更新填充 @Override public void updateFill(MetaObject metaObject) { // setFieldValByName()同上 } } ``` 2.在模型层 ```java @TableField(fill = FieldFill.INSERT...) ``` ### mybatis-plus代码生成器 一、引入pom自动代码依赖generator ```xml com.baomidou mybatis-plus-generator 最新版本 ``` 二、在测试包下添加代码 ```xml public class CodeGenerator { private static final DataSourceConfig.Builder DATA_SOURCE_CONFIG = new DataSourceConfig .Builder("jdbc:mysql://localhost:3306/数据库名?characterEncoding=utf-8&serverTimezone=UTC", "root", "262799"); /** * run */ public static void main(String[] args) throws SQLException { DataSourceConfig dataSourceConfig = DATA_SOURCE_CONFIG.build(); System.out.println(dataSourceConfig.getUrl()); FastAutoGenerator.create(DATA_SOURCE_CONFIG) .globalConfig((scanner, builder) -> builder.author(scanner.apply("作者:"))) .packageConfig((scanner, builder) -> builder.parent(scanner.apply("包名:"))) .strategyConfig((scanner, builder) -> builder.addInclude(scanner.apply("表名:"))) /* .templateEngine(new BeetlTemplateEngine()) .templateEngine(new FreemarkerTemplateEngine()) .templateEngine(new EnjoyTemplateEngine()) */ .templateEngine(new FreemarkerTemplateEngine()) .execute(); } } ``` 其他生成方式见官网。
三、执行结束,文件弹出 ### mybaits-plus分页插件承Page<实体T> 一、在配置类添加分页插件 ```java @Configuration public class MybatisPlusConfig { /** * 拦截器配置,可以添加mybatisPlus各类内置拦截器 * @return */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor());//添加分页插件 return interceptor; } } ``` 二、编写自定义IPage类,也可以实现IPage接口,但是需要重写方法,更加复杂。 ```java /** 自定义分页,传入(current,size),会在sql语句中添加limit语句 limit (current-1)*5,size * 比如页大小为5,要第二页数据,则需要传入(currentPage=2,size=5) ,在数据库会这么添加limit currentPoint=(2-1)*5,size=5 * @author xielin * @date 2022/10/17 14:52 */ public class MyPage extends Page { } ``` 三、BaseMapper自带selectPage方法,不需要声明可以直接调用。如果需要在mapper自定义方法将结果分页,需要自己编写对应sql语句。 ```java @RequestMapping("/car/select/page") private int selectPage(){ MyPage dtoMyPage = new MyPage<>(); dtoMyPage.setCurrent(3L);//需要第三页 dtoMyPage.setSize(2L);//每页两条数据 第三页开始号为4(0,1),(2,3),(4...) limit (4,2) MyPage myPage = carService.getBaseMapper().selectPage(dtoMyPage, null); myPage.getRecords().iterator().forEachRemaining(System.out::println); System.out.println("当前页大小"+myPage.getSize()); System.out.println("当前页"+myPage.getCurrent()); System.out.println("所有数据总数"+myPage.getTotal()); return 0; } ``` 四、有关分页 ```java 使用方法一: 直接在使用basemapper.selectPage方法 使用二: 自定义mapper文件中的分页方法,并在mapper.xml编写好代码。和普通书写sql一样。 在XXmapper.xml中sql语句,使用page.属性获得变量值,这和普通java对象一样。 ``` 五、提示 :::info 如果返回类型是 IPage 则入参的 IPage 不能为null,因为 返回的IPage == 入参的IPage; 如果想临时不分页,可以在初始化IPage时size参数传 <0 的值;
如果返回类型是 List 则入参的 IPage 可以为 null(为 null 则不分页),但需要你手动 入参的IPage.setRecords(返回的 List);
如果 xml 需要从 page 里取值,需要 page.属性 获取 ::: ### mybatis-plus废话 ```java mybatis-plus 1.MetaObjectHandler //MetaObjectHandler 接口式mp为我们提供的一个扩展接口,我们可以利用这个接口在我们插入或者更新数据的时候,为一些字段指定默认值。 // 实现这个需求的方法不止一种,在SQL层面也可以做到,在建表的时候也可以指定默认值。 会设置几个公共字端 // 在使用@TableFilled()注解时,会使用IOC容器中的MetaHandler @TableField(fill = FieldFill.UPDATE) 2.service层自动代码 public class CoreServiceImpl extends ServiceImpl implements CoreService { //extends使得basemapper不需要主动注入 public interface CoreMapper extends BaseMapper 3.分页 /** * 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除) */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } 使用方法一: 直接在mapper接口层使用,使用默认Page类 IPage method(Ipage<> page,QuerryWrapper tiaojian) 也可以直接写条件参数如IPage method(Ipage<> page,int state) 自写sql SELECT id,name FROM user WHERE state=#{state} 如果返回类型是 IPage 则入参的 IPage 不能为null,因为 返回的IPage == 入参的IPage; 如果想临时不分页,可以在初始化IPage时size参数传 <0 的值; 使用二 实现IPage接口,自定义Page MyPage selectPageVo(MyPage page); 使用三: List selectPageVo(IPage page, queryWrapper); 如果返回类型是 List 则入参的 IPage 可以为 null(为 null 则不分页),但需要你手动 入参的IPage.setRecords(返回的 List); 使用四: 仿写IPage 自定义类---不合理 4.泛型 E - Element (在集合中使用,因为集合中存放的是元素) T - Type(表示Java 类,包括基本的类和我们自定义的类) K - Key(表示键,比如Map中的key) V - Value(表示值) ? - (表示不确定的java类型) ``` ### mybatis-plus Sql注入器 一、写类继承AbstractMethod,实现injectMappedStatement()方法 ```xml public class DeleteAllMethod extends AbstractMethod { private final String METHOD_NAME="deleteAll"; /** * 清空表 * @param mapperClass mapper接口class * @param modelClass mapper泛型class * @param tableInfo 表信息 * @return * 这里@TableLogic不会生效。 */ @Override public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { String sql="truncate table "+tableInfo.getTableName().toString(); SqlSource sqlSource = this.languageDriver.createSqlSource(configuration, sql, modelClass);// return this.addDeleteMappedStatement(mapperClass,METHOD_NAME,sqlSource); } } ``` 二、写注入类,继承DefaultSqlInjector重写getMethodList()方法 ```xml /** * @author xielin * @date 2022/10/16 23:16 */ //@Component public class MyInjector2 extends DefaultSqlInjector { /** * 在这里添加自定义的Sql的AbstractMethod实现类,完成SQL注入. * @param mapperClass * @param tableInfo * @return */ @Override public List getMethodList(Class mapperClass, TableInfo tableInfo) { List methodList = super.getMethodList(mapperClass, tableInfo); methodList.add(new DeleteAllMethod()); return methodList; } } ``` 三、在Mapper中写上方法。 ```xml @Mapper public interface CarMapper extends BaseMapper { int deleteAll(); } ``` ### mybatis-plus 乐观锁 一、在配置类添加乐观锁拦截器 ```yaml @Configuration public class MybatisPlusConfig { /** * 拦截器配置,可以添加mybatisPlus各类内置拦截器 * @return */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());//添加乐观锁插件 return interceptor; } } ``` 二、在实体类字段上添加@Version注解 ```yaml /** * 当前版本 */ @TableField(value = "version") @Version @JsonIgnore//不添加到VO中返回 private int version; ``` 三、问题 :::info 2022.10.17记录-目前 Mybatis-Plus 乐观锁仅支持 updateById(id) 与 update(entity, wrapper) 方法 ::: ### mybatis-plus 注解@TableLogic逻辑删除 一、说明 :::info 说明:
只对自动注入的 sql 起效:(自己写的sql语句无效。)
插入: 不作限制
查找: 追加 where 条件过滤掉已删除数据,如果使用 wrapper.entity 生成的 where 条件也会自动追加该字段
更新: 追加 where 条件防止更新到已删除数据,如果使用 wrapper.entity 生成的 where 条件也会自动追加该字段
删除: 转变为 更新
例如:
删除: update user set deleted=1 where id = 1 and deleted=0
查找: select id,name,deleted from user where deleted=0
字段类型支持说明:
支持所有数据类型(推荐使用 Integer,Boolean,LocalDateTime)
如果数据库字段使用datetime,逻辑未删除值和已删除值支持配置为字符串null,另一个值支持配置为函数来获取值如now()
附录:
逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。
如果你需要频繁查出来看就不应使用逻辑删除,而是以一个状态去表示。 ::: 二、配置文件 ```yaml mybatis-plus: global-config: db-config: logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2) logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) ``` 三、使用 ```yaml @TableLogic private Integer deleted; ``` 四、问题 ```yaml 乐观锁以及逻辑删除位不应该被返回到前端,可以添加注解去取消返回该字段。具体注解为 import com.fasterxml.jackson.annotation.JsonIgnore; 某实体类: /** * 是否删除0是1否 */ @TableField(value = "is_delete") @TableLogic @JsonIgnore//不添加到VO中返回 private int deleted; /** * 当前版本 */ @TableField(value = "version") @Version @JsonIgnore//不添加到VO中返回 private int version; ``` ### mybatis-plus执行分析SQL插件 一、导入maven依赖 ```yaml p6spy p6spy 最新版本 ``` 二、修改配置文件 ```yaml spring: datasource: driver-class-name: com.p6spy.engine.spy.P6SpyDriver url: jdbc:p6spy:mysql:// ... ``` 三、添加配置文件spy.properties在资源目录下 ```properties #3.2.1以上使用 modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory #3.2.1以下使用或者不配置 #modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory # 自定义日志打印 logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger #日志输出到控制台 appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger # 使用日志系统记录 sql #appender=com.p6spy.engine.spy.appender.Slf4JLogger # 设置 p6spy driver 代理 deregisterdrivers=true # 取消JDBC URL前缀 useprefix=true # 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset. excludecategories=info,debug,result,commit,resultset # 日期格式 dateformat=yyyy-MM-dd HH:mm:ss # 实际驱动可多个 #driverlist=org.h2.Driver # 是否开启慢SQL记录 outagedetection=true # 慢SQL记录标准 2 秒 outagedetectioninterval=2 ``` 四、查看结果 :::info Consume Time:13 ms 2022-10-16 23:51:16
Execute SQL:SELECT car_id,car_name,car_type,car_color,car_price,manufacturer FROM car ::: 五、提示 - 该插件有性能损耗,不建议生产环境使用。 ### mybatis-plus动态表名 一、在配置类中添加动态表名插件 ```java public class MybatisPlusConfig { /** * 拦截器配置,可以添加mybatisPlus各类内置拦截器 * @return */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor());//添加动态表名插件 return interceptor; } /** * 动态表插件 * 在TableNameHandler类中在dynamicTableName方法中返回表名,传递进去的参数为sql语句,以及原tableName */ private DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor(){ DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor(); dynamicTableNameInnerInterceptor.setTableNameHandler((sql,tableName)->{ //return dynamicTableName return "tableName_01";//在这里返回动态表名,亲测有效 }); return dynamicTableNameInnerInterceptor; } } ``` ### mybatis-plus多租户 一、在配置类上添加租户插件 ```java /** * 拦截器配置,可以添加mybatisPlus各类内置拦截器 * @return */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(tenantLineInnerInterceptor());//添加租户插件 return interceptor; } /** * 租户插件 */ private TenantLineInnerInterceptor tenantLineInnerInterceptor(){ TenantLineInnerInterceptor tenantLineInnerInterceptor = new TenantLineInnerInterceptor(); tenantLineInnerInterceptor.setTenantLineHandler(new TenantLineHandler() { /** * 获取租户 ID 值表达式,只支持单个 ID 值 * @return 租户 ID 值表达式 */ @Override public Expression getTenantId() {//返回自定义租户ID,在Sql中会拼接where 租户id=此方法id 获取租户 ID 值表达式,只支持单个 ID 值 return new StringValue("白色"); } /** * 获取租户字段名 *

* 默认字段名叫: tenant_id * * @return 租户字段名 */ @Override public String getTenantIdColumn() { return "car_color"; } /** * 根据表名判断是否忽略拼接多租户条件 *

* 默认都要进行解析并拼接多租户条件 * * @param tableName 表名 * @return 是否忽略, true:表示忽略,false:需要解析并拼接多租户条件 */ @Override public boolean ignoreTable(String tableName) { if ("role".equals(tableName.toString())){ //举例:-如果是role表,忽略租户,不拼接条件 return true; } return false; } }); return tenantLineInnerInterceptor; } ``` 二、使用
在进行sql查询时,会在where句段自动添加tenant_id=?。 ### mybatis-plus禁止全表更新与删除插件 一、配置类拦截器中添加BlockAttackInnerInterceptor插件 ```java @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor//添加乐观锁插件 .addInnerInterceptor(new OptimisticLockerInnerInterceptor()); interceptor//禁止全局更新删除插件 .addInnerInterceptor(new BlockAttackInnerInterceptor()); return interceptor; } ``` 二、使用 ```java @RequestMapping("/car/updateAll") private int update(@RequestBody CarDTO carDTO){ return carService.getBaseMapper().update(carDTO,null); } ``` 拦截成功示意 :::info Error updating database. Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of full table deletion ::: 三、注意
BlockAttackInnerInterceptor源码中提高了table.getWhere()。事实证明,mp全局更细拦截器通过判断where子句来判断是否更具更新。
1.所以当存在@Version乐观锁(原理是添加where version,set version。)等时。该拦截器失效。
2.当删除语句为truncate,拦截器失效。为delete,update时可用。
3.不过@TableLogic(添加where 标志位)是可用的,因为源码添加了逻辑删除字段判断。 ```java /** * 获取表名中的逻辑删除字段 * * @param tableName 表名 * @return 逻辑删除字段 */ private String getTableLogicField(String tableName) ```