前言
后台开发中,批量往数据库写数据是一个很常见的功能,下面就简单实现一下使用 mybatis-plus 来 batch 写入。
实现介绍
添加依赖
在项目的 pom.xml 中配置 mybatis-plus 以及 mysql 相关的依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.4</version>
</dependency>
实现自定义的 mybatis-plus 方法
1、实现自定义的 mybatis-plus 方法 batchInsert。
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Description:批量新增,要求所有非空字段必须有设置值
* 参数:java.util.List<?> dataList
*
* @author fy
* @version 1.0
*/
public class DemoBatchInsertMethod extends AbstractMethod {
private static final long serialVersionUID = -5592924141208308506L;
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass,
TableInfo tableInfo) {
/*
* <p>
* INSERT INTO tableName
* (id,name)
* VALUES
* <foreach collection="dataList" item="data" open="(" close=")" index="index" separator="),(">
* #{data.id},#{data.name}
* </foreach>
* </p>
*/
String sql = "<script>\nINSERT INTO %s %s \nVALUES %s\n</script>";
sql = String.format(sql, tableInfo.getTableName(),
getFieldSql(tableInfo), getValueSql(tableInfo));
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
return this.addInsertMappedStatement(mapperClass, modelClass,
"insertBatchDemo", sqlSource, new NoKeyGenerator(),
tableInfo.getKeyProperty(), tableInfo.getKeyColumn());
}
private String getFieldSql(TableInfo tableInfo) {
return "(" + tableInfo.getAllSqlSelect() + ")";
}
private String getValueSql(TableInfo tableInfo) {
StringBuilder sb = new StringBuilder();
sb.append("<foreach collection=\"dataList\" item=\"data\" open=\"(\" close=\")\"" +
" index=\"index\" separator=\"),(\">\n");
sb.append("#{data.").append(tableInfo.getKeyProperty()).append("},");
AtomicBoolean isNotFirst = new AtomicBoolean(false);
tableInfo.getFieldList().forEach(field -> {
if (isNotFirst.get()) {
sb.append(",");
}
sb.append("#{data.").append(field.getProperty()).append("}");
isNotFirst.set(true);
});
sb.append("\n</foreach>");
return sb.toString();
}
}
2、实现自定义的 mybatis-plus 方法 batchInsertSelective。
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Description:批量新增,只插入传入值的字段
* 参数:java.util.List<?> dataList
*
* @author fy
* @version 1.0
*/
public class DemoBatchInsertSelectiveMethod extends AbstractMethod {
private static final long serialVersionUID = -5592924141208308506L;
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass,
TableInfo tableInfo) {
/*
* <p>
* <foreach collection="dataList" item="data" index="index" separator=";">
* INSERT INTO tableName
* <trim prefix="(" suffix=")" suffixOverrides=",">
* <if test="data.id != null">
* id,
* </if>
* <if test="data.name != null">
* name,
* </if>
* </trim>
* VALUES
* <trim prefix="(" suffix=")" suffixOverrides=",">
* <if test="data.id != null">
* #{data.id},
* </if>
* <if test="data.name != null">
* #{data.name},
* </if>
* </trim>
* </foreach>
* </p>
*/
String sql = "<script>\n<foreach collection=\"dataList\" item=\"data\"" +
" index=\"index\" separator=\";\">\n" +
"INSERT INTO %s %s VALUES %s \n" +
"</foreach>\n</script>";
sql = String.format(sql, tableInfo.getTableName(),
getFieldSql(tableInfo), getValueSql(tableInfo));
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
// NoKeyGenerator:默认空实现,不需要对主键单独处理;
// Jdbc3KeyGenerator:主要用于数据库的自增主键,比如 MySQL、PostgreSQL;
// SelectKeyGenerator:主要用于数据库不支持自增主键的情况,比如 Oracle、DB2;
return this.addInsertMappedStatement(mapperClass, modelClass,
"insertBatchSelectiveDemo", sqlSource, new NoKeyGenerator(),
tableInfo.getKeyProperty(), tableInfo.getKeyColumn());
}
private String getFieldSql(TableInfo tableInfo) {
StringBuilder sb = new StringBuilder();
sb.append("<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">");
sb.append("<if test=\"data.").append(tableInfo.getKeyProperty())
.append(" != null\">").append(tableInfo.getKeyColumn())
.append(",</if>");
tableInfo.getFieldList().forEach(field ->
sb.append("<if test=\"data.").append(field.getProperty())
.append(" != null\">").append(field.getColumn())
.append(",</if>")
);
sb.append("</trim>");
return sb.toString();
}
private String getValueSql(TableInfo tableInfo) {
StringBuilder sb = new StringBuilder();
sb.append("<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">");
sb.append("<if test=\"data.").append(tableInfo.getKeyProperty())
.append(" != null\">#{data.").append(tableInfo.getKeyProperty())
.append("},</if>");
tableInfo.getFieldList().forEach(field ->
sb.append("<if test=\"data.").append(field.getProperty())
.append(" != null\">#{data.").append(field.getProperty())
.append("},</if>")
);
sb.append("</trim>");
return sb.toString();
}
}
将自定义的方法注入到 mp 的管理器中
将自定义的 batchInsert 及 batchInsertSelective,两个方法加入到 mp 的管理器。
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.fy.config.mybatis.plus.method.DemoBatchInsertMethod;
import com.fy.config.mybatis.plus.method.DemoBatchInsertSelectiveMethod;
import org.springframework.context.annotation.Configuration;
import java.util.List;
/**
* Description:自定义 sql 注入器
*
* @author fy
* @version 1.0
*/
@Configuration
public class CustomizedSqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
methodList.add(new DemoBatchInsertMethod());
methodList.add(new DemoBatchInsertSelectiveMethod());
return methodList;
}
}
定义自己的顶层 mapper
自定一个 RootMapper,保留对 mp 的 BaseMapper 的传递,在该 mapper 中定义刚刚的两个方法。
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* Description:自定义顶层 Mapper
*
* @author fy
* @version 1.0
*/
public interface RootMapper<E> extends BaseMapper<E> {
/***
* <p>
* 自定义的批量新增方法
* </p>
* @author fy
*
* @param dataList 数据列表
* @return int
*/
int insertBatchDemo(@Param("dataList") List<E> dataList);
/***
* <p>
* 自定义的批量新增方法
* </p>
* @author fy
*
* @param dataList 数据列表
* @return int
*/
int insertBatchSelectiveDemo(@Param("dataList") List<E> dataList);
}
继承自定义顶层 mapper
在需要批量操作的 mapper 上改成继承自定义的 RootMapper。
import com.fy.entity.User;
import com.fy.config.mybatis.plus.method.bean.RootMapper;
import java.util.List;
/**
* <p>
* Mapper 接口
* </p>
*
* @author fy
*/
public interface UserMapper extends RootMapper<User> {
}
使用
使用 spring 注入,然后调用即可
@Autowired
private UserMapper userMapper;
public void testMybatisPlusBatchInsertUser(int count) {
long t1 = System.currentTimeMillis();
userMapper.insertBatchDemo(getUserList(count));
System.out.println("【mybatis-plus】插入条数:【" + count + "】耗时:【"
+ (System.currentTimeMillis() - t1) + "】");
}
结语
到此,使用 mybatis-plus 来 batch 写入数据的实现就介绍完了,后续继续其他方式的批量写入 …
如果您看到了这里,欢迎和我沟通交流!
一个95后码农
个人博客:fy-blog