目录

mybatis批量操作

批量操作介绍

默认的 openSession() 方法没有参数,它会创建有如下特性的

  • 会开启一个事务(也就是不自动提交)
  • 连接对象会从由活动环境配置的数据源实例得到。
  • 事务隔离级别将会使用驱动或数据源的默认设置。
  • 预处理语句不会被复用,也不会批量处理更新。

openSession 方法的 ExecutorType 类型的参数,枚举类型:

  • ExecutorType.SIMPLE: 这个执行器类型不做特殊的事情(这是默认装配的)。它为每个语句的执行创建一个新的预处理语句。
  • ExecutorType.REUSE: 这个执行器类型会复用预处理语句。
  • ExecutorType.BATCH: 这个执行器会批量执行所有更新语句
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public interface SqlSessionFactory {
  SqlSession openSession();
  SqlSession openSession(boolean autoCommit);
  SqlSession openSession(Connection connection);
  SqlSession openSession(TransactionIsolationLevel level);
  SqlSession openSession(ExecutorType execType);
  SqlSession openSession(ExecutorType execType, boolean autoCommit);
  SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
  SqlSession openSession(ExecutorType execType, Connection connection);
  Configuration getConfiguration();
}

批量操作我们是使用MyBatis提供的BatchExecutor进行的,它的底层就是通过jdbc攒sql的方式进行的。我们可以让他攒够一定数量后发给数据库一次。

示例程序

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Test
public void testBatch() throws IOException {
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    long start = System.currentTimeMillis();
    try (SqlSession session = sqlSessionFactory.openSession()
    ) {
        EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
        for (int i = 0; i < 10000; i++) {
            employeeMapper.insertEmployee(
                    new Employee(null, UUID.randomUUID().toString().substring(0, 5), "0", "abc@gmail.com", null)
            );
        }
        session.commit();
    }
    System.out.println("执行时长: " + (System.currentTimeMillis() - start));
}

注意:

  1. 批量操作是在session.commit()以后才发送sql语句给数据库进行执行的
  2. 如果我们想让其提前执行,以方便后续可能的查询操作获取数据,我们可以使用sqlSession.flushStatements()方法,让其直接冲刷到数据库进行执行。

运行结果:

1
执行时长: 65136

我们使用批量操作BatchExecutor

1
try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)

运行结果:

1
执行时长: 5091

对比:

  • 非批量

    预编译sql -> 设置参数 -> 数据库执行 各10000次 65秒

  • 批量

    预编译sql一次 -> 设置参数 10000次 -> 数据库执行(5秒)

与spring整合

与Spring整合中,我们推荐,额外的配置一个可以专门用来执行批量操作的SqlSession

1
2
3
4
5
<!--配置一个可以进行批量执行的sqlSession  -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
	<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"></constructor-arg>
	<constructor-arg name="executorType" value="BATCH"></constructor-arg>
</bean>

需要用到批量操作的时候,我们可以注入配置的这个批量SqlSession。通过他获取到mapper映射器进行操作。

1
2
3
4
5
6
@Service
public class EmployeeService {
	
	@Autowired
	private SqlSession sqlSession;
	...

注意:DefaultSqlSession是线程不安全的,SqlSessionTemplate是线程安全的

  • DefaultSqlSession的内部没有提供像SqlSessionManager一样通过ThreadLocal的方式来保证线程的安全性;

  • SqlSessionManager是通过localSqlSession这个ThreadLocal变量,记录与当前线程绑定的SqlSession对象,供当前线程循环使用,从而避免在同一个线程多次创建SqlSession对象造成的性能损耗;

  • mybatis-spring框架中没有直接使用线程安全的SqlSessionManager(SqlSessionFactory它是线程安全的)而是使用DefaultSqlSession这个线程不安全的类,并通过动态代理的方式来保证DefaultSqlSession操作的线程安全性

  • SqlSessionManager和SqlSessionTemplate的区别

    SqlSessionManager和SqlSessionTemplate都可以作SqlSession,并且在各自类里都对SqlSession做了动态代理。区别是SqlSessionTemplate的动态代理更高效(有SqlSession的引用计数),并且有对Spring框架的配合。动态代理的实现不容易复用,所以干脆分开,做到低耦合。