转载自 mybatis源码阅读(六) ---StatementHandler了解一下
StatementHandler类结构图与接口设计
BaseStatementHandler:一个抽象类,只是实现了一些不涉及具体操作的方法
RoutingStatementHandler:类似路由器,根据配置文件来路由选择具体实现类SimpleStatementHandler、CallableStatementHandler和PreparedStatementHandler
SimpleStatementHandler:就是直接使用普通的Statement对象,这样每次执行SQL语句都需要数据库对SQL进行预编译
PrepareStatementHandler:使用PrepareStatement执行,虽然初次创建PrepareStatement时开销比较大,但在多次处理SQL时只需要初始化一次,可以有效提高性能
CallableStatementHandler:使用CallableStatement执行,CallableStatement是用来执行存储过程的。
在每个mapper节点可以设置statementType决定是否使用谁 ,如下
<!-- statementType (可选配置,默认配置为PREPARED)STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 -->
<select id="findUserById" resultType="com.lpf.entity.User" statementType="PREPARED">select * from m_user where id = #{id}
</select>
1. StatementHandler
public interface StatementHandler {// 从连接中获取一个Statement对象Statement prepare(Connection connection, Integer transactionTimeout)throws SQLException;// 绑定Statement执行是所需要的参数void parameterize(Statement statement)throws SQLException;// 批量执行SQLvoid batch(Statement statement)throws SQLException;// 执行update,delete,insert语句int update(Statement statement)throws SQLException;// 执行select语句<E> List<E> query(Statement statement, ResultHandler resultHandler)throws SQLException;<E> Cursor<E> queryCursor(Statement statement)throws SQLException;BoundSql getBoundSql();// 获取封装的ParameterterHandler对象ParameterHandler getParameterHandler();}
2. BaseStatementHandler
BaseStatementHandler是一个抽象类,和Executor一样使用了模板设计方法。代码如下:
public abstract class BaseStatementHandler implements StatementHandler {protected final Configuration configuration;protected final ObjectFactory objectFactory;protected final TypeHandlerRegistry typeHandlerRegistry;//将结果映射成结果对象protected final ResultSetHandler resultSetHandler;// 使用传入的实参替换SQL语句中的?protected final ParameterHandler parameterHandler;// 记录执行sql的executorprotected 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);}@Overridepublic BoundSql getBoundSql() {return boundSql;}@Overridepublic ParameterHandler getParameterHandler() {return parameterHandler;}@Overridepublic Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {ErrorContext.instance().sql(boundSql.getSql());Statement statement = null;try {// 初始化Statement对象 instantiateStatement由具体的子类对象实现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);}}protected abstract Statement instantiateStatement(Connection connection) throws SQLException;protected void setStatementTimeout(Statement stmt, Integer transactionTimeout) throws SQLException {Integer queryTimeout = null;if (mappedStatement.getTimeout() != null) {queryTimeout = mappedStatement.getTimeout();} else if (configuration.getDefaultStatementTimeout() != null) {queryTimeout = configuration.getDefaultStatementTimeout();}if (queryTimeout != null) {stmt.setQueryTimeout(queryTimeout);}StatementUtil.applyTransactionTimeout(stmt, queryTimeout, transactionTimeout);}protected void setFetchSize(Statement stmt) throws SQLException {Integer fetchSize = mappedStatement.getFetchSize();if (fetchSize != null) {stmt.setFetchSize(fetchSize);return;}Integer defaultFetchSize = configuration.getDefaultFetchSize();if (defaultFetchSize != null) {stmt.setFetchSize(defaultFetchSize);}}protected void closeStatement(Statement statement) {try {if (statement != null) {statement.close();}} catch (SQLException e) {//ignore}}protected void generateKeys(Object parameter) {KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();ErrorContext.instance().store();keyGenerator.processBefore(executor, mappedStatement, null, parameter);ErrorContext.instance().recall();}}
2.1 ParameterHandler
在BaseStatementHandler中有一个对象叫ParameterHandler是用来设置参数规则的,当StatementHandler调用prepare方法之后,接下来就是调用它来进行设置参数。
public interface ParameterHandler {Object getParameterObject();void setParameters(PreparedStatement ps)throws SQLException;}
getParameterObject是用来获取参数的,setParameters(PreparedStatement ps)是用来设置参数的,相当于对sql中所有的参数都执行ps.setXXX(value);
ParameterHandler的默认实现类是DefaultParameterHandler,其实现了接口中定义的两个方法。
getParameterObject是获取参数,这个参数值就是你传递进来的值,可能是个实体、map或单个基本类型数据。
@Override
public void setParameters(PreparedStatement ps) {ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());// 去除SQL中的参数映射列表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 {// 获取对象中相应的属性值或查找map对象中的值MetaObject metaObject = configuration.newMetaObject(parameterObject);value = metaObject.getValue(propertyName);}//获取参数值对应的jdbc类型TypeHandler typeHandler = parameterMapping.getTypeHandler();JdbcType jdbcType = parameterMapping.getJdbcType();if (value == null && jdbcType == null) {jdbcType = configuration.getJdbcTypeForNull();}try {//设置参数值和jdbc类型的对应关系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);}}}}
}
3. RoutingStatementHandler
/*** RoutingStatementHandler的主要功能就是根据mapper文件中的Statement的配置,生成一个对应的StatementHandler* 可以是 SimpleStatementHandler,PreparedStatementHandler,CallableStatementHandler* 此类中的所有方法都是通过调用delegate对象的对应方法实现的*/
public class RoutingStatementHandler implements StatementHandler {private final StatementHandler delegate;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());}}
}
4. SimpleStatementHandler
SimpleStatementHandler就是使用基本的Statement来执行query、batch、update等操作,其实现还是比较简单的,SQL语句中是没有占位符的,所以相应 的paramterize()方法是空实现。
public class SimpleStatementHandler extends BaseStatementHandler {public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);}@Overridepublic 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;}@Overridepublic void batch(Statement statement) throws SQLException {String sql = boundSql.getSql();statement.addBatch(sql);}@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {String sql = boundSql.getSql();statement.execute(sql);return resultSetHandler.<E>handleResultSets(statement);}@Overridepublic <E> Cursor<E> queryCursor(Statement statement) throws SQLException {String sql = boundSql.getSql();statement.execute(sql);return resultSetHandler.<E>handleCursorResultSets(statement);}@Overrideprotected Statement instantiateStatement(Connection connection) throws SQLException {if (mappedStatement.getResultSetType() != null) {return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);} else {return connection.createStatement();}}@Overridepublic void parameterize(Statement statement) throws SQLException {// N/A}}
5. PreparedStatementHandler
PreparedStatementHandler底层依赖PreparedStatement对象来完成数据库的相关操作,在调用parameterize()方法完成SQL语句的参数绑定,代码也比较简单。
public class PreparedStatementHandler extends BaseStatementHandler {public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);}@Overridepublic 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;}@Overridepublic void batch(Statement statement) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;ps.addBatch();}@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;ps.execute();return resultSetHandler.<E> handleResultSets(ps);}@Overridepublic <E> Cursor<E> queryCursor(Statement statement) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;ps.execute();return resultSetHandler.<E> handleCursorResultSets(ps);}@Overrideprotected 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);}}@Overridepublic void parameterize(Statement statement) throws SQLException {parameterHandler.setParameters((PreparedStatement) statement);}}
6. CallableStatementHandler
CallableStatementHandler实际就是使用CallableStatement来执行SQL语句,当然它执行的是存储过程。
public class CallableStatementHandler extends BaseStatementHandler {public CallableStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);}@Overridepublic 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;}@Overridepublic void batch(Statement statement) throws SQLException {CallableStatement cs = (CallableStatement) statement;cs.addBatch();}@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {CallableStatement cs = (CallableStatement) statement;cs.execute();List<E> resultList = resultSetHandler.<E>handleResultSets(cs);resultSetHandler.handleOutputParameters(cs);return resultList;}@Overridepublic <E> Cursor<E> queryCursor(Statement statement) throws SQLException {CallableStatement cs = (CallableStatement) statement;cs.execute();Cursor<E> resultList = resultSetHandler.<E>handleCursorResultSets(cs);resultSetHandler.handleOutputParameters(cs);return resultList;}@Overrideprotected 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);}}@Overridepublic void parameterize(Statement statement) throws SQLException {//注册out参数registerOutputParameters((CallableStatement) statement);parameterHandler.setParameters((CallableStatement) statement);}private void registerOutputParameters(CallableStatement cs) throws SQLException {List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();for (int i = 0, n = parameterMappings.size(); i < n; i++) {ParameterMapping parameterMapping = parameterMappings.get(i);//处理存储过程的INOUT和OUT if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT) {if (null == parameterMapping.getJdbcType()) {throw new ExecutorException("The JDBC Type must be specified for output parameter. Parameter: " + parameterMapping.getProperty());} else {if (parameterMapping.getNumericScale() != null && (parameterMapping.getJdbcType() == JdbcType.NUMERIC || parameterMapping.getJdbcType() == JdbcType.DECIMAL)) {cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE, parameterMapping.getNumericScale());} else {if (parameterMapping.getJdbcTypeName() == null) {cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE);} else {cs.registerOutParameter(i + 1, parameterMapping.getJdbcType().TYPE_CODE, parameterMapping.getJdbcTypeName());}}}}}}}