MybatisPlus进阶篇学习笔记(八)------SQL注入器

原创:兜里的猫

本章目录

  • SQL注入器简介
  • 自定义方法实现
  • 选装件InsertBatchSomeColumn
  • 选装件LogicDeleteByIdWithFill
  • 选装件alwaysUpdateSomeColumnById

1 SQL注入器简介

使用SQL注入器我们可以自定义通用方法,就像selectById、insert这些方法,自定义的方法要添加到SQL注入器中。例如MP提供的那些原始的方法,他也都是添加到了SQL注入器中,如果MP提供的方法不能完全覆盖我们的需求,我们也可以编写自定义方法。简单来讲,就是编写我们自定义的sql进行全局使用。

2 自定义方法实现

实现步骤:

step1:创建定义方法的类

step2:创建注入器

step3:在Mapper中加入自定义方法

step1:创建定义方法的类

这里创建一个删除全部的方法

/**
* 自定义deleteAll全局方法
*/
public class DeleteAllMethod extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
// 执行的sql
String sql = "delete from " + tableInfo.getTableName();
// mapper接口方法名
String method = "deleteAll";
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);

return addDeleteMappedStatement(mapperClass, method, sqlSource);
}
}

step2:创建注入器

用于注入我们自定义的方法,加入注入器后就完成了我们自定义的方法deleteAll()可供全局使用。

/**
* 创建注入器
*/
@Component
public class MySqlInjector extends DefaultSqlInjector {

@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
// 加载DefaultSqlInjector类提供的所有方法
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
// 再加入我们自定义的方法
methodList.add(new DeleteAllMethod());

return methodList;
}
}

step3:在Mapper中加入自定义方法

然后我们就可以在mapper中使用我们自定义的方法了,和MP提供的方法一样使用。

@Repository
public interface UserMapper extends MyBaseMapper<User> {

/**
* 删除所有(自定义的全局方法)
* @return 影响行数
*/
int deleteAll();
}

测试:

user_2020表中的数据如下:

image
image

使用自定义的deleteAll方法:

image
image

执行完后user_2020表中的数据已经全部删除,测试成功。

image
image

扩展:

我们自定的方法是全局方法,如何使其在其他对象mapper中也能使用。思路是将自定义的全局方法封装到一个我们定义的基础接口MyBaseMapper类中,MyBaseMapper继承原先的BaseMapper,然后所有的对象mapper都继承我们定义的MyBaseMapper。代码如下:

/**
* 创建我们的通用mapper类,将自定义全局方法放到该类中,
* MyBaseMapper<T>中就包含了MP提供的BaseMapper<T>类中所有方法以及我们自定义的方法,
* 然后所有的mapper都继承该接口类。
*/
public interface MyBaseMapper<T> extends BaseMapper<T> {

/**
* 删除所有(自定义的全局方法)
* @return 影响行数
*/
int deleteAll();
}
/**
* UserMapper类继承我们上面定义的通用mapper,
* 如果有其他的Mapper也都是继承MyBaseMapper<T>,
* 这样就实现了自定义方法的全局使用。
*/
@Repository
public interface UserMapper extends MyBaseMapper<User> {

// 自定义SQL语句接口
@SqlParser(filter = true) // 使用注解配置接口是否启动多租户,true为不启用,默认为false启用
@Select("select * from user ${ew.customSqlSegment}")
List<User> mySelectUsers(@Param(Constants.WRAPPER) Wrapper<User> wrapper);
}

3 选装件InsertBatchSomeColumn

InsertBatchSomeColumn是MP官方提供的自定义方法, 该方法是自定义批量插入的时候有些不需要设置的字段进行插入时排除设置(即插入为null)。

首先在我们定义的SQL注入器中的methodList中加入该方法,然后将insertBatchSomeColumn方法引用到mapper中即可。

@Component
public class MySqlInjector extends DefaultSqlInjector {

@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
// 加载DefaultSqlInjector类提供的所有方法
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
// 再加入我们自定义的方法
methodList.add(new DeleteAllMethod());
// 注入一个InsertBatchSomeColumn方法:在批量插入的时候不插入逻辑删除字段deleted值
methodList.add(new InsertBatchSomeColumn(t ->!t.isLogicDelete()));

return methodList;
}
}
public interface MyBaseMapper<T> extends BaseMapper<T> {

/**
* 删除所有(自定义的全局方法)
* @return 影响行数
*/
int deleteAll();

/**
* 批量插入(排除deleted逻辑字段的插入)
* @param list
* @return
*/
int insertBatchSomeColumn(List<T> list);
}

然后使用该方法测试,可以看到插入成功同时也将deleted字段排除。

image
image

注: 这里当我们加入多租户的时候会出现一个问题就是,租户字段manager_id在insert语句中设置了2次,此时程序会报错,报错信息如下:

image
image

想解决此问题可以使用下面的方法,就是在我们选装InsertBatchSomeColumn的时候,排除一次manager_id,这样程序就不会报错。

image
image

另外还有需要注意的是,例如我们设置了version字段默认值的时候,也是需要手动排除该字段的,不然的话user中没有setVersion就会导致他为null不会设置默认值1。手动排除之后不用我们setVersion,默认值就会自动配置为1。

methodList.add(new InsertBatchSomeColumn(t ->!t.isLogicDelete()&&!t.getColumn().equals("manager_id")&&!t.getColumn().equals("version")));

4 选装件LogicDeleteByIdWithFill

LogicDeleteByIdWithFill是MP官方提供的自定义方法,该方法是在我们通过id进行逻辑删除的时候自动填充一些字段,比如逻辑删除的时候添加删除的操作人、操作时间等。

首先在我们定义的SQL注入器中的methodList中加入该方法,然后将deleteByIdWithFill方法引用到mapper中即可。

@Component
public class MySqlInjector extends DefaultSqlInjector {

@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
// 加载DefaultSqlInjector类提供的所有方法
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
// 再加入我们自定义的方法
methodList.add(new DeleteAllMethod());
// 注入一个InsertBatchSomeColumn方法:在批量插入的时候不插入逻辑删除字段deleted值
methodList.add(new InsertBatchSomeColumn(t ->!t.isLogicDelete()));
// 注入一个逻辑删除方法:在通过id进行逻辑删除的时候自动填充一些字段值,例如操作人、操作时间等。
methodList.add(new LogicDeleteByIdWithFill());

return methodList;
}
}
public interface MyBaseMapper<T> extends BaseMapper<T> {

/**
* 删除所有(自定义的全局方法)
* @return 影响行数
*/
int deleteAll();

/**
* 批量插入(排除deleted逻辑字段的插入)
* @param list
* @return
*/
int insertBatchSomeColumn(List<T> list);

/**
* 逻辑删除并自动填充相关字段
* @param entity
* @return
*/
int deleteByIdWithFill(T entity);
}

然后使用该方法测试之前,我们还要进行一步操作,在需要自动填充的字段上加上注解@TableField(fill = FieldFill.UPDATE),不然在逻辑删除的时候是不会自动填充的。这里我们使用age字段进行测试,同时在方法中我们也需要将age进行设置值。

image
image

5 选装件alwaysUpdateSomeColumnById

AlwaysUpdateSomeColumnById是MP官方提供的自定义方法,该方法是在我们通过id进行更新的时候,可以设置哪些字段需要更新,哪些字段不需要更新,在需要更新的字段在实体中没有设置值时,它会设置为null(逻辑删除字段方法中已经排除)

首先在我们定义的SQL注入器中的methodList中加入该方法并排除不更新name字段,然后将alwaysUpdateSomeColumnById方法引用到mapper中即可。

@Component
public class MySqlInjector extends DefaultSqlInjector {

@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
// 加载DefaultSqlInjector类提供的所有方法
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
// 再加入我们自定义的方法
methodList.add(new DeleteAllMethod());
// 注入一个InsertBatchSomeColumn方法:在批量插入的时候不插入逻辑删除字段deleted值
methodList.add(new InsertBatchSomeColumn(t ->!t.isLogicDelete()));
// 注入一个逻辑删除方法:在通过id进行逻辑删除的时候自动填充一些字段值,例如操作人、操作时间等。
methodList.add(new LogicDeleteByIdWithFill());
// 注入一个更新固定字段方法:可以设置哪些字段需要更新 哪些字段不需要更新,在需要更新的字段在实体中没有设置值时 它会设置为null(逻辑删除字段方法中已经排除)
methodList.add(new AlwaysUpdateSomeColumnById(t ->!t.getColumn().equals("name")));

return methodList;
}
}
public interface MyBaseMapper<T> extends BaseMapper<T> {

/**
* 删除所有(自定义的全局方法)
* @return 影响行数
*/
int deleteAll();

/**
* 批量插入(排除deleted逻辑字段的插入)
* @param list
* @return
*/
int insertBatchSomeColumn(List<T> list);

/**
* 逻辑删除并自动填充相关字段
* @param entity
* @return
*/
int deleteByIdWithFill(T entity);

/**
* 根据id更新固定的某些字段
* @param entity
* @return
*/
int alwaysUpdateSomeColumnById(@Param(Constants.ENTITY)T entity);
}

然后使用该方法测试,从控制台打印的sql语句中可以看出我在代码中设置了name值,但是update并没有对name进行set,因为我们将其排除了,同时也没有deleted逻辑删除字段,这是方法中自动排除的,另外除了我设置的age字段其他字段都设置为null了,updateTime是之前配置的自动填充。

image
image

MybatisPlus进阶篇到此完结,项目已更新至github:https://github.com/NightOnewith/mp-high

兜里的猫 小程序

dvdf

欢迎访问博客网页版:www.wldeer.com

评论

兜里的猫 : 是md
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×