mybatis源码学习------StatementHandler
StatementHandler接口
StatementHandler接口提供了很多功能(从接口的定义中可以看出),是sql执行器接口的基础。
接口定义
public interface StatementHandler {
//从连接对象中获取一个Statement
Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;
//给Statement绑定实参
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;
//执行查询,返回一个游标
<E> Cursor<E> queryCursor(Statement statement)
throws SQLException;
//获取绑定的BoundSql对象
BoundSql getBoundSql();
//获取ParameterHandler的实例
ParameterHandler getParameterHandler();
}
StatementHandler的类图
RoutingStatementHandler
RoutingStatementHandler实现了StatementHandler接口,使用了策略设计模式实现,帮调用者屏蔽了不同实现的差异,简化调用。
在RoutingStatementHandler的构造函数中对delegate属性进行了初始化,其他方法的调用都是通过委派者完成的。
public class RoutingStatementHandler implements StatementHandler {
//持有一个StatementHandler的具体实现
private final StatementHandler delegate;
//构造函数根据MappedStatement的类型为其创建不同的StatementHandler实例
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());
}
}
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
return delegate.prepare(connection, transactionTimeout);
}
@Override
public void parameterize(Statement statement) throws SQLException {
delegate.parameterize(statement);
}
@Override
public void batch(Statement statement) throws SQLException {
delegate.batch(statement);
}
@Override
public int update(Statement statement) throws SQLException {
return delegate.update(statement);
}
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
return delegate.query(statement, resultHandler);
}
@Override
public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
return delegate.queryCursor(statement);
}
@Override
public BoundSql getBoundSql() {
return delegate.getBoundSql();
}
@Override
public ParameterHandler getParameterHandler() {
return delegate.getParameterHandler();
}
}
BaseStatementHandler
BaseStatementHandler及其子类使用了模板设计模式,模板设计模式有以下优点:
- 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
- 在父类中提取了公共的部分代码,便于代码复用。
- 分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
属性
//全局配置对象
protected final Configuration configuration;
//默认对象工厂
protected final ObjectFactory objectFactory;
//类型处理器注册中心
protected final TypeHandlerRegistry typeHandlerRegistry;
//结果集处理器
protected final ResultSetHandler resultSetHandler;
//参数处理器
protected final ParameterHandler parameterHandler;
//sql执行器
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();
//boundSql为空表示当前为修改类操作,即增删改操作,则先生成对应的主键再创建BoundSql对象
if (boundSql == null) {
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
}
this.boundSql = boundSql;
//创建ParameterHandler对象
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
//创建ResultSetHandler对象
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
接口方法
prepare
代码中有设置fetchSize属性的逻辑,关于Statement的fetchSize属性可以参考这篇文章学习:https://blog.csdn.net/seven_3306/article/details/9303879
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
//创建Statement对象
statement = instantiateStatement(connection);
//设置超时时间
setStatementTimeout(statement, transactionTimeout);
//设置statement的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);
}
}
getBoundSql
@Override
public BoundSql getBoundSql() {
return boundSql;
}
getParameterHandler
@Override
public ParameterHandler getParameterHandler() {
return parameterHandler;
}
instantiateStatement
抽象方法,交由具体子类实现
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
setStatementTimeout
用于设置查询超时时间。如果sql语句的配置中设置了查询超时时间,则以sql语句的配置中设置的为准,否则以全局配置的查询超时时间为准。如果上面两个地方都没有设置查询超时时间,则以事务的超时时间为兜底方案。具体代码如下:
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);
}
setFetchSize
设置FetchSize属性,如果sql语句的配置中设置了fetchSize的大小,则以sql语句的配置中设置的为准,否则以全局配置的fetchSize大小为准。
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);
}
}
closeStatement
关闭statement,JDBC的模板代码
protected void closeStatement(Statement statement) {
try {
if (statement != null) {
statement.close();
}
} catch (SQLException e) {
//ignore
}
}
generateKeys
生成自增主键
protected void generateKeys(Object parameter) {
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
ErrorContext.instance().store();
keyGenerator.processBefore(executor, mappedStatement, null, parameter);
ErrorContext.instance().recall();
}
ParameterHandler接口
ParameterHandler接口提供了为sql语句绑定实参的功能
public interface ParameterHandler {
//获取实参对象
Object getParameterObject();
//为sql语句绑定实参
void setParameters(PreparedStatement ps) throws SQLException;
}
ParameterHandler接口类图如下
从其类图中可以看出,该接口只有一个实现DefaultParameterHandler,其代码如下:
DefaultParameterHandler
属性
//类型处理器注册中心
private final TypeHandlerRegistry typeHandlerRegistry;
private final MappedStatement mappedStatement;
//实参对象
private final Object parameterObject;
private final BoundSql boundSql;
//全局配置对象
private final Configuration configuration;
构造函数
public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
this.mappedStatement = mappedStatement;
this.configuration = mappedStatement.getConfiguration();
this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
this.parameterObject = parameterObject;
this.boundSql = boundSql;
}
接口方法
getParameterObject
获取实参对象
@Override
public Object getParameterObject() {
return parameterObject;
}
setParameters
通过TypeHandler为sql语句绑定实参。
@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) {//OUT表示出参,这里只处理入参
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {//属性存在于additionalParameters属性中(可以通过<bind/>传入)
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {//实参为空
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {//可以转换为对应的JDBC类型
value = parameterObject;
} else {//其余情况直接将其包装为MetaObject对象
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实现为sql语句绑定实参
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
Statement和PreparedStatement的区别
1、Statement用于执行静态SQL语句,在执行时,必须指定一个事先准备好的SQL语句。
2、PrepareStatement是预编译的SQL语句对象,sql语句被预编译并保存在对象中。被封装的sql语句代表某一类操作,语句中可以包含动态参数“?”,在执行时可以为“?”动态设置参数值。
3、使用PrepareStatement对象执行sql时,sql被数据库进行解析和编译,然后被放到命令缓冲区,每当执行同一个PrepareStatement对象时,它就会被解析一次,但不会被再次编译。在缓冲区可以发现预编译的命令,并且可以重用。
4、PrepareStatement可以减少编译次数提高数据库性能。
SimpleStatementHandler
继承自抽象类BaseStatementHandler,底层使用JDBC的Statement对象来完成数据库操作。
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) {
//执行sql
statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
//获取受影响的行数
rows = statement.getUpdateCount();
//执行后处理逻辑,将自增主键保存到入参中
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else if (keyGenerator instanceof SelectKeyGenerator) {//通过<selectKey>配置的主键生成器
//执行sql
statement.execute(sql);
//获取受影响的行数
rows = statement.getUpdateCount();
//执行后处理逻辑
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else {//没有配置主键生成器
statement.execute(sql);
rows = statement.getUpdateCount();
}
return rows;
}
其余方法
因为SimpleStatementHandler底层使用的是Statement完成对数据库的操作,所以parameterize方法是一个空实现。
其中ResultSetType枚举类的意思如下:
枚举值 | 含义 |
---|---|
ResultSet.TYPE_FORWARD_ONLY | 默认的cursor 类型,仅仅支持结果集forward ,不支持backforward ,random ,last ,first 等操作 |
ResultSet.TYPE_SCROLL_INSENSITIVE | 支持结果集backforward ,random ,last ,first 等操作,对其它session 对数据库中数据做出的更改是不敏感的。 |
ResultSet.TYPE_SCROLL_SENSITIVE | 支持结果集backforward ,random ,last ,first 等操作,对其它session 对数据库中数据做出的更改是敏感的,即其他session 修改了数据库中的数据,会反应到本结果集中。 |
ResultSet.DEFAULT | 默认类型,依赖不同的驱动 |
@Override
public void batch(Statement statement) throws SQLException {
String sql = boundSql.getSql();
statement.addBatch(sql);
}
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
//通过ResultSetHandler组装结果
return resultSetHandler.handleResultSets(statement);
}
@Override
public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
//通过ResultSetHandler组装结果
return resultSetHandler.handleCursorResultSets(statement);
}
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {//结果类型未设置
return connection.createStatement();
} else {//设置结果集为只读的
return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
@Override
public void parameterize(Statement statement) {
// N/A
}
PreparedStatementHandler
继承自抽象类BaseStatementHandler,底层使用JDBC的PreparedStatement完成对数据库的操作。实现逻辑与SimpleStatementHandler类似,看看就ok。
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);
}
@Override
public int update(Statement statement) throws SQLException {
//预编译语句
PreparedStatement ps = (PreparedStatement) statement;
//执行sql
ps.execute();
//受影响的行数
int rows = ps.getUpdateCount();
//获取实参
Object parameterObject = boundSql.getParameterObject();
//获取主键生成器
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
//后置处理逻辑
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
return rows;
}
@Override
public void batch(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.addBatch();
}
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.handleResultSets(ps);
}
@Override
public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler.handleCursorResultSets(ps);
}
//底层通过JDBC的Connection完成PrepareStatement的创建
@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() == ResultSetType.DEFAULT) {
return connection.prepareStatement(sql);
} else {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
@Override
public void parameterize(Statement statement) throws SQLException {
//通过parameterHandler完成实参的绑定
parameterHandler.setParameters((PreparedStatement) statement);
}
}
上一篇: mybatis实现简单的增删改查操作
下一篇: mybatis实现简单的增删改查
推荐阅读
-
Mybatis中SqlSessionFactory和SqlSession学习和原理
-
Mybaits 源码解析 (十)----- 全网最详细,没有之一:Spring-Mybatis框架使用与源码解析
-
Mybatis学习 四
-
PHP网页游戏学习之Xnova(ogame)源码解读(十一)
-
PHP网页游戏学习之Xnova(ogame)源码解读(九)
-
Bootstrap Scrollspy源码学习
-
PHP网页游戏学习之Xnova(ogame)源码解读(五)
-
PHP网页游戏学习之Xnova(ogame)源码解读(四)
-
PHP网页游戏学习之Xnova(ogame)源码解读(七)
-
PHP网页游戏学习之Xnova(ogame)源码解读(十)