您的位置:首页 > 其它

mybatis源码分析(6)-----核心调度对象StatmentHandler

2017-04-09 12:41 465 查看

写在前面

  通过上一偏文章,我们知道:mybatis 的插件开发,主要是集中在Executor(执行器),ParameterHandler(参数处理器),ResultSetHandler(结果集处理器),StatementHandler( 语句处理器)。可以称作myBatis核心的四大金刚

  我们知道了mybatis对外暴露的API(SqlSession)的操作,其实是其持有Executor对于底层的操作。也知道了Executor 的设计采用了模板方法模式。

  查看Executor 的源代码发现,它对底层的操作,其实是其持有StatmentHandler对于 底层的操作。

public class SimpleExecutor extends BaseExecutor {
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
    //更新方法,首先获取StatmentHandler
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
stmt = prepareStatement(handler, ms.getStatementLog());
//对于update 方法,只会有parameterHandler 帮忙绑定预编译的sql的参数。①
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
    //查询方法,首先获取StatmentHandler
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
//对于query方法,parameterHandler 和 resultSetHandler 会前来帮绑定参数和结果集处理  ②
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
//statmentHandler 预编译sql
stmt = handler.prepare(connection);
//statmentHandler 绑定参数
handler.parameterize(stmt);
return stmt;
}
}


  首先说明本文重点:

  文中将的四大对象是指:executor, statementHandler,parameterHandler,resultSetHandler对象

  讲到statementHandler,毫无疑问它是我们四大对象最重要的一个,它的任务就是和数据库对话。在它这里会使用parameterHandler和ResultSetHandler对象为我们绑定SQL参数和组装最后的结果返回。

StatmentHandler 接口

  statmentHandler 位于mybatis的org.apache.ibatis.executor.statement 包下面。也可以发现其和executor的关系

public interface StatementHandler {
//预编译
Statement prepare(Connection connection) throws SQLException;
//绑定参数
void parameterize(Statement statement) throws SQLException;
//批量操作
void batch(Statement statement)  throws SQLException;
//更新操作
int update(Statement statement) throws SQLException;
//查询操作
<E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;

BoundSql getBoundSql();
ParameterHandler getParameterHandler();
}


通过对stantmentHandler 接口的结构分析。它和executor的设计几乎一模一样,我可以猜测statmentHandler 也是采用了模板方法模式的设计。



StatmentHandler 的产生

  通过对于Executor 的分析,可以发现executor 产生了一个RoutingStatmentHandler (从名字中可以发现,这是一个路由)

  这里有几个重要的方法,prepare,parameterize和query,update,他们的作用是不一样的。

  在MyBatis实现了statementHandler的有四个类:

  RoutingStatementHandler,这是一个封装类,它不提供具体的实现,只是根据Executor的类型,创建不同的类型StatementHandler。

  SimpleStatementHandler,这个类对应于JDBC的Statement对象,用于没有预编译参数的SQL的运行。

  PreparedStatementHandler 这个用于预编译参数SQL的运行。

  CallableStatementHandler 它将实存储过程的调度。

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//statmentHandler 的产生
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}


  根据不同的statatmentType 产生具体的statmentHandler实例

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());
}
}


  在MyBatis中,Configuration对象会采用new RoutingStatementHandler()来生成StatementHandler对象,换句话说我们真正使用的是RoutingStatementHandler对象,然后它会根据Executor的类型去创建对应具体的statementHandler对象(SimpleStatementHandler,PreparedStatementHandler和CallableStatementHandler)。

  然后利用具体statementHandler的方法完成所需要的功能。那么这个具体的statementHandler是保存在RoutingStatementHandler对象的delegate属性的,所以当我们拦截statementHandler的时候就要常常访问它了。

statmentHandler 的执行

  prepare方法:首先prepare方法是用来编译SQL的,让我们看看它的源码实现。这里我们看到了BaseStatementHandler对prepare方法的实现,

  parameterize方法:上面我们在prepare方法里面预编译了SQL。那么我们这个时候希望设置参数。在Statement中我们是使用parameterize方法进行设置参数的。

  让我们看看PreparedStatementHandler中的parameterize方法:

@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}


  很显然这里很简单是通过parameterHandler来实现的,我们这篇文章只是停留在statementhandler的程度,等我们讲解parameterHandler的时候再来看它如何实现吧,期待一下吧。

  也就是不管是执行,query和update。parameterHandler 都会前来帮忙,帮助参数的绑定。参见①

对于执行query方法。ResultSetHandler才会前来帮忙,对结果集做处理。参见②

  query/update方法

  我们用了prepare方法预编译了SQL,用了parameterize方法设置参数,那么我们接下来肯定是想执行SQL,而SQL无非是两种:
    一种是进行查询——query,另外就是更新——update。这些方法都很简单,让我们看看PreparedStatementHandler的实现:

@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;
}

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}


  我们可以看到如果是进行update的,它将会执行生成主键的操作(插入数据要自动生成主键的时候),然后就返回影响行数。
  如果是进行query的就更加简单了,它就是执行SQL语句,然后讲结果使用resultSetHandler去完成我们的结果组装。

    

parameterhandler 和 ResultSetHandler

  parameterhandler 和 ResultSetHandler好比StatmentHandler 的左膀右臂。先后完成了参数绑定设置,已经结果集设置。他们的默认实现了是DefaultParameterHandler和DefaultResultSetHandler

  在构造statmentHandler 实例的时候,会同时产生parameterhandler 和 ResultSetHandler实例。并持有他们的对象。

public abstract class BaseStatementHandler implements StatementHandler {

protected final Configuration configuration;
protected final ObjectFactory objectFactory;
protected final TypeHandlerRegistry typeHandlerRegistry;
protected final ResultSetHandler resultSetHandler;
protected final ParameterHandler parameterHandler;

protected final Executor executor;
protected final MappedStatement mappedStatement;
protected final RowBounds rowBounds;

protected BoundSql boundSql;

protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;

this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory();

if (boundSql == null) { // issue #435, get the key before calculating the statement
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
}

this.boundSql = boundSql;

this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
}


ParameterHandler 的作用  

  我们知道ParameterHandler是用来设置参数规则的。档StatementHandler使用prepare()方法后,接下来就是使用它来设置参数,让我们看看它的定义:

public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement ps) throws SQLException;
}


  十分简单getParameterObject()是获取参数的,而setParameters()是设置参数的,相当于对一条sql所有的参数都执行ps.setXXX(value);

  让我们看看它的实现类:DefaultParameterHandler中的方法实现

@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
} catch (SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}


这里重点是setParameters(),首先它读取了ParameterObject参数对象,然后用typeHandler对参数进行设置,而typeHandler里面需要对jdbcType和javaType进行处理,然后就设置参数了。也很好理解。所以当我们使用TypeHandler的时候完全可以控制如何设置SQL参数。

ResultSetHandler

  ResultSetHandler要比ParameterHandler复杂得多,对其原理要讲清楚真的很困难,但是我们一般在插件里面使用不多。所以我就不分析太详细了,大致讲讲它的大概原理。让我们看看ResultSetHandler的接口定义:

public interface ResultSetHandler {
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
void handleOutputParameters(CallableStatement cs) throws SQLException;

}


总结

  通过上面几篇我们懂得了SqlSession下得【四大对象】Executor,StatementHandler,ParameterHandler 和 ResultSetHandler之间的主要方法和协作。

  因为掌握好四大对象的基本方法,了解他们的作用是我们插件编写的根本,没有掌握它们你是没有办法准确的去编写你的插件的。

  StatementHandler是MyBatis四大对象里面最重要的对象,它的方法是十分重要的,也是我们插件的基础。

  当我们需要改变sql的时候,显然我们要在预编译SQL(prepare方法前加入修改的逻辑)。
  当我们需要修改参数的时候我们可以在调用parameterize方法前修改逻辑。或者使用ParameterHandler来改造设置参数。
  我们需要控制组装结果集的时候,也可以在query方法前后加入逻辑,或者使用ResultSetHandler来改造组装结果。
  懂的这些方法,才能理解我需要拦截什么对象,如何处理插件,这是MyBatis的核心内容。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: