一、添加自定义方法SQL注入器
//将批量插入的方法添加method
public class CustomSqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
// 获取父类SQL注入方法列表
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
// 将批量插入方法添加进去,这个方法在下方会重写
methodList.add(new CustomSqlInjector ());
return methodList;
}
}
二、方法添加到spring管理
//将上方的方法托管到spring管理
@Configuration
public class MybatisPlusConfig {
@Bean
public CustomSqlInjector customSqlInjector() {
return new CustomSqlInjector();
}
}
三、解决中批量插入null 属性不能使用默认值的问题
就是重新写一下InsertBatchSomeColumn 这个类,然后修改其中的加载参数方法
上述流程只能保证可以使用,但是还是存在限制必须全字段有值,或者可以为null
/**
* 增强版的批量插入,解决mp 中批量插入null 属性不能使用默认值的问题
*/
@SuppressWarnings("serial")
@AllArgsConstructor
@NoArgsConstructor
public class InsertBatchSomeColumn extends AbstractMethod {
/**
* 字段筛选条件
*/
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;
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 -> getInsertSqlProperty(i, 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(getMethod(sqlMethod), 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, getMethod(sqlMethod), sqlSource, keyGenerator,
keyProperty, keyColumn);
}
public String getMethod(SqlMethod sqlMethod) {
// 自定义 mapper 方法名
return "insertBatchSomeColumn";
}
/**
* <p>
* 重写该方法,项目启动加载的时候会设置
* 如果对应的参数值为空,就使用数据库的默认值
* </p>
*
* @param tableFieldInfo
* @param prefix
* @return
*/
private String getInsertSqlProperty(TableFieldInfo tableFieldInfo, final String prefix) {
String newPrefix = prefix == null ? "" : prefix;
String elPart = SqlScriptUtils.safeParam(newPrefix + tableFieldInfo.getEl());
//属性为空时使用默认值
String result =
SqlScriptUtils.convertIf(elPart, String.format("%s != null", newPrefix + tableFieldInfo.getEl()),
false) + SqlScriptUtils.convertIf("default",
String.format("%s == null", newPrefix + tableFieldInfo.getEl()), false);
return result + ",";
}
}
四、添加通用mapper&解决大数据插入InsertBatchSomeColumn报错问题
报错原因:数据量过大超过mysql的单次传输限制
查询限制:select @@max_allowed_packet/(1024*1024);如果数据量过大建议调用分页进行批量插入,因为sql如果拼接过大,在sql解析的时候也会比较耗时
/**
* 自定义Mapper,添加批量插入接口
*/
public interface CustomMapper<T> extends BaseMapper<T> {
/**
* <p>
* 批量插入,方法的名字是固定的不能随意设置
* 命名来源InsertBatchSomeColumn.getMethod()
* </p>
* @param entityList 实体列表
* @return 影响行数
*/
Integer insertBatchSomeColumn(Collection<T> entityList);
/**
* <p>
* 解决大数据插入InsertBatchSomeColumn报错问题。
* </p>
* @param entityList
* @param batchSize
*/
@Transactional(rollbackFor = {Exception.class})
default void saveBatchsss(Collection<T> entityList, int batchSize) {
int size = entityList.size();
int idxLimit = Math.min(batchSize, size);
int i = 1;
//保存单批提交的数据集合
List<T> oneBatchList = new ArrayList<>();
for (Iterator<T> var7 = entityList.iterator(); var7.hasNext(); ++i) {
T element = var7.next();
oneBatchList.add(element);
if (i == idxLimit) {
insertBatchSomeColumn(oneBatchList);
//每次提交后需要清空集合数据
oneBatchList.clear();
idxLimit = Math.min(idxLimit + batchSize, size);
}
}
}
}
想了解更多的请关注博主另一篇文章:MyBatis 批量插入-优化&效率对比