MyBatis 最佳实践篇 2:批量更新

2019-04-11  本文已影响0人  兆雪儿

MyBatis 一般有三种方式可以实现批量更新,分别为:for 循环、动态sql 的 foreach 元素和 ExecutorType.BATCH。下面我们分别来介绍这三种方式以及其各自的优缺点。

1 for 循环实现

在代码中直接使用 for 循环,每一个循环执行一次更新和自动提交。如下:

public void testByFor() {
        SqlSession session = FactoryBuildByXML.getFactory().openSession(true);
        try {
            TagMapper tagMapper = session.getMapper(TagMapper.class);

            long start = System.currentTimeMillis();
            for (int i = 0; i < 10; i++) {
                Tag tag = new Tag(null, "tagname" + i, "This is tag" + i);
                tagMapper.insertTag(tag);
            }
            System.out.println("共用时:" + (System.currentTimeMillis() - start));
        } finally {
            session.close();
        }
}
<insert id="insertTag" parameterType="Tag">
  insert into tag(
    id,
    name,
    remark
  )values (
    #{id},
    #{name},
    #{remark}
  )
</insert>

执行结果:

DEBUG [main] - ==>  Preparing: insert into tag( id, name, remark )values ( ?, ?, ? ) 
DEBUG [main] - ==> Parameters: null, tagname0(String), This is tag0(String)
DEBUG [main] - <==    Updates: 1
DEBUG [main] - ==>  Preparing: insert into tag( id, name, remark )values ( ?, ?, ? ) 
DEBUG [main] - ==> Parameters: null, tagname1(String), This is tag1(String)
DEBUG [main] - <==    Updates: 1
//此处略 2-8
DEBUG [main] - ==>  Preparing: insert into tag( id, name, remark )values ( ?, ?, ? ) 
DEBUG [main] - ==> Parameters: null, tagname9(String), This is tag9(String)
DEBUG [main] - <==    Updates: 1
共用时:1040

这种方式的缺点显而易见:每条语句都要进行一次与数据库的会话,性能最低,因此不建议使用。

2 foreach 元素实现

使用 MyBatis 提供的动态 sql 元素 foreach 进行批量更新和自动提交。如下:

    public void testByForeach() {
        SqlSession session = FactoryBuildByXML.getFactory().openSession(true);
        try {
            TagMapper tagMapper = session.getMapper(TagMapper.class);

            long start = System.currentTimeMillis();
            List<Tag> tagList = new ArrayList<Tag>();
            for (int i = 0; i < 10; i++) {
                Tag tag = new Tag(null, "tagname2-" + i, "This is tag by foreach" + i);
                tagList.add(tag);
            }
            tagMapper.insertBatch(tagList);
            System.out.println("共用时:" + (System.currentTimeMillis() - start));
        } finally {
            session.close();
        }
    }
<insert id="insertBatch" parameterType="Tag">
  insert into tag(
    id,
    name,
    remark
  )values
  <foreach collection="list" separator="," item="item" >
    (#{item.id},#{item.name},#{item.remark})
  </foreach>
</insert>

执行结果:

DEBUG [main] - ==>  Preparing: insert into tag( id, name, remark )values (?,?,?) , (?,?,?) , (?,?,?) , (?,?,?) , (?,?,?) , (?,?,?) , (?,?,?) , (?,?,?) , (?,?,?) , (?,?,?) 
DEBUG [main] - ==> Parameters: null, tagname2-0(String), This is tag by foreach0(String), null, tagname2-1(String), This is tag by foreach1(String), null, tagname2-2(String), This is tag by foreach2(String), null, tagname2-3(String), This is tag by foreach3(String), null, tagname2-4(String), This is tag by foreach4(String), null, tagname2-5(String), This is tag by foreach5(String), null, tagname2-6(String), This is tag by foreach6(String), null, tagname2-7(String), This is tag by foreach7(String), null, tagname2-8(String), This is tag by foreach8(String), null, tagname2-9(String), This is tag by foreach9(String)
DEBUG [main] - <==    Updates: 10
共用时:517

从执行时间我们就可以看出,相对于 for 循环,这种方式的执行时间明显少很多(当然最合理的校验对比方式是使用更多的循环次数)。

SHOW VARIABLES like '%packet%';
SHOW VARIABLES like '%net_buffer%';
结果1 结果2

3 ExecutorType.BATCH 实现

MyBatis 同样提供另外一种批量更新的方式:ExecutorType.BATCH,它允许我们一次性向数据库发送多条 sql,然后批量执行和提交。使用如下:

    public void testByExecutorTypeBatch() {
        SqlSession session = FactoryBuildByXML.getFactory().openSession(ExecutorType.BATCH,false);
        try {
            TagMapper tagMapper = session.getMapper(TagMapper.class);

            long start = System.currentTimeMillis();
            for (int i = 1; i <= 10; i++) {
                Tag tag = new Tag(null, "tagname3-" + i, "This is tag by ExecutorType.Batch" + i);
                tagMapper.insertTag(tag);
                if (i % 5 == 0){
                    session.commit();
                    session.clearCache();
                }
            }
            System.out.println("共用时:" + (System.currentTimeMillis() - start));
        } finally {
            session.close();
        }
    }

sql 同 for 循环,此处不再赘述。

执行结果:

DEBUG [main] - ==>  Preparing: insert into tag( id, name, remark )values ( ?, ?, ? ) 
DEBUG [main] - ==> Parameters: null, tagname3-1(String), This is tag by ExecutorType.Batch1(String)
DEBUG [main] - ==> Parameters: null, tagname3-2(String), This is tag by ExecutorType.Batch2(String)
DEBUG [main] - ==> Parameters: null, tagname3-3(String), This is tag by ExecutorType.Batch3(String)
DEBUG [main] - ==> Parameters: null, tagname3-4(String), This is tag by ExecutorType.Batch4(String)
DEBUG [main] - ==> Parameters: null, tagname3-5(String), This is tag by ExecutorType.Batch5(String)
DEBUG [main] - ==>  Preparing: insert into tag( id, name, remark )values ( ?, ?, ? ) 
DEBUG [main] - ==> Parameters: null, tagname3-6(String), This is tag by ExecutorType.Batch6(String)
DEBUG [main] - ==> Parameters: null, tagname3-7(String), This is tag by ExecutorType.Batch7(String)
DEBUG [main] - ==> Parameters: null, tagname3-8(String), This is tag by ExecutorType.Batch8(String)
DEBUG [main] - ==> Parameters: null, tagname3-9(String), This is tag by ExecutorType.Batch9(String)
DEBUG [main] - ==> Parameters: null, tagname3-10(String), This is tag by ExecutorType.Batch10(String)
共用时:560

综上,推荐使用第二种,即动态 sql 的 foreach 元素进行批量更新操作。

附:

当前版本:mybatis-3.5.0
官网文档:MyBatis
项目实践:MyBatis Learn
手写源码:MyBatis 简易实现

上一篇下一篇

猜你喜欢

热点阅读