大家好,又见面了,我是你们的朋友全栈君。
1.原始 jdbc 工作流程
原始 jdbc 工作流程 以查询为例
1.1 加载驱动
Class.forName(Driver.class.getName())
1.2 建立数据库连接
Connection root = DriverManager.getConnection(“xx”, “xx”, “xx”)
1.3 预编译sql语句
PreparedStatement preparedStatement = root.prepareStatement(sql)
1.4 占位符参数赋值
preparedStatement.setString(1,“1”); PS:下标从1开始
1.5 执行sql
1.6 返回结果集
ResultSet resultSet = preparedStatement.executeQuery()
1.7 关闭数据库连接
xxx…close()
2.源码探究 mybatis 工作流程
通过jdbc 的工作流程可以看到大致分为:
预编译sql语句,处理参数,执行sql语句,封装结果集
同样 mybatis 工作流程大致也是这样的。但是mybatis 在初始化封装 MappedStatement 对象的时候就已经完成了预编译。
大致分为:选择执行器,处理参数,执行sql语句,封装结果集 对应工作的mybatis 四大对象分别为: Executor ParameterHandler StatementHandler ResultSetHandler 非常相似,因为mybatis 底层就是封装的 jdbc
执行器
类图
1.选择执行器
mybatis 官网中 也有价绍,在mybatis 初始化的时候可以在配置文件的settings节点配置 defaultExecutorType 类型 ,默认的执行器为SIMPLE 还有另外两个即REUSE,BATCH。 区别:以批量插入为例
SIMPLE 每执行一次update操作,就开启一个Statement对象,用完立刻关闭Statement对象。
源码:SimpleExecutor
代码语言:javascript复制 @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();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
REUSE 每执行一次update操作,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象
源码:ReuseExecutor
代码语言:javascript复制 public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
Statement stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
}
BATCH 每执行一次update操作,就缓存一个Statement对象,然后统一执行
源码:BatchExecutor
代码语言:javascript复制 public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException {
Statement stmt = null;
try {
flushStatements();
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
Connection connection = getConnection(ms.getStatementLog());
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
2 实例化执行器
在通过 SqlSessionFactory 实例化 SqlSession 的时候完成了执行器的初始化
代码语言:javascript复制SqlSession sqlSession = sqlSessionFactory.openSession()
//默认的SqlSessionFactory
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
代码语言:javascript复制 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();
}
}
初始化数据库信息
Environment environment = configuration.getEnvironment();
初始化事务信息
TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
主要的一行
代码语言:javascript复制final Executor executor = configuration.newExecutor(tx, execType);
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;
}
3 通过执行器选择执行方法
当我们进行查询的时候,会通过命名空间 方法名 封装成一个 id ,去MappedStatement 集合里面获取到当前的 MappedStatement 对象,并开始通过执行器开始执行查询方法,源码:DefaultSqlSession
代码语言:javascript复制 public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " e, e);
} finally {
ErrorContext.instance().reset();
}
}
3.通过mybatis 工作流程 窥探拦截器执行顺序
调用拦截器
executor.query(ms, wrapCollection(parameter), rowBounds, handler);
这一行是通过执行器的代理对象 去执行query的方法。 官网提供的拦截器插件文档 Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) 如果命中到拦截器就会执行拦截器的拦截方法,如果有条件限制 要么放行,要么执行拦截逻辑,代理对象是如何生成的呢?
创建代理对象
1 创建 Executor 代理对象
最终会进入到两个分支BaseExecutor 和 CachingExecutor BaseExecutor 有三个实现类 CachingExecutor 内部维护了一个 Executor接口,在执行query方法的时候会再次进入 BaseExecutor 的 query 方法里面
BaseExecutor#query(MappedStatement, Object, RowBounds, ResultHandler, CacheKey, BoundSql)
这个方法里面有一个去查询的方法
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
到这个方法就是上面提到的不同执行器的 doQuery 方法
以SimpleExecutor 的 doQuery 方法为例
代码语言:javascript复制 @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();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
而在我们实例化执行器的时候 ,提到过这样一行代码
final Executor executor = configuration.newExecutor(tx, execType);
在这个newExecutor 里面去实例化了一个 执行器,并且
executor = (Executor) interceptorChain.pluginAll(executor);
会生成 Executor 的代理对象(在初始化的mybatis 环境的时候就已经将自定义的拦截器全部添加到一个内部维护的集合里面去了)
2 创建 StatementHandler 代理对象
代码语言:javascript复制StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
先创建 一种 RoutingStatementHandler 的实例
代码语言:javascript复制 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());
}
}
3 创建 ParameterHandler 代理对象
三个分支里面全部都有这样的一行代码
代码语言:javascript复制this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
代码语言:javascript复制 public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
4 创建 ResultSetHandler 代理对象
三个分支里面全部都有这样的一行代码
代码语言:javascript复制this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
代码语言:javascript复制 public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
调用拦截器
官网提供的拦截器插件文档 StatementHandler (prepare, parameterize, batch, update, query) 以SimpleExecutor 的 doQuery 方法为例
代码语言:javascript复制 @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();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
stmt = prepareStatement(handler, ms.getStatementLog());
RoutingStatementHandler
代码语言:javascript复制 public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
return delegate.prepare(connection, transactionTimeout);
}
再调用 BaseStatementHandler 的 prepare 方法 再回来 执行拦截器拦截的StatementHandler 的 prepare 方法,要么放行,要么执行拦截逻辑。再回来执行代码的下一行
代码语言:javascript复制 private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
即 StatementHandler handler.parameterize(stmt);又会拦截
调用拦截器
即 StatementHandler handler.parameterize(stmt);
一共四种策略
代码语言:javascript复制CallableStatementHandler #parameterize
public void parameterize(Statement statement) throws SQLException {
registerOutputParameters((CallableStatement) statement);
parameterHandler.setParameters((CallableStatement) statement);
}
官网提供的拦截器插件文档 ParameterHandler (getParameterObject, setParameters)
执行 CallableStatementHandler 类型的 setParameters 方法的拦截器
代码语言:javascript复制PreparedStatementHandler #parameterize
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
执行 PreparedStatementHandler 类型的 setParameters 方法的拦截器
代码语言:javascript复制RoutingStatementHandler #parameterize
@Override
public void parameterize(Statement statement) throws SQLException {
delegate.parameterize(statement);
}
通过路由到某一种StatementHandler 类型的 setParameters 方法的拦截器
代码语言:javascript复制SimpleStatementHandler #parameterize
@Override
public void parameterize(Statement statement) {
// N/A
}
此类型的 setParameters 无拦截器工作
调用拦截器
回到上面的 doQuery 方法的最后一行
return handler.query(stmt, resultHandler);
官网提供的拦截器插件文档 StatementHandler (prepare, parameterize, batch, update, query) ResultSetHandler (handleResultSets, handleOutputParameters)
调用拦截器
代码语言:javascript复制CallableStatementHandler #query
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
CallableStatement cs = (CallableStatement) statement;
cs.execute();
List<E> resultList = resultSetHandler.handleResultSets(cs);
resultSetHandler.handleOutputParameters(cs);
return resultList;
}
调用拦截器
List resultList = resultSetHandler.handleResultSets(cs);
调用拦截器
代码语言:javascript复制resultSetHandler.handleOutputParameters(cs);
public void handleOutputParameters(CallableStatement cs) throws SQLException {
final Object parameterObject = parameterHandler.getParameterObject();
//省略......
}
调用拦截器
ParameterHandler # getParameterObject
另外两种 PreparedStatementHandler SimpleStatementHandler 只有这一行调用了一次拦截器 List resultList = resultSetHandler.handleResultSets(cs);
而RoutingStatementHandler 是从新路由到上面三种的其中一种。
这样一整个doQuery 方法的拦截器调用完成
图1:SqlSessionFactory 到 doQuery
图2:doQuery – close
所以一个正常的查询被拦截器拦截的顺序应为: Executor -> query StatementHandler -> prepare StatementHandler -> parameterize ParameterHandler -> setParameters StatementHandler -> query ResultSetHandler -> handleResultSets ResultSetHandler -> handleOutputParameters ParameterHandler -> getParameterObject
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/197346.html原文链接:https://javaforall.cn