美文网首页
MyBatis 最佳实践篇 2:批量更新

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

作者: 兆雪儿 | 来源:发表于2019-04-11 16:40 被阅读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 循环,这种方式的执行时间明显少很多(当然最合理的校验对比方式是使用更多的循环次数)。

    • 优点:性能高,一个SQL一次性发送到数据库执行,与数据库只进行一次 IO,推荐使用。
    • 缺点:因为数据库的限制,有 sql 的长度限制,所以使用时要注意限制批量执行的长度。
      在 MySQL 中执行可以看到其对应的长度限制:
    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一次性发送到数据库执行和提交。
    • 缺点:在 openSession 时我们要关闭自动提交功能(即 openSession(ExecutorType.BATCH,false)),在代码中手动提交。

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

    附:

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

    相关文章

      网友评论

          本文标题:MyBatis 最佳实践篇 2:批量更新

          本文链接:https://www.haomeiwen.com/subject/afptwqtx.html