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));
}
結論:

注:每種插入方式都是在空表情況下進行。
附:表結構

