mybatisplus批量插入編輯及sql打印


一、自定義數據方法注入 EasySqlInjector 

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.extension.injector.methods.additional.InsertBatchSomeColumn;

import java.util.List;

/**
 * @ClassName EasySqlInjector
 * @Author ZhangRF
 * @CreateDate 2021/01/19
 * @Decription 自定義數據方法注入(改寫mybatis-plus批量插入)
 */
public class EasySqlInjector extends DefaultSqlInjector {
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass);
        methodList.add(new InsertBatchSomeColumn());//批量插入
        methodList.add(new InsertIgnoreBatchAllColumn());//批量插入sql模板(忽略唯一索引沖突)
        methodList.add(new InsertOrUpdateBathColumn());//自定義批量插入sql模板(insert數據存在進行update操作)
        return methodList;
    }
}

二、自定義批量插入sql模板(忽略唯一索引沖突)

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.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
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;

/**
 * @ClassName InsertIgnoreBatchAllColumn
 * @Author ZhangRF
 * @CreateDate 2021/06/25
 * @Decription 自定義批量插入sql模板(忽略唯一索引沖突)
 */
public class InsertIgnoreBatchAllColumn extends AbstractMethod {
    /**
     * mapper 對應的方法名
     */
    private static final String MAPPER_METHOD = "insertIgnoreBatchAllColumn";

    /**
     * 字段篩選條件
     */
    @Setter
    @Accessors(chain = true)
    private Predicate<TableFieldInfo> predicate;

    @SuppressWarnings("Duplicates")
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        KeyGenerator keyGenerator = new NoKeyGenerator();
        SqlMethod sqlMethod = SqlMethod.INSERT_ONE;
        String sqlTemplate = "<script>\nINSERT IGNORE INTO %s %s VALUES %s\n</script>";

        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 (StringUtils.isNotEmpty(tableInfo.getKeyProperty())) {
            if (tableInfo.getIdType() == IdType.AUTO) {
                /* 自增主鍵 */
                keyGenerator = new Jdbc3KeyGenerator();
                keyProperty = tableInfo.getKeyProperty();
                keyColumn = tableInfo.getKeyColumn();
            } else {
                if (null != tableInfo.getKeySequence()) {
                    keyGenerator = TableInfoHelper.genKeyGenerator(tableInfo, builderAssistant, sqlMethod.getMethod(), languageDriver);
                    keyProperty = tableInfo.getKeyProperty();
                    keyColumn = tableInfo.getKeyColumn();
                }
            }
        }
        String sql = String.format(sqlTemplate, tableInfo.getTableName(), columnScript, valuesScript);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
        return this.addInsertMappedStatement(mapperClass, modelClass, MAPPER_METHOD, sqlSource, keyGenerator, keyProperty, keyColumn);
    }
}

三、自定義批量插入sql模板(insert數據存在進行update操作)

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 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 org.springframework.util.StringUtils;

/**
 * @ClassName InsertOrUpdateBathColumn
 * @Author ZhangRF
 * @CreateDate 2021/07/20
 * @Decription 自定義批量插入sql模板(insert數據存在進行update操作)
 */
public class InsertOrUpdateBathColumn extends AbstractMethod {
    /**
     * mapper 對應的方法名
     */
    private static final String MAPPER_METHOD = "insertOrUpdateBathColumn";

    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        final String sqlTemplate = "<script>insert into %s %s values %s ON DUPLICATE KEY UPDATE %s</script>";
        final String tableName = tableInfo.getTableName();
        final String filedSql = prepareFieldSql(tableInfo);
        final String modelValuesSql = prepareModelValuesSql(tableInfo);
        final String duplicateKeySql = prepareDuplicateKeySql(tableInfo);

        KeyGenerator keyGenerator = new NoKeyGenerator();
        String keyProperty = null;
        String keyColumn = null;
        // 表包含主鍵處理邏輯,如果不包含主鍵當普通字段處理
        if (com.baomidou.mybatisplus.core.toolkit.StringUtils.isNotEmpty(tableInfo.getKeyProperty())) {
            if (tableInfo.getIdType() == IdType.AUTO) {
                /* 自增主鍵 */
                keyGenerator = new Jdbc3KeyGenerator();
                keyProperty = tableInfo.getKeyProperty();
                keyColumn = tableInfo.getKeyColumn();
            } else {
                if (null != tableInfo.getKeySequence()) {
                    keyGenerator = TableInfoHelper.genKeyGenerator(tableInfo, builderAssistant, sqlTemplate, languageDriver);
                    keyProperty = tableInfo.getKeyProperty();
                    keyColumn = tableInfo.getKeyColumn();
                }
            }
        }

        final String sqlResult = String.format(sqlTemplate, tableName, filedSql, modelValuesSql, duplicateKeySql);
        SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
        return this.addInsertMappedStatement(mapperClass, modelClass, MAPPER_METHOD, sqlSource, keyGenerator, keyProperty, keyColumn);
    }

    /**
     * 准備ON DUPLICATE KEY UPDATE sql
     *
     * @param tableInfo
     * @return
     */
    private String prepareDuplicateKeySql(TableInfo tableInfo) {
        final StringBuilder duplicateKeySql = new StringBuilder();
        //編輯時不修改id、創建人、創建時間(由於創建人、創建時間自動補全,所以無法屏蔽,目前直支持不修改id)
        if (!StringUtils.isEmpty(tableInfo.getKeyColumn()) &&
                !tableInfo.getKeyColumn().equals("id") &&
                !tableInfo.getKeyColumn().equals("gmt_create") &&
                !tableInfo.getKeyColumn().equals("create_user")) {
            duplicateKeySql.append(tableInfo.getKeyColumn()).append("=values(").append(tableInfo.getKeyColumn()).append("),");
        }

        tableInfo.getFieldList().forEach(x -> {
            duplicateKeySql.append(x.getColumn())
                    .append("=values(")
                    .append(x.getColumn())
                    .append("),");
        });

        duplicateKeySql.delete(duplicateKeySql.length() - 1, duplicateKeySql.length());
        return duplicateKeySql.toString();
    }

    /**
     * 准備屬性名
     *
     * @param tableInfo
     * @return
     */
    private String prepareFieldSql(TableInfo tableInfo) {
        StringBuilder fieldSql = new StringBuilder();
        fieldSql.append(tableInfo.getKeyColumn()).append(",");
        tableInfo.getFieldList().forEach(x -> {
            fieldSql.append(x.getColumn()).append(",");
        });
        fieldSql.delete(fieldSql.length() - 1, fieldSql.length());
        fieldSql.insert(0, "(");
        fieldSql.append(")");
        return fieldSql.toString();
    }

    private String prepareModelValuesSql(TableInfo tableInfo) {
        final StringBuilder valueSql = new StringBuilder();
        valueSql.append("<foreach collection=\"list\" item=\"item\" index=\"index\" open=\"(\" separator=\"),(\" close=\")\">");
        if (!StringUtils.isEmpty(tableInfo.getKeyProperty())) {
            valueSql.append("#{item.").append(tableInfo.getKeyProperty()).append("},");
        }
        tableInfo.getFieldList().forEach(x -> valueSql.append("#{item.").append(x.getProperty()).append("},"));
        valueSql.delete(valueSql.length() - 1, valueSql.length());
        valueSql.append("</foreach>");
        return valueSql.toString();
    }
}

四、完整sql打印

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.springframework.context.annotation.Profile;

import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.regex.Matcher;

/**
 * @ClassName PrintSqlInterceptor
 * @Author ZhangRF
 * @CreateDate 2021/06/29
 * @Decription
 */
@Profile({"dev", "test"})
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})}
)
@SuppressWarnings({"unchecked", "rawtypes"})
@Slf4j
public class PrintSqlInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        try {
            // 獲取xml中的一個select/update/insert/delete節點,是一條SQL語句
            MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
            Object parameter = null;
            // 獲取參數,if語句成立,表示sql語句有參數,參數格式是map形式
            if (invocation.getArgs().length > 1) {
                parameter = invocation.getArgs()[1];
//                log.info("parameter = " + parameter);
            }
            String sqlId = mappedStatement.getId(); // 獲取到節點的id,即sql語句的id
//            log.info("sqlId = " + sqlId);
            BoundSql boundSql = mappedStatement.getBoundSql(parameter); // BoundSql就是封裝myBatis最終產生的sql類
            Configuration configuration = mappedStatement.getConfiguration(); // 獲取節點的配置
            String sql = getSql(configuration, boundSql); // 獲取到最終的sql語句
            log.debug("  完整的 sql  =   {}", sql);
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 執行完上面的任務后,不改變原有的sql執行過程
        return invocation.proceed();
    }

    // 封裝了一下sql語句,使得結果返回完整xml路徑下的sql語句節點id + sql語句
    public static String getSql(Configuration configuration, BoundSql boundSql, String sqlId) {
        String sql = showSql(configuration, boundSql);
        StringBuilder str = new StringBuilder(100);
        str.append(sqlId);
        str.append(":");
        str.append(sql);
        return str.toString();
    }

    // 封裝了一下sql語句,使得結果返回完整xml路徑下的sql語句
    public static String getSql(Configuration configuration, BoundSql boundSql) {
        return showSql(configuration, boundSql);
    }

    // 如果參數是String,則添加單引號, 如果是日期,則轉換為時間格式器並加單引號; 對參數是null和不是null的情況作了處理
    private static String getParameterValue(Object obj) {
        String value = null;
        if (obj instanceof String) {
            value = "'" + obj.toString() + "'";
        } else if (obj instanceof Date) {
            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT,
                    DateFormat.DEFAULT, Locale.CHINA);
            value = "'" + formatter.format(obj) + "'";
        } else {
            if (obj != null) {
                value = obj.toString();
            } else {
                value = "";
            }
        }
        return value;
    }

    // 進行?的替換
    public static String showSql(Configuration configuration, BoundSql boundSql) {
        // 獲取參數
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        // sql語句中多個空格都用一個空格代替
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        if (CollectionUtils.isNotEmpty(parameterMappings) && parameterObject != null) {
            // 獲取類型處理器注冊器,類型處理器的功能是進行java類型和數據庫類型的轉換
            TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
            // 如果根據parameterObject.getClass()可以找到對應的類型,則替換
            if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                sql = sql.replaceFirst("\\?",
                        Matcher.quoteReplacement(getParameterValue(parameterObject)));
            } else {
                // MetaObject主要是封裝了originalObject對象,提供了get和set的方法用於獲取和設置originalObject的屬性值,主要支持對JavaBean、Collection、Map三種類型對象的操作
                MetaObject metaObject = configuration.newMetaObject(parameterObject);
                for (ParameterMapping parameterMapping : parameterMappings) {
                    String propertyName = parameterMapping.getProperty();
                    if (metaObject.hasGetter(propertyName)) {
                        Object obj = metaObject.getValue(propertyName);
                        sql = sql.replaceFirst("\\?",
                                Matcher.quoteReplacement(getParameterValue(obj)));
                    } else if (boundSql.hasAdditionalParameter(propertyName)) {
                        // 該分支是動態sql
                        Object obj = boundSql.getAdditionalParameter(propertyName);
                        sql = sql.replaceFirst("\\?",
                                Matcher.quoteReplacement(getParameterValue(obj)));
                    } else {
                        // 打印出缺失,提醒該參數缺失並防止錯位
                        sql = sql.replaceFirst("\\?", "缺失");
                    }
                }
            }
        }
        return sql;
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof Executor) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) {

    }
}

五、創建自定義mapper   EasyBaseMapper

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

import java.util.List;

/**
 * @ClassName EasyBaseMapper
 * @Author ZhangRF
 * @CreateDate 2021/01/19
 * @Decription 擴展自帶 BaseMapper
 */
public interface EasyBaseMapper<T> extends BaseMapper<T> {
    /**
     * 批量插入 僅適用於mysql
     *
     * @param entityList 實體列表
     * @return 影響行數
     */
    Integer insertBatchSomeColumn(List<T> entityList);

    /**
     * 批量插入(忽略唯一索引沖突) 僅適用於mysql
     *
     * @param entityList 實體列表
     * @return 影響行數
     */
    Integer insertIgnoreBatchAllColumn(List<T> entityList);

    /**
     * 自定義批量插入sql模板(insert數據存在進行update操作)
     *
     * @param entityList 實體列表
     * @return 影響行數
     */
    Integer insertOrUpdateBathColumn(List<T> entityList);
}

六、把創建的自定義工具注入bean

@Bean
    public EasySqlInjector easySqlInjector() {
        return new EasySqlInjector();
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM