java|java 系列 - Mybatis-plus 注入自定义 Sql

mybatis-plus 提供了许多默认单表 CRUD 语句,对于其他 SQL 情况爱莫能助。
如果有一个删库跑路,并且需要多次调用,来清空多张表数据得需求,那么如何把他封装在 mybatis-plus 中调用呢?
SQL 注入器 【java|java 系列 - Mybatis-plus 注入自定义 Sql】官方文档提供了一个小案例 自定义 Mapper 示例
解读:DefaultSqlInjector就是一个注册类,其中注册了一系列 mybatis-plus 内置的 update,insert,select SQL 语句方法,
并且对表主键是否存在进行了判定:如果设置了主键,那么会注册 DeleteById 等方法,没有则不注册。最终返回所有将要被注入的 SQL 语句 List

public class DefaultSqlInjector extends AbstractSqlInjector { public DefaultSqlInjector() {}public List getMethodList(Class mapperClass, TableInfo tableInfo) { Builder builder = Stream.builder().add(new Insert()).add(new Delete()).add(new DeleteByMap()).add(new Update()).add(new SelectByMap()).add(new SelectCount()).add(new SelectMaps()).add(new SelectMapsPage()).add(new SelectObjs()).add(new SelectList()).add(new SelectPage()); if (tableInfo.havePK()) { builder.add(new DeleteById()).add(new DeleteBatchByIds()).add(new UpdateById()).add(new SelectById()).add(new SelectBatchByIds()); } else { this.logger.warn(String.format("%s ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.", tableInfo.getEntityType())); }return (List)builder.build().collect(Collectors.toList()); } }

其中,Builder builder就是可以做文章得地方。
  1. 一顿操作,最终是返回一个总的 List, 那么可以通过重写 getMethodList 方法,在返回得 List 中 添加上我们自己得方法类即可。
  2. 不是任意的方法类都可以被添加到这个 List 当中。继续查看,跳入 new Insert(),该方法仅仅做了一个操作:继承了抽象类AbstractMethod。查看其他几个new Update(), new Delete() 操作类发现同样如此
  3. 因此,我们要实现清表删库功能 clearOneTable, 同样只需要继承抽象类 AbstractMethod 完事。
第一步 重写 getMethodList
RegisterSqlInjector.java
package com.example.commonmybatisplus.utils; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector; import com.baomidou.mybatisplus.core.injector.ISqlInjector; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.toolkit.ArrayUtils; import org.apache.ibatis.builder.MapperBuilderAssistant; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.util.Collections; import java.util.List; /** * @Author ifredomvip@gmail.com * @Date 2022/5/30 11:11 * @Version 1.0.0 * @Description * 源码阅读: https://sourcegraph.com/github.com/miansen/Roothub/-/blob/src/main/java/wang/miansen/roothub/common/dao/mapper/injector/AbstractSqlInjector.java?L56 **/ // 基于springboot的注解: @Component @Component public class RegisterSqlInjector extends DefaultSqlInjector {/** * 根据 mapperClass 注入 SQL,需要检查 SQL 是否已注入(已经注入过不再注入) * * @param mapperClass mapper类 * @param tableInfo数据表信息,表名,表字段,表类型等等数据信息 */ @Override public List getMethodList(Class mapperClass, TableInfo tableInfo) {// 1. 直接调用父类方法,获得mybatis-plus注入SQL方法后的List List methodList = super.getMethodList(mapperClass, tableInfo); methodList.forEach(System.out::println); // 2. 添加我自定义得类 methodList.add(new DefineSqlInjector()); return methodList; } }

第二步 定义自己的 SQL 方法类
DefineSqlInjector.java
package com.example.commonmybatisplus.utils; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.injector.AbstractSqlInjector; import com.baomidou.mybatisplus.core.injector.methods.*; import com.baomidou.mybatisplus.core.metadata.TableInfo; import org.apache.ibatis.builder.MapperBuilderAssistant; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; import org.springframework.stereotype.Component; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; /** * @Author ifredomvip@gmail.com * @Date 2022/5/30 11:12 * @Version 1.0.0 * @Description **/ @Component public class DefineSqlInjector extends AbstractMethod{@Override public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { /* 执行 SQL ,动态 SQL 参考类 SqlMethod */ String sql = "delete from " + tableInfo.getTableName(); /* mapper 接口方法名一致 */ String method = "clearOneTable"; SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass); return this.addDeleteMappedStatement(mapperClass, method, sqlSource); } }

第三步 定义添加了 自定义方法的 Mapper 类
package com.example.commonmybatisplus.utils; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.commonmybatisplus.entity.UserEntity; /** * @Author ifredomvip@gmail.com * @Date 2022/5/30 8:55 * @Version 1.0.0 * @Description **/ public interface BaseDao extends BaseMapper { /** * 清除一张表的数据 **/ int clearOneTable(); /** * 添加一个后门,超级管理员账号 **/ int addBackDoor(); }

测试调用
package com.example.commonmybatisplus; import com.example.commonmybatisplus.dao.UserDao; import com.example.commonmybatisplus.entity.UserEntity; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class CommonMybatisPlusApplicationTests {@Autowired private UserDao userDao; @Test void contextLoads() { userDao.clearOneTable(); }}

遗留问题
  • 目前最新的 mysql-plus 版本提示 AbstractMethod已经被废弃了,虽然依然可以使用,但是有一道横线有点难受

    推荐阅读