一、简介
StatementHandler是Mybatis最核心的接口之一, 他完成了Mybatis和数据库交互的相关工作(Mybatis与JDBC之间关于Statement的交互工作)。StatementHandler主要作用:
- 创建Statement对象
- 为SQL 语句绑定实参
- 执行select、insert 、update 、delete 等多种类型的SQL语句
- 批量执行SQL语句
- 结果集映射成结果对象
StatementHandler接口定义的方法如下:
public interface StatementHandler {
/**
* 从连接中获取一个Statement
* @param connection
* @param transactionTimeout
* @return
* @throws SQLException
*/
Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;
/**
* 绑定statement执行时所需的实参
* @param statement
* @throws SQLException
*/
void parameterize(Statement statement)
throws SQLException;
/**
* 批量执行SQL语句
* @param statement
* @throws SQLException
*/
void batch(Statement statement)
throws SQLException;
/**
* 执行update/insert/delete语句
* @param statement
* @return
* @throws SQLException
*/
int update(Statement statement)
throws SQLException;
/**
* 执行select语句
* @param <E>
* @param statement
* @param resultHandler
* @return
* @throws SQLException
*/
<E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException;
/**
* 执行select语句
* @param <E>
* @param statement
* @return
* @throws SQLException
*/
<E> Cursor<E> queryCursor(Statement statement)
throws SQLException;
/**
* 获取记录SQL语句对应的BoundSql对象
* @return
*/
BoundSql getBoundSql();
/**
* 获取使用的ParameterHandler对象
* @return
*/
ParameterHandler getParameterHandler();
}
二、StatementHandler接口及其实现类
在Mybatis中,StatementHandler接口提供了一个抽象类BaseStatementHandler,一个直接实现类RoutingStatementHandler,而抽象类BaseStatementHandler又有三个实现子类,分别是:SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler等。每个实现类的作用如下:
- BaseStatementHandler 抽象类,主要实现了公共的方法
- SimpleStatementHandler 实现类,主要对应JDBC中的Statement接口,用于简单SQL的处理;
- PreparedStatementHandler 实现类,主要对应JDBC中的PreparedStatement,处理预编译SQL的接口;
- CallableStatementHandler 实现类,主要对应JDBC中CallableStatement,用于执行存储过程相关的接口;
- RoutingStatementHandler 直接实现类,上述三个实现类的路由类,没有实际操作,只是负责上面三个StatementHandler的创建及调用。
StatementHandler接口的类图:
三、实现类RoutingStatementHandler
RoutingStatementHandler在StatementHandler的结构体系中,扮演者路由器的角色。
RoutingStatementHandler会根据MappedStatement中指定的statementType字段,创建对应
的StatementHandler接口实现。具体实现如下:
public class RoutingStatementHandler implements StatementHandler {
/**
* 真正StatementHandler的实例对象
*/
private final StatementHandler delegate;
/**
* 根据StatementType类型,选择创建真正的StatementHandler实例,并赋值给变量delegate
* @param executor
* @param ms
* @param parameter
* @param rowBounds
* @param resultHandler
* @param boundSql
*/
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());
}
}
//下面的方法都通过真正的StatementHandler对象实现
……
}
四、抽象类BaseStatementHandler
抽象类BaseStatementHandler是SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler三个实现类的父类,主要实现了一些公共的方法。
1、变量
在BaseStatementHandler中定义的变量及其含义,如下所示:
//BaseStatementHandler.java
/**
* 全局变量,Mybatis的核心配置对象
*/
protected final Configuration configuration;
/**
* 工具类,用来创建对象,通过configuration获取该实例对象。
*/
protected final ObjectFactory objectFactory;
/**
* 类型处理器的注册器,用来获取符合条件的类型处理器,通过configuration获取该实例对象。
*/
protected final TypeHandlerRegistry typeHandlerRegistry;
/**
* 记录使用的ResultSetHandler对象, 它的主要功能是将结果集映射成结果对象
*/
protected final ResultSetHandler resultSetHandler;
/**
* 记录使用的ParameterHandler对象, ParameterHandler的主要功能是为SQL语句绑定实参,也就是
* 使用传入的实参替换SQL语句的中“?”占位符
*/
protected final ParameterHandler parameterHandler;
/**
* 记录执行SQL语句的Executor对象
*/
protected final Executor executor;
/**
* 记录SQL语句对应的MappedStatement对象
*/
protected final MappedStatement mappedStatement;
/**
* RowBounds记录了用户设置的offset和limit ,用于在结果集中定位映射的起始位置和结束位置
*/
protected final RowBounds rowBounds;
/**
* 记录SQL语句对应的BoundSql对象
*/
protected BoundSql boundSql;
2、构造函数
在构造函数中,除了通过构造函数的参数进行初始的字段外,还有通过全局变量configuration初始化的参数,比如typeHandlerRegistry和objectFactory是在配置文件初始化的时候就解析并存储到全局变量configuration中的,还有parameterHandler变量是通过configuration的newParameterHandler()方法完成了初始化,resultSetHandler变量是通过configuration的newResultSetHandler()方法完成了初始化。除此之外,当boundSql参数为空时,首先通过generateKeys()方法,实现初始化SQL的主键,然后再根据mappedStatement.getBoundSql()方法,获取对应的boundSql实例。
//BaseStatementHandler.java
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
//调用KeyGenerator.processBefore()方法初始化SQL语句的主键
generateKeys(parameterObject);
//根据参数,初始化boundSql变量
boundSql = mappedStatement.getBoundSql(parameterObject);
}
this.boundSql = boundSql;
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
3、prepare()方法
prepare()方法用来实现根据Connection连接获取对应的Statement实例对象。在该方法中,首先通过调用instantiateStatement()抽象方法初始化Statement对象,然后再通过setStatementTimeout()设置超时时间,
setFetchSize()方法设置fetchSize等参数。其中,instantiateStatement()抽象方法,是由BaseStatementHandler抽象定义的,并交由子类实现的,这样初始化Statement对象的操作就可以各个子类根据自身要求进行创建,而设置参数相关通用配置,由抽象类(父类)统一完成。
//BaseStatementHandler.java
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
//调用instantiateStatement()抽象方法初始化java.sql.Statement对象
statement = instantiateStatement(connection);
//设置超时时间
setStatementTimeout(statement, transactionTimeout);
//设置fetchSize参数等
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);
}
}
//BaseStatementHandler.java
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);
}
//BaseStatementHandler.java
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);
}
}
五、实现类SimpleStatementHandler
实现类SimpleStatementHandler主要对应JDBC中的Statement接口,用于简单SQL的处理。
- parameterize()方法
空方法,没有做任何处理。 - instantiateStatement()方法
在该方法中,通过connection的createStatement()方法实现了Statement对象的实例化。
//SimpleStatementHandler.java
@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();
}
}
- query()、queryCursor()方法
query()、queryCursor()两个方法,逻辑相似。都是首先获取需要执行的SQL语句,然后通过statement.execute()方法执行SQL,最后再通过resultSetHandler的handleResultSets()方法或handleCursorResultSets()方法处理结果集。
//SimpleStatementHandler.java
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
//调用Statement.executor()方法执行SQL语句
statement.execute(sql);
//将结果集映射成结果对象
return resultSetHandler.<E>handleResultSets(statement);
}
@Override
public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
return resultSetHandler.<E>handleCursorResultSets(statement);
}
- batch()方法
//SimpleStatementHandler.java
@Override
public void batch(Statement statement) throws SQLException {
String sql = boundSql.getSql();
statement.addBatch(sql);
}
- update()方法
该方法主要用来完成insert、update、delete等SQL语句的执行。该方法需要处理生成主键的相关逻辑,首先statement.execute()方法,然后根据keyGenerator的类型,执行对应的processAfter()方法。
//SimpleStatementHandler.java
@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.executor()方法执行SQL语句
statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
rows = statement.getUpdateCount();
//将数据库生成的主键添加到parameterObject中
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else if (keyGenerator instanceof SelectKeyGenerator) {
statement.execute(sql);
rows = statement.getUpdateCount();
//执行<selectKey>节点中配置的SQL语句获取数据库生成的主键,并添加到parameterObject中
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else {
statement.execute(sql);
rows = statement.getUpdateCount();
}
return rows;
}
六、实现类PreparedStatementHandler
PreparedStatementHandler实现类,主要对应JDBC中的PreparedStatement,处理预编译SQL。
- parameterize()方法
通过ParameterHandler.setParameters()方法完成SQL语句的参数绑定。
//PreparedStatementHandler.java
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
- instantiateStatement()方法
直接调用JDBC Connection的prepareStatement()方法,创建PreparedStatement对象。当mappedStatement.getKeyGenerator()返回的类型为Jdbc3KeyGenerator时,根据keyColumnNames包含的字段来初始化PreparedStatement对象。
//PreparedStatementHandler.java
@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 {
//在insert语句执行完成之后,会将keyColumnNames指定的列返回
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);
}
}
- query()、queryCursor()方法
query()、queryCursor()两个方法,逻辑相似。都是首先把参数statement转换成PreparedStatement类型,然后执行ps.execute()方法,最后再通过resultSetHandler的handleResultSets()方法或handleCursorResultSets()方法处理结果集。
//PreparedStatementHandler.java
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleResultSets(ps);
}
@Override
public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.<E> handleCursorResultSets(ps);
}
- batch()方法
//PreparedStatementHandler.java
@Override
public void batch(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.addBatch();
}
- update()方法
该方法主要用来完成insert、update、delete等SQL语句的执行。首先把statement类型强转成PreparedStatement,然后执行ps.execute()方法,然后根据参数,执行keyGenerator.processAfter()处理生成主键的相关逻辑。
//PreparedStatementHandler.java
@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
CallableStatementHandler 实现类,主要对应JDBC中CallableStatement,用于执行存储过程相关的接口。该类和实现类PreparedStatementHandler逻辑类似,这里不再重复。只是分析一下特殊的parameterize()方法及其调用的registerOutputParameters()方法。
- parameterize()方法
在parameterize()方法中,首先通过registerOutputParameters()方法处理statement参数,然后在调用parameterHandler.setParameters()方法进行参数设置。
//CallableStatementHandler.java
@Override
public void parameterize(Statement statement) throws SQLException {
registerOutputParameters((CallableStatement) statement);
parameterHandler.setParameters((CallableStatement) statement);
}
- registerOutputParameters()方法
处理存储过程的输出参数。在后续介绍parameterHandler和resultSetHandler相关内容时,会详细介绍。
//CallableStatementHandler.java
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);
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());
}
}
}
}
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/68873.html