1. SqlSession四大对象之Executor
1.1 Executor简介
每一个SqlSession对象都被分配一个Executor,主要负责connection获取和statement对象管理方案。
1.2 Statement对象给管理方案
- 简单管理方案:一个Statement接口对象只执行一次。执行完毕就会Statement接口对象进行销毁。
- 可重用方案: 使用一个Map集合,关键字就是一条Sql语句。对应内容Statement接口对象,等到SqlSession再次接收到相同命令时,就从map集合找到对应Statement接口使用map.put("select * from order", Statement1)。
- 批处理管理方案:将多个Statement包含的SQL语句,交给一个Statement对象输送到数据库,形成批处理操作。
1.3 Executor继承结构
- Excutor主要有两个实现类:
BaseExcutor:是一个抽象类,这种通过抽象类实现接口的方式是适配器模式的体现,主要用于方便一级子类对接口中方法的实现。
CachingExcutor(缓存执行器):提高效率,MyBatis框架默认到缓存中寻找对应的数据,如果存在就返回,不存在,再委托给其他执行器。
@Override
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
- ReuseExecutor(可重用执行器):这里指的可重用是statement,它会在内部利用一个Map把创建的Statement都缓存起来,每次执行一条SQL语句时,它都会去判断之前是否存在基于该SQL缓存的Statement对象,存在,而且之前缓存的Statement对象对应的connection还没有关闭的时候就会使用之前Statement对象,否则将创建个新的Statement对象,并将其缓存起来。因为每一个新的sqlSession都有一个新的Executor对象,所以我们缓存在ReuseExecutor行的Statement的作用域是同一个sqlSession。
private boolean hasStatementFor(String sql) {
try {
return statementMap.keySet().contains(sql) && !statementMap.get(sql).getConnection().isClosed();
} catch (SQLException e) {
return false;
}
}
private Statement getStatement(String s) {
return statementMap.get(s);
}
private void putStatement(String sql, Statement stmt) {
statementMap.put(sql, stmt);
}
- BatchExecutor(批处理执行器):用于将多个sql语句一次性输送到数据库执行。(将多个statement中的sql语句存入到一个statement中)
@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));
}
- SimpleExecutor(简单执行器):是MyBatis中默认的执行器,每执行一次update或select ,就开启一个statement对象,用完立刻关闭statement对象。
@Override
public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
1.4 Executor对象的创建
执行器对象由Coniguration对象负责创建(SqlSessionFactory),Coniguration对象会根据得到【ExecutorType】创建对应的Excecutor对象,并把这个Excecutor对象传给SqlSession对象。
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
继续往newExecutor里面看一下,会根据选择器的类型来配合cacheEnabled来配合使用。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
//这个是代理对象 这个是给开发人员做插件开发用的
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
1.5 ExcecutorType选择
ExecutorType来决定Configuration对象创建何种类型的执行器。它的赋值可以通过两个地方进行赋值:
- 通过
标签来设置当前工程中所有SqlSession对象使用的默认Executour
- 通过SqlSessionFactory中openSession方法来指定具体的SqlSession使用的执行器
@Before
public void start() throws IOException{
InputStream is = Resources.getResourceAsStream("myBatis-config.xml");
SqlSessionFactoryBuilder builderObj = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builderObj.build(is);
factory.openSession(ExecutorType.BATCH);
session = factory.openSession();
}
2. SqlSession四大对象之StatementHandler
2.1 StatementHandler介绍
是四大神器中最重要的一个对象,负责操作Statement与数据库进行交流。在工作时还会使用ParameterHandler进行参数配置,使用ResultHandler将查询结果与实体类对象进行绑定。
在StatementHandler接口中有四种重要 方法,分别是:
//创建Statement对象或则PreparedStatment或则CallableStatment
Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;
//主要针对PreparedStatment或则CallableStatment关联预编译SQL语句中占位符进行修改
void parameterize(Statement statement)
throws SQLException;
//用于通知Statement将[insert,update,delete]推送到数据库
int update(Statement statement)
throws SQLException;
//用于通知Statement将[select]推送到数据库并返回对应查询结果
List query(Statement statement, ResultHandler resultHandler)
throws SQLException;
2.2 StatementHandler继承结构
- 从上图可以看到StatementHandler接口下有两个直接实现类:
RoutingStatementHandler:是一个具体实现类,这个类中并没有对Statement对象具体使用,还是根据得到Excutor类型,决定创建何种类型StatementHandler对象。在MyBatis工作时,使用的StatementHandler接口对象实际上就是RoutingStatementHandler对象。
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
BaseStatementHandler:是个抽象类,用于简化StatementHandler接口开发的难度,是适配器模式的体现,他有三个实现类:SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler。在RoutingStatementHandler创建时,就根据接受的Excutor来创建这三个类型对象。
- SimpleStatementHandler:管理statement对象向数据库中推送不需要预编译SQL语句。
- PreparedStatementHandler:管理PreparedStatementHandler对象向数据库中推送预编译的SQL语句。
- CallableStatementHandler:管理CallableStatementHandler调用数据库中的构造函数。
2.3 StatementHandler对象的创建
StatementHandler对象是在SqlSession对象接收到操作命令时,由Configuraion中newStatementHandler方法负责调用的。
RoutingStatementHandler构造方法,将会根据Executor的类型决定创建SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler实例对象。
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
2.3 StatementHandler接口介绍
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
statement = instantiateStatement(connection);
setStatementTimeout(statement, transactionTimeout);
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
- prepare方法:
- prepare方法用于创建一个(Statement、PreparedStatement 、CallableStatement)对象,并设置statement对象最大等待时间和一次性读取的最大数据量,然后将生成statement对象返回。
- prepare方法只在BaseStatementHandler被实现,再其三个子类中没有被重写,用于三个子类调用获得对应的statement接口对象。
- prepare方法依靠instantiateStatement(connection)方法来实现返回具体的statement接口对象,这个方法是BaseStatementHandler中定义的抽象方法,由三个子类来具体实现。
CallableStatementHandler方法中instantiateStatement(Connection connection)方法的实现
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
if (mappedStatement.getResultSetType() != null) {
return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.createStatement();
}
}
CallableStatementHandler
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() != null) {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.prepareStatement(sql);
}
}
CallableStatementHandler
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getResultSetType() != null) {
return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.prepareCall(sql);
}
}
- parameterize方法
主要为PreparedStatement和CallableStatement传参,因此只在这两个对象中被重写。
PreparedStatementHandler中的parameterize。
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
CallableStatementHandler中的parameterize.
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
在这两个方法中,可以看到都是ParameterHandler对象进行参数传参的。
- query方法
输送查询语句,并将查询语句结果转换成对应的实体类。
SimpleStatementHandler 中的 query 方法。
@Override
public List query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
return resultSetHandler.handleResultSets(statement);
}
PreparedStatementHandler中的query方法
@Override
public List query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler. handleResultSets(ps);
}
CallableStatementHandler中的query方法
@Override
public List query(Statement statement, ResultHandler resultHandler) throws SQLException {
CallableStatement cs = (CallableStatement) statement;
cs.execute();
List resultList = resultSetHandler.handleResultSets(cs);
resultSetHandler.handleOutputParameters(cs);
return resultList;
}
可以看到在得到查询结果后,都是使用ResultSetHandler对结果进行转换.
- update方法
输送insert、update、delete语句并返回处理数据行
SimpleStatementHandler中的update方法
@Override
public int update(Statement statement) throws SQLException {
String sql = boundSql.getSql();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
int rows;
if (keyGenerator instanceof Jdbc3KeyGenerator) {
statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
rows = statement.getUpdateCount();
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else if (keyGenerator instanceof SelectKeyGenerator) {
statement.execute(sql);
rows = statement.getUpdateCount();
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else {
statement.execute(sql);
rows = statement.getUpdateCount();
}
return rows;
}
PreparedStatementHandler中update方法
@Override
public int update(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
int rows = ps.getUpdateCount();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
return rows;
}
CallableStatementHandler中update方法
@Override
public int update(Statement statement) throws SQLException {
CallableStatement cs = (CallableStatement) statement;
cs.execute();
int rows = cs.getUpdateCount();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
keyGenerator.processAfter(executor, mappedStatement, cs, parameterObject);
resultSetHandler.handleOutputParameters(cs);
return rows;
}
2.4 关于三种数据库操作对象选择(不需要人为控制)
执行相关SQL语句:
情况1: select * from order; SimpleStatementHander Statement
情况2: select * from order where orderId=? PreparedStatementHandler PreparedStatement
情况3: call{pro} CallableStatementHandler CallableStatment