一、自定義數據方法注入 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(); }