上回我们讲到Mybatis加载相关的配置文件进行初始化,这回我们讲一下一次SQL查询怎么进行的。
准备工作
Mybatis完成一次SQL查询需要使用的代码如下:
String resource = "mybatis.cfg.xml";
Reader reader = Resources.getResourceAsReader(resource);
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = ssf.openSession();
try {
UserInfo user = (UserInfo) session.selectOne("User.selectUser", "1");
System.out.println(user);
} catch (Exception e) {
e.printStackTrace();
} finally {
session.close();
}
本次我们需要进行深入跟踪分析的是:
SqlSession session = ssf.openSession();
UserInfo user = (UserInfo) session.selectOne("User.selectUser", "1");
源码分析
第一步:打开一个会话,我们看看里面具体做了什么事情。
SqlSession session = ssf.openSession();
DefaultSqlSessionFactory的 openSession()方法内容如下:
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
跟进去,我们看一下openSessionFromDataSource方法到底做了啥:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Connection connection = null;
try {
final Environment environment = configuration.getEnvironment();
final DataSource dataSource = getDataSourceFromEnvironment(environment);
TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
connection = dataSource.getConnection();
if (level != null) {
connection.setTransactionIsolation(level.getLevel());
}
connection = wrapConnection(connection);
Transaction tx = transactionFactory.newTransaction(connection, autoCommit);
Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeConnection(connection);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
这里我们分析一下这里所涉及的步骤:
(1)获取前面我们加载配置文件的环境信息,并且获取环境信息中配置的数据源。
(2)通过数据源获取一个连接,对连接进行包装代理(通过JDK的代理来实现日志功能)。
(3)设置连接的事务信息(是否自动提交、事务级别),从配置环境中获取事务工厂,事务工厂获取一个新的事务。
(4)传入事务对象获取一个新的执行器,并传入执行器、配置信息等获取一个执行会话对象。
从上面的代码我们可以得出,一次配置加载只能有且对应一个数据源。对于上述步骤,我们不难理解,我们重点看看新建执行器和DefaultSqlSession。
首先,我们看看newExecutor到底做了什么?
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;
}
上面代码的执行步骤如下:
(1)判断执行器类型,如果配置文件中没有配置执行器类型,则采用默认执行类型ExecutorType.SIMPLE。
(2)根据执行器类型返回不同类型的执行器(执行器有三种,分别是 BatchExecutor、SimpleExecutor和CachingExecutor,后面我们再详细看看)。
(3)跟执行器绑定拦截器插件(这里也是使用代理来实现)。
DefaultSqlSession到底是干什么的呢?
DefaultSqlSession实现了SqlSession接口,里面有各种各样的SQL执行方法,主要用于SQL操作的对外接口,它会的调用执行器来执行实际的SQL语句。
接下来我们看看SQL查询是怎么进行的
UserInfo user = (UserInfo) session.selectOne("User.selectUser", "1");
实际调用的是DefaultSqlSession类的selectOne方法,该方法代码如下:
public Object selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List list = selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
我们再看看selectList方法(实际上是调用该类的另一个selectList方法来实现的):
public List selectList(String statement, Object parameter) {
return selectList(statement, parameter, RowBounds.DEFAULT);
}
public List selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
第二个selectList的执行步骤如下:
(1)根据SQL的ID到配置信息中找对应的MappedStatement,在之前配置被加载初始化的时候我们看到了系统会把配置文件中的SQL块解析并放到一个MappedStatement里面,并将MappedStatement对象放到一个Map里面进行存放,Map的key值是该SQL块的ID。
(2)调用执行器的query方法,传入MappedStatement对象、SQL参数对象、范围对象(此处为空)和结果处理方式。
好了,目前只剩下一个疑问,那就是执行器到底怎么执行SQL的呢?
上面我们知道了,默认情况下是采用SimpleExecutor执行的,我们看看这个类的doQuery方法:
public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler);
stmt = prepareStatement(handler);
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
doQuery方法的内部执行步骤:
(1) 获取配置信息对象。
(2)通过配置对象获取一个新的StatementHandler,该类主要用来处理一次SQL操作。
(3)预处理StatementHandler对象,得到Statement对象。
(4)传入Statement和结果处理对象,通过StatementHandler的query方法来执行SQL,并对执行结果进行处理。
我们看一下newStatementHandler到底做了什么?
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
上面代码的执行步骤:
(1)根据相关的参数获取对应的StatementHandler对象。
(2)为StatementHandler对象绑定拦截器插件。
RoutingStatementHandler类的构造方法RoutingStatementHandler如下:
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
根据 MappedStatement对象的StatementType来创建不同的StatementHandler,这个跟前面执行器的方式类似。StatementType有STATEMENT、PREPARED和CALLABLE三种类型,跟JDBC里面的Statement类型一一对应。
我们看一下prepareStatement方法具体内容:
private Statement prepareStatement(StatementHandler handler) throws SQLException {
Statement stmt;
Connection connection = transaction.getConnection();
//从连接中获取Statement对象
stmt = handler.prepare(connection);
//处理预编译的传入参数
handler.parameterize(stmt);
return stmt;
}
分享到:
相关推荐
主要是对sql基本原理,加部分sqlmap源码分析
, 《深入理解SPARK:核心思想与源码分析》一书对Spark1.2.0版本的源代码进行了全面而深入的分析,旨在为Spark的优化、定制和扩展提供原理性的指导。阿里巴巴集团专家鼎力推荐 、阿里巴巴资深Java开发和大数据专家...
一个完整而简单SSM项目,包含SQL脚本、代码与学习笔记。内容Spring如何整合SpringMVC与Mybatis以及整合原理的源码分析。
核心设计篇(第3~7章),着重讲解SparkContext的初始化、存储体系、任务提交与执行、计算引擎及部署模式的原理和源码分析。通过这部分的内容,读者可以通过源码剖析更加深入理解Spark的核心设计与实现,以便在...
关系数据库原理及应用课程设计基于VB和SQL Server实现的题库管理系统源码+数据库设计 一、系统需求分析 题库管理系统由用户,课程,课程章节,题型,题目,套题,套题名6个模块(表)组成,可实现插入,删除,更新,...
Flink SQL大数据项目实战课程以FlinkSQL流批一体技术为主线,全面讲解Flink Table编程、SQL编程、Time与WaterMark、Window操作、函数使用、元数据管理,最后以一个完整的实战项目为例,详细讲解FlinkSQL的流式项目...
java8 集合源码分析 开发面试常见问题整理 算法题:这个一般不难,剑指offer或者LeetCode ...网络:TCP三次握手四次挥手,TCP/IP传输原理,拥塞避免算法,DNS原理,访问一个URL的过程,UDP TCP区别,LVS,路由
实现一个基于 Hadoop、Spark 或其他大数据平台的数据分析系统,理解其中的实现细节以及各种算法的原理。 实验软件和硬件环境: 1)操作系统:Linux(实验室版本为 Ubuntu17.04,集群环境为 centos6.5); 2)...
通过一些程序源码对SQL的攻击进行了细致分析,使我们对SQL Injection机理有了一个深入认识,作为一名Web应用开发人员,一定不要盲目相信用户的输入,而要对用户输入的数据进行严格的校验处理,否则的话,SQL ...
随着全球信息化大局的全面铺开,全世界生产力和社会经济的发展都越来越离 不开互联网,大到国家,小到组织和个人,都已经习惯...(2)介绍和研究了SQL注入漏洞的产生原理和常见的的利用方式,并列举SQL 注入方法,分析和
5个目标文件,演示Address EJB的实现,创建一个EJB测试客户端,得到名字上下文,查询jndi名,通过强制转型得到Home接口,getInitialContext()函数返回一个经过初始化的上下文,用client的getHome()函数调用Home接口...
开源 bbs 源码 java Software-and-System-Security- maybe more than 200 tasks ...要求:对照清单,上面所有的东西都掌握,一项项检测自己的能力 ...XSS,SQL注入 ...主要是一些代码的逻辑错误,每个...不看源码,把需要分析的
本文深入探讨了ShardingSphere的内核原理及其核心源码,提供了关于其解析、路由、改写、执行和归并引擎的详细分析。首先,解析引擎部分介绍了ShardingSphere如何将SQL语句转换为抽象语法树(AST),以及如何根据不同...
西安电子科技大学编译原理大作业代码实现及报告 一 上机题目 实现一个简单语言(Core Programming Language,CPL)的编译器前端 二 功能要求 接收以CPL编写的程序,对其进行词法分析、语法分析、语法制导翻译,...
58:快速设计一个企业网站之BIWEB 59:关于BIWEB常见问题和结构分析 60:PHP与数据库代码开发规范 61:PHP+Mysql新闻系统的开发一 62:PHP+Mysql新闻系统的开发二 63:PHP+Mysql新闻系统的开发三 64:PHP+Mysql新闻...
**航空公司管理系统VB+SQL毕业设计源码与论文** 本资源提供了一个基于VB和SQL的航空公司管理系统,旨在帮助航空公司高效地管理航班、乘客、员工以及票务信息。系统采用模块化设计,包括用户登录、航班管理、乘客...
于是开启了对其文档翻译以及源码分析的工作,旨在了解平台运作的原理,能力边界以及使用的场景。 本教程是一个动态进行时,内容随时会跟着研究更新。 为了避免一些猜测性的想法和已经探明的事实混淆一起,将会在已经...
(C#源码+Sqlserver数据库+系统设计解决方案文档)的动物诊所综合管理平台(源码).rar 宠物在人们的生活起到了很重要的作用,对于一部分人来说,或许宠物是个不可替代的角色。因为宠物有时是人类情感抒发的一个对象...