04-MyBatis执行器

MyBatis执行器(Executor)

当构建完成SqlSession后,就需要执行SQL语句,MyBatis中的Executor类是执行引擎的核心,是由它来完成跟数据库的交互。Executor类图如下:

从图中可以看出,Executor主要提供的方法如下:

  • query|update(insert和delete也是使用update)。
  • 事务提交/回滚,委派给Transaction对象来完成。
  • 缓存createCacheKey/isCached/clearLocalCache
  • 提交commit,回滚rollback
    重要方法参数上解释:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
*
* @param ms 存储了一个sql对应的所有信息
* @param parameter 参数
* @param rowBounds 分页参数
* @param resultHandler 结果处理
* @param boundSql sql信息
* @param <E>
* @return
* @throws SQLException
*/
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
Statement stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
}

执行器分类

  1. SimpleExecutor(简易执行器)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
} finally {
closeStatement(stmt);//关闭Statement
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
  1. ReuseExecutor(复用型执行器)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
Statement stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
if (hasStatementFor(sql)) {
stmt = getStatement(sql);
applyTransactionTimeout(stmt);
} else {
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
putStatement(sql, stmt);
}
handler.parameterize(stmt);
return stmt;
}

private boolean hasStatementFor(String sql) {
try {
return statementMap.keySet().contains(sql) && !statementMap.get(sql).getConnection().isClosed();
} catch (SQLException e) {
return false;
}
}
  1. BatchExecutor(批处理型执行器)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Override
public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
final Configuration configuration = ms.getConfiguration();
final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
final BoundSql boundSql = handler.getBoundSql();
final String sql = boundSql.getSql();
final Statement stmt;
if (sql.equals(currentSql) && ms.equals(currentStatement)) {
int last = statementList.size() - 1;
stmt = statementList.get(last);
applyTransactionTimeout(stmt);
handler.parameterize(stmt);//fix Issues 322
BatchResult batchResult = batchResultList.get(last);
batchResult.addParameterObject(parameterObject);
} else {
Connection connection = getConnection(ms.getStatementLog());
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt); //fix Issues 322
currentSql = sql;
currentStatement = ms;
statementList.add(stmt);
batchResultList.add(new BatchResult(ms, sql, parameterObject));
}
handler.batch(stmt);
return BATCH_UPDATE_RETURN_VALUE;
}

三种执行器执行对比:

doQuery()

环境准备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
SqlSessionFactory sqlSessionFactory;
MappedStatement ms;
BoundSql boundSql;
Configuration configuration;
Transaction transaction;
DataSource dataSource;
@Before
public void init() throws IOException, SQLException {
// 读取mybatis核心配置文件
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
// 创建会话工厂
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
sqlSessionFactory = sqlSessionFactoryBuilder.build(reader);

Configuration configuration = sqlSessionFactory.getConfiguration();
dataSource = configuration.getEnvironment().getDataSource();
transaction = configuration.getEnvironment().getTransactionFactory().newTransaction(dataSource.getConnection());
ms = sqlSessionFactory.getConfiguration().getMappedStatement("com.mybatis.mapper.UserMapper.updateUser");
boundSql = ms.getBoundSql(1);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
SqlSessionFactory sqlSessionFactory;
MappedStatement ms;
BoundSql boundSql;
Configuration configuration;
Transaction transaction;
DataSource dataSource;
@Before
public void init() throws IOException, SQLException {
// 读取mybatis核心配置文件
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
// 创建会话工厂
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
sqlSessionFactory = sqlSessionFactoryBuilder.build(reader);

Configuration configuration = sqlSessionFactory.getConfiguration();
dataSource = configuration.getEnvironment().getDataSource();
transaction = configuration.getEnvironment().getTransactionFactory().newTransaction(dataSource.getConnection());
ms = sqlSessionFactory.getConfiguration().getMappedStatement("com.mybatis.mapper.UserMapper.findStudentById");
boundSql = ms.getBoundSql(1);
}

SimpleExecutor

1
2
3
4
5
6
@Test
public void SimpleExecutorSelect() throws SQLException {
SimpleExecutor executor = new SimpleExecutor(configuration, transaction);
executor.doQuery(ms, 1, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER, boundSql);
executor.doQuery(ms, 1, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER, boundSql);
}
1
2
3
4
5
6
7
@Test
public void ReuseExecutorSelect() throws SQLException {
ReuseExecutor executor = new ReuseExecutor(configuration, transaction);
executor.doQuery(ms, 1, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER, boundSql);
executor.doQuery(ms, 1, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER, boundSql);

}
1
2
3
4
5
6
@Test
public void BatchExecutorSelect() throws SQLException {
BatchExecutor executor = new BatchExecutor(configuration, transaction);
executor.doQuery(ms, 1, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER, boundSql);
executor.doQuery(ms, 1, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER, boundSql);
}

doUpdate()

1
2
3
4
5
6
7
8
9
10
@Test
public void SimpleExecutorUpdate() throws SQLException {
SimpleExecutor executor = new SimpleExecutor(configuration, transaction);
User user = new User();
user.setId("10");
user.setUsername("2323");
executor.doUpdate(ms, user);
executor.doUpdate(ms, user);

}
1
2
3
4
5
6
7
8
9
10
@Test
public void ReuseExecutorUpdate() throws SQLException {
ReuseExecutor executor = new ReuseExecutor(configuration, transaction);
User user = new User();
user.setId("10");
user.setUsername("2323");
executor.doUpdate(ms, user);
executor.doUpdate(ms, user);

}
1
2
3
4
5
6
7
8
9
10
@Test
public void BatchExecutorUpdate() throws SQLException {
BatchExecutor executor = new BatchExecutor(configuration, transaction);
User user = new User();
user.setId("10");
user.setUsername("2323");
executor.doUpdate(ms, user);
executor.doUpdate(ms, user);
executor.commit(true);
}

结论:

SimpleExecutor

  • 每条SQL都要进行预处理。
  • 每次只执行一条SQL。

ReuseExecutor

  • 相同SQL只需要预处理一次。
  • 每次只执行一条SQL。

BatchExecutor

  • 每条SQL都要处理一次。
  • 多条SQL可以一次批量执行。
  • 最后一定要调用commit()方法,否则不生效。

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!