Mybatis的执行器类型

  • Post author:
  • Post category:其他


MyBatis框架提供了三种不同的执行器(Executor)类型,用于执行SQL语句和映射语句:

1.SimpleExecutor(简单执行器)

这是默认的执行器类型,它每次执行都会创建一个Statement对象,并立即执行SQL语句。这种执行器不支持事务,每次都会关闭Statement对象,适用于简单的查询场景。

2.ReuseExecutor(重用执行器)

这种执行器重用预处理的Statement对象。它会缓存Statement对象在map中,key为sql,value为Statement对象,当需要执行相同的SQL语句时,会直接使用缓存的Statement对象,而不是每次都创建新的对象。这种执行器也不支持事务。

3.BatchExecutor(批处理执行器)

这种执行器用于批量操作,可以一次执行多个SQL语句。它会将相同类型的SQL语句分组,并使用JDBC的批处理功能执行。这种执行器可以提高性能,尤其适用于需要执行大量相同类型SQL语句的场景,如批量插入或更新操作。

执行器的设置方式

Mybatis 支持全局修改执行器,参数名为:defaultExecutorType,也可以在获取sqlSession对象时设置。

1、局部设置

在获取sqlSession时设置,需要注意的时,如果选择的是批量执行器时,需要手工提交事务。

// 获取指定执行器的sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)

// 获取批量执行器时, 需要手动提交事务
sqlSession.commit();

2、全局配置

可在全局配置文件中配置,不推荐。

<settings>
    <setting name="defaultExecutorType" value="BATCH" />
</settings>

三种执行器测试

对于单条sql执行,不同的执行器没有太大的差异,所以笔者使用批量插入操作来测试不同执行器的不同行为方式。需要特别注意的是,当选择批量执行器时,纵使在获取sqlSession时,设置了自动提交事务,也需要手动提交事务。

映射文件

为保证测试条件尽可能一致,笔者写里一个清空表的方法,在每个测试用例执行之前,先清空表。

<mapper namespace="org.zongf.learn.mybatis3.l01.mapper.StudentMapper">

    <!-- 清空表中数据, 同时重置自增序列从0开始 -->
    <delete id="clear">
        truncate table t_student
    </delete>

    <!-- 新增 -->
    <insert id="save" useGeneratedKeys="true" keyProperty="id">
        insert into t_student  values (null , #{name}, #{age}, #{sex}, #{birth})
    </insert>

</mapper>

测试用例

public class TestStudentMapper {

    // 批量保存方法
    private void batchSave(StudentMapper mapper) {

        // 初始化10000个对象
        List<StudentPO> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            list.add(new StudentPO("zhangsan_" + i, "M",20 + i % 10,  LocalDate.now()));
        }

        // 批量执行
        long start = System.currentTimeMillis();
        for (StudentPO studentPO : list) {
            mapper.save(studentPO);
        }
        long end = System.currentTimeMillis();

        // 输出执行耗时
        System.out.println("耗时:" + (end - start) + " ms!");
    }

    // 每次执行前,请空表
    @Before
    public void setUp(){
        StudentMapper studentMapper = SqlSessionUtil.getMapper(StudentMapper.class);
        studentMapper.clear();
    }

    // 默认执行器
    @Test
    public void test_SIMPLE(){
        // 获取自动提交事务的Maper
        StudentMapper mapper = SqlSessionUtil.getMapperAutoTx(StudentMapper.class);

        // 执行批量保存
        batchSave(mapper);
    }

    // 重用预编译执行器
    @Test
    public void test_REUSE(){
        // 获取批量保存sqlSession
        SqlSession sqlSession = SqlSessionUtil.openSession(ExecutorType.REUSE, true);

        // 获取Mapper 对象
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

        // 执行批量保存
        batchSave(mapper);

    }

    // 批量执行器
    @Test
    public void test_BATCH(){
        // 获取批量保存sqlSession
        SqlSession sqlSession = SqlSessionUtil.openSession(ExecutorType.BATCH, true);

        // 获取Mapper 对象
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

        // 执行批量保存
        batchSave(mapper);

        sqlSession.commit();
    }
}    

批量日志分析

从测试结果可以看出,在做批量操作时,使用批量执行器,性能会有很大的提升。


simple类型:

从执行日志可以看出,每次插入操作,都会执行编译,设置参数,执行sql操作。

[2019-06-13 11:30:38:812][main][DEBUG][o.z.l.m.l.mapper.StudentMapper.save]- ==>  Preparing: insert into t_student values (null , ?, ?, ?, ?) 
[2019-06-13 11:30:38:819][main][DEBUG][o.z.l.m.l.mapper.StudentMapper.save]- ==> Parameters: zhangsan_0(String), 20(Integer), M(String), 2019-06-13(Date)
[2019-06-13 11:30:38:824][main][DEBUG][o.z.l.m.l.mapper.StudentMapper.save]- <==    Updates: 1

[2019-06-13 11:30:38:827][main][DEBUG][o.z.l.m.l.mapper.StudentMapper.save]- ==>  Preparing: insert into t_student values (null , ?, ?, ?, ?) 
[2019-06-13 11:30:38:828][main][DEBUG][o.z.l.m.l.mapper.StudentMapper.save]- ==> Parameters: zhangsan_1(String), 21(Integer), M(String), 2019-06-13(Date)
[2019-06-13 11:30:38:832][main][DEBUG][o.z.l.m.l.mapper.StudentMapper.save]- <==    Updates: 1

[2019-06-13 11:30:38:833][main][DEBUG][o.z.l.m.l.mapper.StudentMapper.save]- ==>  Preparing: insert into t_student values (null , ?, ?, ?, ?) 
[2019-06-13 11:30:38:839][main][DEBUG][o.z.l.m.l.mapper.StudentMapper.save]- ==> Parameters: zhangsan_2(String), 22(Integer), M(String), 2019-06-13(Date)
[2019-06-13 11:30:38:841][main][DEBUG][o.z.l.m.l.mapper.StudentMapper.save]- <==    Updates: 1
...
耗时:21575 ms!


reuse方式:

从执行日志可以看出,只有第一次插入操作,执行了sql编译步骤,对其它插入操作执行了设置参数,执行sql的操作。

[2019-06-13 11:31:11:752][main][DEBUG][o.z.l.m.l.mapper.StudentMapper.save]- ==>  Preparing: insert into t_student values (null , ?, ?, ?, ?) 

[2019-06-13 11:31:11:757][main][DEBUG][o.z.l.m.l.mapper.StudentMapper.save]- ==> Parameters: zhangsan_0(String), 20(Integer), M(String), 2019-06-13(Date)
[2019-06-13 11:31:11:759][main][DEBUG][o.z.l.m.l.mapper.StudentMapper.save]- <==    Updates: 1

[2019-06-13 11:31:11:761][main][DEBUG][o.z.l.m.l.mapper.StudentMapper.save]- ==> Parameters: zhangsan_1(String), 21(Integer), M(String), 2019-06-13(Date)
[2019-06-13 11:31:11:764][main][DEBUG][o.z.l.m.l.mapper.StudentMapper.save]- <==    Updates: 1

[2019-06-13 11:31:11:776][main][DEBUG][o.z.l.m.l.mapper.StudentMapper.save]- ==> Parameters: zhangsan_2(String), 22(Integer), M(String), 2019-06-13(Date)
[2019-06-13 11:31:11:778][main][DEBUG][o.z.l.m.l.mapper.StudentMapper.save]- <==    Updates: 1
...
耗时:19322 ms!


batch方式:

从执行日志可以看出,只对第一次插入操作执行了sql编译操作,对其它插入操作仅执行了设置参数操作,最后统一执行。

[2019-06-13 11:31:29:270][main][DEBUG][o.z.l.m.l.mapper.StudentMapper.save]- ==>  Preparing: insert into t_student values (null , ?, ?, ?, ?) 
[2019-06-13 11:31:29:276][main][DEBUG][o.z.l.m.l.mapper.StudentMapper.save]- ==> Parameters: zhangsan_0(String), 20(Integer), M(String), 2019-06-13(Date)
[2019-06-13 11:31:29:277][main][DEBUG][o.z.l.m.l.mapper.StudentMapper.save]- ==> Parameters: zhangsan_1(String), 21(Integer), M(String), 2019-06-13(Date)
[2019-06-13 11:31:29:277][main][DEBUG][o.z.l.m.l.mapper.StudentMapper.save]- ==> Parameters: zhangsan_2(String), 22(Integer), M(String), 2019-06-13(Date)
...
耗时:835 ms!



版权声明:本文为qq_39350797原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。