文章目录

  • 前言
  • 一、代码结构
  • 二、使用步骤
  • 1.创建通用方法的实现类(InsertIgnore、InsertIgnoreBatch、Replace)
  • 2.MybatisPlus自定义SQL方法枚举
  • 3.创建自定义SQL注入器CustomerSqlInjector
  • 4.自定义基础CustomerMapper继承BaseMapper
  • 5.自定义基础Service继承IService及实现类
  • 6.增加或MybatisPlus配置类,加载自定义sql注入器
  • 总结



前言

虽然MyBats-Plus的BaseMapper提供了非常好用,非常多的方法,但是还是不能适应业务的变化,那么如果我们需要扩充BaseMapper中的方法又该如何实现呢。

MyBats-Plus在一开始就给大家提供了很多通用的方法,在DefaultSqlInjector这个类中,在MethodList这个集合当中包含的都是通用方法类,如果想要使用自定义通用方法,也需要添加到这个集合当中。

自定义自己的通用方法可以实现接口 ISqlInjector 也可以继承抽象类 AbstractSqlInjector 注入通用方法 SQL 语句 然后继承 BaseMapper 添加自定义方法,全局配置 sqlInjector 注入 MP 会自动将类所有方法注入到 mybatis 容器中。

下面我们一起来实现这个功能;


一、代码结构

mybatis 自定义KeyGenerator processAfter mybatisplus自定义方法_sql


二、使用步骤

1.创建通用方法的实现类(InsertIgnore、InsertIgnoreBatch、Replace)

InsertIgnore 插入数据实现类,如果中已经存在相同的记录,则忽略当前新数据
代码如下:

package com.chqiuu.common.expand.mybatisplus.methods;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
import com.chqiuu.common.expand.mybatisplus.CustomerSqlMethod;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

/**
 * 插入一条数据(选择字段插入)
 * INSERT IGNORE 表示,如果中已经存在相同的记录,则忽略当前新数据;
 *
 * @author chqiuu
 */
public class InsertIgnore extends AbstractMethod {

    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        KeyGenerator keyGenerator = new NoKeyGenerator();
        CustomerSqlMethod sqlMethod = CustomerSqlMethod.INSERT_IGNORE_ONE;
        String columnScript = SqlScriptUtils.convertTrim(tableInfo.getAllInsertSqlColumnMaybeIf(null),
                LEFT_BRACKET, RIGHT_BRACKET, null, COMMA);
        String valuesScript = SqlScriptUtils.convertTrim(tableInfo.getAllInsertSqlPropertyMaybeIf(null),
                LEFT_BRACKET, RIGHT_BRACKET, null, COMMA);
        String keyProperty = null;
        String keyColumn = null;
        // 表包含主键处理逻辑,如果不包含主键当普通字段处理
        if (StringUtils.isNotBlank(tableInfo.getKeyProperty())) {
            if (tableInfo.getIdType() == IdType.AUTO) {
                /* 自增主键 */
                keyGenerator = new Jdbc3KeyGenerator();
                keyProperty = tableInfo.getKeyProperty();
                keyColumn = tableInfo.getKeyColumn();
            } else {
                if (null != tableInfo.getKeySequence()) {
                    keyGenerator = TableInfoHelper.genKeyGenerator(sqlMethod.getMethod(), tableInfo, builderAssistant);
                    keyProperty = tableInfo.getKeyProperty();
                    keyColumn = tableInfo.getKeyColumn();
                }
            }
        }
        String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), columnScript, valuesScript);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return this.addInsertMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource, keyGenerator, keyProperty, keyColumn);
    }
}

InsertIgnoreBatch 批量插入数据,如果中已经存在相同的记录,则忽略当前新数据

代码如下:

package com.chqiuu.common.expand.mybatisplus.methods;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
import com.chqiuu.common.expand.mybatisplus.CustomerSqlMethod;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

import java.util.List;
import java.util.function.Predicate;

/**
 * 批量新增数据,自选字段 insert ignore
 *
 * @author chqiu
 */
public class InsertIgnoreBatch extends AbstractMethod {

    /**
     * mapper 对应的方法名
     */
    private static final String MAPPER_METHOD = "insertIgnoreBatch";

    /**
     * 字段筛选条件
     */
    @Setter
    @Accessors(chain = true)
    private Predicate<TableFieldInfo> predicate;

    @SuppressWarnings("Duplicates")
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        KeyGenerator keyGenerator = new NoKeyGenerator();
        CustomerSqlMethod sqlMethod = CustomerSqlMethod.INSERT_IGNORE_ONE;
        List<TableFieldInfo> fieldList = tableInfo.getFieldList();
        String insertSqlColumn = tableInfo.getKeyInsertSqlColumn(false) +
                this.filterTableFieldInfo(fieldList, predicate, TableFieldInfo::getInsertSqlColumn, EMPTY);
        String columnScript = LEFT_BRACKET + insertSqlColumn.substring(0, insertSqlColumn.length() - 1) + RIGHT_BRACKET;
        String insertSqlProperty = tableInfo.getKeyInsertSqlProperty(ENTITY_DOT, false) +
                this.filterTableFieldInfo(fieldList, predicate, i -> i.getInsertSqlProperty(ENTITY_DOT), EMPTY);
        insertSqlProperty = LEFT_BRACKET + insertSqlProperty.substring(0, insertSqlProperty.length() - 1) + RIGHT_BRACKET;
        String valuesScript = SqlScriptUtils.convertForeach(insertSqlProperty, "list", null, ENTITY, COMMA);
        String keyProperty = null;
        String keyColumn = null;
        // 表包含主键处理逻辑,如果不包含主键当普通字段处理
        if (tableInfo.havePK()) {
            if (tableInfo.getIdType() == IdType.AUTO) {
                /* 自增主键 */
                keyGenerator = new Jdbc3KeyGenerator();
                keyProperty = tableInfo.getKeyProperty();
                keyColumn = tableInfo.getKeyColumn();
            } else {
                if (null != tableInfo.getKeySequence()) {
                    keyGenerator = TableInfoHelper.genKeyGenerator(MAPPER_METHOD, tableInfo, builderAssistant);
                    keyProperty = tableInfo.getKeyProperty();
                    keyColumn = tableInfo.getKeyColumn();
                }
            }
        }
        String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), columnScript, valuesScript);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return this.addInsertMappedStatement(mapperClass, modelClass, MAPPER_METHOD, sqlSource, keyGenerator, keyProperty, keyColumn);
    }

    @Override
    public String getMethod(SqlMethod sqlMethod) {
        // 自定义 mapper 方法名
        return MAPPER_METHOD;
    }
}

Replace替换数据,如果中已经存在相同的记录,则覆盖旧数据

代码如下:

package com.chqiuu.common.expand.mybatisplus.methods;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
import com.chqiuu.common.expand.mybatisplus.CustomerSqlMethod;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

/**
 * 替换数据实现类
 * 替换数据,如果中已经存在相同的记录,则覆盖旧数据
 *
 * @author chqiu
 */
public class Replace extends AbstractMethod {
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        KeyGenerator keyGenerator = new NoKeyGenerator();
        CustomerSqlMethod sqlMethod = CustomerSqlMethod.REPLACE_ONE;
        String columnScript = SqlScriptUtils.convertTrim(tableInfo.getAllInsertSqlColumnMaybeIf(null),
                LEFT_BRACKET, RIGHT_BRACKET, null, COMMA);
        String valuesScript = SqlScriptUtils.convertTrim(tableInfo.getAllInsertSqlPropertyMaybeIf(null),
                LEFT_BRACKET, RIGHT_BRACKET, null, COMMA);
        String keyProperty = null;
        String keyColumn = null;
        // 表包含主键处理逻辑,如果不包含主键当普通字段处理
        if (StringUtils.isNotBlank(tableInfo.getKeyProperty())) {
                if (null != tableInfo.getKeySequence()) {
                    keyGenerator = TableInfoHelper.genKeyGenerator(sqlMethod.getMethod(), tableInfo, builderAssistant);
                    keyProperty = tableInfo.getKeyProperty();
                    keyColumn = tableInfo.getKeyColumn();
            }
        }
        String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), columnScript, valuesScript);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return this.addInsertMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource, keyGenerator, keyProperty, keyColumn);
    }
}

2.MybatisPlus自定义SQL方法枚举

代码如下:

package com.chqiuu.common.expand.mybatisplus;

import lombok.Getter;

/**
 * MybatisPlus自定义SQL方法枚举
 *
 * @author chqiu
 */
@Getter
public enum CustomerSqlMethod {
    /**
     * 插入
     */
    INSERT_IGNORE_ONE("insertIgnore", "插入一条数据(选择字段插入),如果中已经存在相同的记录,则忽略当前新数据", "<script>\nINSERT IGNORE INTO %s %s VALUES %s\n</script>"),
    /**
     * 替换
     */
    REPLACE_ONE("replace", "替换一条数据(选择字段插入),存在则替换,不存在则插入", "<script>\nREPLACE INTO %s %s VALUES %s\n</script>");

    private final String method;
    private final String desc;
    private final String sql;

    CustomerSqlMethod(String method, String desc, String sql) {
        this.method = method;
        this.desc = desc;
        this.sql = sql;
    }
}

3.创建自定义SQL注入器CustomerSqlInjector

代码如下:

package com.chqiuu.common.expand.mybatisplus;

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.chqiuu.common.expand.mybatisplus.methods.InsertIgnore;
import com.chqiuu.common.expand.mybatisplus.methods.InsertIgnoreBatch;
import com.chqiuu.common.expand.mybatisplus.methods.Replace;

import java.util.List;

/**
 * 自定义sql注入器,增加通用方法
 *
 * @author chqiu
 */
public class CustomerSqlInjector extends DefaultSqlInjector {

    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass);
        // 插入数据,如果中已经存在相同的记录,则忽略当前新数据
        methodList.add(new InsertIgnore());
        // 批量插入数据,如果中已经存在相同的记录,则忽略当前新数据
        methodList.add(new InsertIgnoreBatch());
        // 替换数据,如果中已经存在相同的记录,则覆盖旧数据
        methodList.add(new Replace());
        return methodList;
    }
}

4.自定义基础CustomerMapper继承BaseMapper

代码如下:

package com.chqiuu.common.expand.mybatisplus;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.chqiuu.common.expand.mybatisplus.methods.InsertIgnore;
import com.chqiuu.common.expand.mybatisplus.methods.Replace;

import java.util.List;

/**
 * 自定义基础Mapper继承BaseMapper
 *
 * @author chqiu
 */
public interface CustomerMapper<T> extends BaseMapper<T> {
    /**
     * 插入数据,如果中已经存在相同的记录,则忽略当前新数据
     * {@link InsertIgnore}
     *
     * @param entity 实体类
     * @return 影响条数
     */
    int insertIgnore(T entity);

    /**
     * 批量插入数据,如果中已经存在相同的记录,则忽略当前新数据
     * {@link InsertIgnore}
     *
     * @param entityList 实体类列表
     * @return 影响条数
     */
    int insertIgnoreBatch(List<T> entityList);

    /**
     * 替换数据
     * replace into表示插入替换数据,需求表中有PrimaryKey,或者unique索引,如果数据库已经存在数据,则用新数据替换,如果没有数据效果则和insert into一样;
     * {@link Replace}
     *
     * @param entity 实体类
     * @return 影响条数
     */
    int replace(T entity);
}

5.自定义基础Service继承IService及实现类

自定义基础Service继承IService接口类

代码如下:

package com.chqiuu.common.expand.mybatisplus.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.chqiuu.common.expand.mybatisplus.methods.InsertIgnore;
import com.chqiuu.common.expand.mybatisplus.methods.Replace;

import java.util.List;

/**
 * 自定义基础Service继承IService及实现类
 *
 * @author chqiu
 */
public interface CustomerService<T> extends IService<T> {
    /**
     * 插入数据,如果中已经存在相同的记录,则忽略当前新数据
     * {@link InsertIgnore}
     *
     * @param entity 实体类
     * @return 影响条数
     */
    int insertIgnore(T entity);

    /**
     * 批量插入数据,如果中已经存在相同的记录,则忽略当前新数据
     * {@link InsertIgnore}
     *
     * @param entityList 实体类列表
     * @return 影响条数
     */
    int insertIgnoreBatch(List<T> entityList);

    /**
     * 替换数据
     * replace into表示插入替换数据,需求表中有PrimaryKey,或者unique索引,如果数据库已经存在数据,则用新数据替换,如果没有数据效果则和insert into一样;
     * {@link Replace}
     *
     * @param entity 实体类
     * @return 影响条数
     */
    int replace(T entity);
}

自定义基础Service继承IService实现类

代码如下:

package com.chqiuu.common.expand.mybatisplus.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chqiuu.common.expand.mybatisplus.mapper.CustomerMapper;
import com.chqiuu.common.expand.mybatisplus.service.CustomerService;

import java.util.List;

/**
 * 此处可以写通用的Service
 *
 * @param <M>
 * @param <T>
 * @author chqiu
 */
public class CustomerServiceImpl<M extends CustomerMapper<T>, T> extends ServiceImpl<CustomerMapper<T>, T> implements CustomerService<T> {

    @Override
    public int insertIgnore(T entity) {
        return baseMapper.insertIgnore(entity);
    }

    @Override
    public int insertIgnoreBatch(List<T> entityList) {
        return baseMapper.insertIgnoreBatch(entityList);
    }

    @Override
    public int replace(T entity) {
        return baseMapper.replace(entity);
    }
}

6.增加或MybatisPlus配置类,加载自定义sql注入器

代码如下:

package com.chqiuu.config;

import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.chqiuu.common.expand.mybatisplus.CustomerSqlInjector;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Mybatis配置
 *
 * @author chqiu
 */
@Configuration(proxyBeanMethods = false)
@MapperScan("com.chqiuu.modules.**.mapper")
public class MybatisConfig {
    /**
     * 3.4.0 以后的配置方式
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 乐观锁
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        // 分页配置
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return interceptor;
    }

    /**
     * 自定义sql注入器
     * 关键部位重要的事情说三遍,不注入不生效,不注入不生效,不注入不生效
     */
    @Bean
    public ISqlInjector iSqlInjector() {
        return new CustomerSqlInjector();
    }
}

总结

以上就算完成了。