Mybatis的几种数据插入方式的效率对比


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));
    }

  

结论:

 

 

 

 

 

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

附:表结构

 

 

 

  


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM