Mybatis作为一种非常流行的ORM框架,经常会遇到需要插入大量数据的情况,本文对比一下几种插入方式的效率。
1、DefaultSqlSession,单条插入
private static void insertSingle(SqlSessionFactory factory) {
SqlSession sqlSession = factory.openSession(false);
SysUserMapper mapper = sqlSession.getMapper(SysUserMapper.class);
long start = System.currentTimeMillis();
for (long i = 0; i < SIZE; i++) {
SysUser sysUser = createSysUser();
mapper.insertUser(sysUser);
}
sqlSession.commit();
sqlSession.close();
long end = System.currentTimeMillis();
System.out.println(String.format("single insert %s items cost:%s ms.", SIZE, end - start));
}
2、DefaultSqlSession,BatchExecutor批量插入
private static void insertBatch(SqlSessionFactory factory) { SqlSession sqlSession = factory.openSession(ExecutorType.BATCH, false); SysUserMapper mapper = sqlSession.getMapper(SysUserMapper.class); long start = System.currentTimeMillis(); for (long i = 0; i < SIZE; i++) { SysUser sysUser = createSysUser(); mapper.insertUser(sysUser); } sqlSession.commit(); sqlSession.close(); long end = System.currentTimeMillis(); System.out.println(String.format("batch insert %s items cost:%s ms.", SIZE, end - start)); }
3、Mybatis的动态Sql,foreach标签拼接
<insert id="insertUsers" parameterType="java.util.List"> insert into sys_user( user_id, dept_id, login_name, user_name, email, avatar, phonenumber, sex, password, salt, status, create_by, remark, create_time ) values <foreach collection="list" item="item" index="index" separator="," > (#{item.userId}, #{item.deptId}, #{item.loginName}, #{item.userName}, #{item.email}, #{item.avatar}, #{item.phonenumber}, #{item.sex}, #{item.password}, #{item.salt}, #{item.status}, #{item.createBy}, #{item.remark}, sysdate()) </foreach> </insert>
private static void insertForeach(SqlSessionFactory factory) {
SqlSession sqlSession = factory.openSession(false);
SysUserMapper mapper = sqlSession.getMapper(SysUserMapper.class);
long start = System.currentTimeMillis();
List<SysUser> userList = new ArrayList<>();
for (long i = 0; i < SIZE; i++) {
SysUser sysUser = createSysUser();
userList.add(sysUser);
}
mapper.insertUsers(userList);
sqlSession.commit();
sqlSession.close();
long end = System.currentTimeMillis();
System.out.println(String.format("mybatis foreach insert %s items cost:%s ms.", SIZE, end - start));
}
4、手动拼接Sql语句,然后调用Mybatis框架执行现成的Sql语句
需要定义一个对象封装sql语句:
public class SqlVo {
private String sql;
public SqlVo(String sql) {
this.sql = sql;
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
}
mapper文件里定义一个标签,将sql语句作为参数传入:
<select id="executeSql" parameterType="domain.SqlVo">
${sql}
</select>
然后拼接Sql语句:
private static void insertSqlJoin(SqlSessionFactory factory) {
SqlSession sqlSession = factory.openSession(false);
long start = System.currentTimeMillis();
SysUserMapper mapper = sqlSession.getMapper(SysUserMapper.class);
String sqlHeader = " insert into sys_user(dept_id,login_name,user_name,email,avatar,phonenumber,sex,password,salt,status,create_by,remark,create_time)values";
StringBuilder sql = new StringBuilder(sqlHeader);
for (long i = 0; i < SIZE; i++) {
SysUser sysUser = createSysUser();
sql.append("(");
sql.append(sysUser.getDeptId()).append(",");
sql.append("'").append(sysUser.getLoginName()).append("'").append(",");
sql.append("'").append(sysUser.getUserName()).append("'").append(",");
sql.append("'").append(sysUser.getEmail()).append("'").append(",");
sql.append("'").append(sysUser.getAvatar()).append("'").append(",");
sql.append("'").append(sysUser.getPhonenumber()).append("'").append(",");
sql.append("'").append(sysUser.getSex()).append("'").append(",");
sql.append("'").append(sysUser.getPassword()).append("'").append(",");
sql.append("'").append(sysUser.getSalt()).append("'").append(",");
sql.append("'").append(sysUser.getStatus()).append("'").append(",");
sql.append("'").append(sysUser.getCreateBy()).append("'").append(",");
sql.append("'").append(sysUser.getRemark()).append("'").append(",");
sql.append("'").append(format0.format(sysUser.getCreateTime())).append("'").append(")");
sql.append(",");
}
sql.setCharAt(sql.length() - 1, ';');
mapper.executeSql(new SqlVo(sql.toString()));
sqlSession.commit();
sqlSession.close();
long end = System.currentTimeMillis();
System.out.println(String.format("sql join insert %s items cost:%s ms.", SIZE, end - start));
}
5、采用Load Data Local InFile,这种方式与mybatis框架无关,但也可以适配使用:
定义一个工具类:
import javax.sql.DataSource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class LoadDataInFileUtil {
private Connection conn;
private int loadoadFromInputStream(String loadDataSql,
InputStream dataStream,
DataSource dataSource) throws SQLException {
if (null == dataStream) {
return 0;
}
conn = dataSource.getConnection();
PreparedStatement statement = conn.prepareStatement(loadDataSql);
int result = 0;
if (statement.isWrapperFor(com.mysql.jdbc.Statement.class)) {
com.mysql.jdbc.PreparedStatement mysqlStatement = statement.unwrap(com.mysql.jdbc.PreparedStatement.class);
mysqlStatement.setLocalInfileInputStream(dataStream);
result = mysqlStatement.executeUpdate();
}
return result;
}
public String assembleSql(String dataBaseName, String tableName, String[] columnNames) {
String insertColumnName = String.join( ",",columnNames);
String sql = "LOAD DATA LOCAL INFILE 'sql.csv' IGNORE INTO TABLE " + dataBaseName + "." + tableName + "(" + insertColumnName + ")";
return sql;
}
public void builderAppend(StringBuilder builder, Object object) {
builder.append(object);
builder.append("\t");
}
public void builderEnd(StringBuilder builder, Object object) {
builder.append(object);
builder.append("\n");
}
public int fastInsertData(String sql, StringBuilder builder,DataSource dataSource) {
int rows = 0;
InputStream is = null;
try {
byte[] bytes = builder.toString().getBytes();
if (bytes.length > 0) {
is = new ByteArrayInputStream(bytes);
rows = loadoadFromInputStream(sql, is, dataSource);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (null != is) {
is.close();
}
if (null != conn) {
conn.close();
}
} catch (IOException | SQLException e) {
e.printStackTrace();
}
}
return rows;
}
}
业务流程:
private static void insertLoadDataLocalInFile(SqlSessionFactory factory) {
long start = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < SIZE; i++) {
SysUser sysUser = createSysUser();
loadDataInFileUtil.builderAppend(sb, sysUser.getDeptId());
loadDataInFileUtil.builderAppend(sb, sysUser.getLoginName());
loadDataInFileUtil.builderAppend(sb, sysUser.getUserName());
loadDataInFileUtil.builderAppend(sb, sysUser.getEmail());
loadDataInFileUtil.builderAppend(sb, sysUser.getAvatar());
loadDataInFileUtil.builderAppend(sb, sysUser.getPhonenumber());
loadDataInFileUtil.builderAppend(sb, sysUser.getSex());
loadDataInFileUtil.builderAppend(sb, sysUser.getPassword());
loadDataInFileUtil.builderAppend(sb, sysUser.getSalt());
loadDataInFileUtil.builderAppend(sb, sysUser.getStatus());
loadDataInFileUtil.builderAppend(sb, sysUser.getCreateBy());
loadDataInFileUtil.builderAppend(sb, sysUser.getRemark());
loadDataInFileUtil.builderEnd(sb, format0.format(sysUser.getCreateTime()));
}
String sql = loadDataInFileUtil.assembleSql("ry", "sys_user", COLUMN_NAME);
DataSource dataSource = factory.getConfiguration().getEnvironment().getDataSource();
loadDataInFileUtil.fastInsertData(sql, sb, dataSource);
long end = System.currentTimeMillis();
System.out.println(String.format("Load Data Local InFile insert %s items cost:%s ms.", SIZE, end - start));
}
结论:

注:每种插入方式都是在空表情况下进行。
附:表结构

