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
|
@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); }
|
执行器分类
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); } } 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; }
|
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; } }
|
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); BatchResult batchResult = batchResultList.get(last); batchResult.addParameterObject(parameterObject); } else { Connection connection = getConnection(ms.getStatementLog()); stmt = handler.prepare(connection, transaction.getTimeout()); handler.parameterize(stmt); 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 { 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 { 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()方法,否则不生效。