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)); }
结论:
注:每种插入方式都是在空表情况下进行。
附:表结构