转载自: Mybatis-Plus 自定义SQL注入器,非常实用!

Java学习指南:https://java-family.cn

大家好,我是不才陈某~

一、什么是SQL注入器

我们在使用Mybatis-Plus时,dao层都会去继承BaseMapper接口,这样就可以用BaseMapper接口所有的方法,BaseMapper中每一个方法其实就是一个SQL注入器

在Mybatis-Plus的核心(core)包下,提供的默认可注入方法有这些:

那如果我们想自定义SQL注入器呢,我们该如何去做?

比如在Mybatis-Plus中调用updateById方法进行数据更新默认情况下是不能更新空值字段的。

而在实际开发过程中,往往会遇到需要将字段值更新为空值的情况。

那如何让Mybatis-Plus支持空值更新呢?

如果仅是想实现支持更新空值字段并不需要我们自定义SQL注入器,因为Mybatis-Plus提供了几个扩展SQL注入器。

二、内置扩展SQL注入器有哪些?

1、自带扩展SQL注入器

Mybatis-Plus 扩展SQL注入器在扩展包下,为我们提供了可扩展的可注入方法:

AlwaysUpdateSomeColumnById : 根据id更新字段(全量更新不忽略null字段),updateById默认会自动忽略实体中null值字段。

InsertBatchSomeColumn : 真实批量插入,saveBatch其实是伪批量插入。

LogicDeleteBatchByIds : 逻辑删除增加填充功能,比如删除的时候填充更新时间、更新人。

Upsert : 插入一条数据(选择字段插入)。

2、SQL注入器全局配置

@Component  
public class MySqlInjector extends DefaultSqlInjector {  
      
    @Override  
    public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {  
        List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);  
        /\*\*  
         \* 把两个扩展内置扩展SQL注入器注入  
         \*/  
        methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));  
        methodList.add(new AlwaysUpdateSomeColumnById(i -> i.getFieldFill() != FieldFill.INSERT));  
        return methodList;  
    }  
}  

3、自定义Mapper

public interface MyBaseMapper<T> extends BaseMapper<T> {  
      
    /\*\*  
     \* 全字段更新,不会忽略null值  
     \*  
     \* @param entity 实体对象  
     \*/  
    int alwaysUpdateSomeColumnById(@Param("et") T entity);  

    /\*\*  
     \* 全量插入,等价于insert  
     \*   
     \* @param entityList 实体集合  
     \*/  
    int insertBatchSomeColumn(List<T> entityList);  
}  

三、扩展SQL注入器示例测试

1、用户表

CREATE TABLE `user` (  
  `id` int unsigned  AUTO_INCREMENT COMMENT '主键',  
  `username` varchar(128)  COMMENT '用户名',  
  `phone` varchar(32)  COMMENT '手机号',  
  `sex` char(1)  COMMENT '性别',  
  `create\_time` datetime  COMMENT '创建时间',  
  `update\_time` datetime  COMMENT '更新时间',  
  `deleted` tinyint DEFAULT '0' COMMENT '1、删除 0、未删除',  
  PRIMARY KEY (`id`)  
) ENGINE=InnoDB AUTO_INCREMENT=1   

2、创建对应实体

@Data  
@Accessors(chain = true)  
@TableName("user")  
public class UserDO implements Serializable {  

    private static final long serialVersionUID = 1L;  

    @TableId(value = "id", type = IdType.AUTO)  
    private Integer id;  
    /\*\*  
     \* 用户名  
     \*/  
    @TableField("username")  
    private String username;  
    /\*\*  
     \* 手机号  
     \*/  
    @TableField("phone")  
    private String phone;  
    /\*\*  
     \* 性别  
     \*/  
    @TableField("sex")  
    private String sex;  
    /\*\*  
     \* 创建时间  
     \*/  
    @TableField(value = "create\_time",fill = FieldFill.INSERT)  
    private LocalDateTime createTime;  
    /\*\*  
     \* 更新时间  
     \*/  
    @TableField(value = "update\_time",fill = FieldFill.INSERT_UPDATE)  
    private LocalDateTime updateTime;  
    /\*\*  
     \* 1、删除 0、未删除  
     \*/  
    @TableField(value = "deleted",fill = FieldFill.INSERT)  
    private Integer deleted;  
}  

其它有关代码这里就不粘贴了,具体看项目源码。

我们自定义的Mapper不再继承BaseMapper而是继承MyBaseMapper

 /\*\*  
  \*  通用mapper接口,以后创建其他mapper接口时,不再继承BaseMapper,而是继承MyBaseMapper  
  \*/  
@Mapper  
public interface UserMapper extends MyBaseMapper<UserDO> {  

}  

3、测试代码

@SpringBootTest  
@RunWith(SpringRunner.class)  
@ComponentScan("com.jincou.mybatisplus.dao")  
public class SqlInjectorTest  {  

   @Autowired  
   private UserMapper mapper;  
     
    @Test  
    public void alwaysUpdateSomeColumnById() {  
        UserDO user = new UserDO();  
        user.setUsername("小小");  
        user.setPhone(null);  
        user.setSex("女");  
        user.setId(1);  
        mapper.alwaysUpdateSomeColumnById(user);  
    }  
      
    @Test  
    public void insertBatchSomeColumn() {  
        UserDO user = new UserDO();  
        user.setUsername("zhangsan");  
        user.setPhone("13811111111");  
        user.setSex("女");  

        UserDO user1 = new UserDO();  
        user1.setUsername("lisi");  
        user1.setPhone("13822222222");  
        user1.setSex("男");  

        ArrayList<UserDO> userDOS = Lists.newArrayList(user, user1);  
        mapper.insertBatchSomeColumn(userDOS);  
    }  
}  

alwaysUpdateSomeColumnById方法运行结果

insertBatchSomeColumn方法运行结果

成功!

四、如何自定义SQL注入器?

在实际开发过程中,当Mybatis-Plus自带的一些SQL注入器不满足我们的条件时,我们就需要自定义SQL注入器,整个流程也非常简单

这里我们以一个很简单的findAll方法为例进行学习。

在MyBaseMapper中添加findAll方法

public interface MyBaseMapper<T> extends BaseMapper<T> {  
   
     /**  
       *  查询所有用户  
       */  
      List<T> findAll();  
}  

2、编写FindAll SQL注入器

public class FindAll extends AbstractMethod {  

    public FindAll() {  
        super("findAll");  
    }  


    public FindAll(String methodName) {  
        super(methodName);  
    }  

    @Override  
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {  
        /\* 执行 SQL ,动态 SQL 参考类 SqlMethod \*/  
        String sql = "select \*  from " + tableInfo.getTableName();  
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);  
        return this.addSelectMappedStatementForTable(mapperClass, sqlSource, tableInfo);  
    }  
}  

3、注册到Spring容器

@Component  
public class MySqlInjector extends DefaultSqlInjector {  

    @Override  
    public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {  
        List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);  
        /\*\*  
         \* 自定义SQL注入器注入  
         \*/  
        methodList.add(new FindAll());  
        return methodList;  
    }  
}  

4、测试

 @Test  
    public void findAll() {  
         List<UserDO> userDOS = mapper.findAll();  
    }  

运行结果

成功!

最后说一句(别白嫖,求关注)

陈某每一篇文章都是精心输出,如果这篇文章对你有所帮助,或者有所启发的话,帮忙点赞、在看、转发、收藏,你的支持就是我坚持下去的最大动力!

另外陈某的知识星球开通了,加入只需129元,星球回馈的价值巨大,目前更新了Spring全家桶实战系列、亿级数据分库分表实战、DDD微服务实战专栏、我要进大厂、Spring,Mybatis等框架源码、架构实战22讲、精尽RocketMQ等....每增加一个专栏价格将上涨20元

关注公众号:【码猿技术专栏】,公众号内有超赞的粉丝福利,回复:加群,可以加入技术讨论群,和大家一起讨论技术,吹牛逼!