原理分析之四:一次SQL查询的源码分析

上回我们讲到Mybatis加载相关的配置文件进行初始化,这回我们讲一下一次SQL查询怎么进行的。

 

准备工作

Mybatis完成一次SQL查询需要使用的代码如下:

 

 

Java代码   收藏代码
  1. String resource = "mybatis.cfg.xml";  
  2.         Reader reader = Resources.getResourceAsReader(resource);  
  3.         SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);  
  4.           
  5.       <strong>  </strong>SqlSession session = ssf.openSession();  
  6.           
  7.         try {  
  8.             UserInfo user = (UserInfo) session.selectOne("User.selectUser""1");  
  9.             System.out.println(user);  
  10.         } catch (Exception e) {  
  11.             e.printStackTrace();  
  12.         } finally {  
  13.             session.close();  
  14.         }  

 

本次我们需要进行深入跟踪分析的是:

 

Java代码   收藏代码
  1. SqlSession session = ssf.openSession();  
  2.         
  3. UserInfo user = (UserInfo) session.selectOne("User.selectUser""1");  
 

 

 

源码分析

 

第一步:打开一个会话,我们看看里面具体做了什么事情。

 

Java代码   收藏代码
  1. SqlSession session = ssf.openSession();  

 

 

DefaultSqlSessionFactory的 openSession()方法内容如下:

 

Java代码   收藏代码
  1. public SqlSession openSession() {  
  2.     return openSessionFromDataSource(configuration.getDefaultExecutorType(), nullfalse);  
  3.   }  
 

 

跟进去,我们看一下openSessionFromDataSource方法到底做了啥:

 

Java代码   收藏代码
  1. private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {  
  2.     Connection connection = null;  
  3.     try {  
  4.       final Environment environment = configuration.getEnvironment();  
  5.       final DataSource dataSource = getDataSourceFromEnvironment(environment);  
  6.       TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);  
  7.       connection = dataSource.getConnection();  
  8.       if (level != null) {  
  9.         connection.setTransactionIsolation(level.getLevel());  
  10.       }  
  11.       connection = wrapConnection(connection);  
  12.       Transaction tx = transactionFactory.newTransaction(connection, autoCommit);  
  13.       Executor executor = configuration.newExecutor(tx, execType);  
  14.       return new DefaultSqlSession(configuration, executor, autoCommit);  
  15.     } catch (Exception e) {  
  16.       closeConnection(connection);  
  17.       throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);  
  18.     } finally {  
  19.       ErrorContext.instance().reset();  
  20.     }  
  21.   }  
 

 

这里我们分析一下这里所涉及的步骤:

(1)获取前面我们加载配置文件的环境信息,并且获取环境信息中配置的数据源。

(2)通过数据源获取一个连接,对连接进行包装代理(通过JDK的代理来实现日志功能)。

(3)设置连接的事务信息(是否自动提交、事务级别),从配置环境中获取事务工厂,事务工厂获取一个新的事务。

(4)传入事务对象获取一个新的执行器,并传入执行器、配置信息等获取一个执行会话对象。

 

从上面的代码我们可以得出,一次配置加载只能有且对应一个数据源。对于上述步骤,我们不难理解,我们重点看看新建执行器和DefaultSqlSession。

首先,我们看看newExecutor到底做了什么?

 

Java代码   收藏代码
  1. public Executor newExecutor(Transaction transaction, ExecutorType executorType) {  
  2.    executorType = executorType == null ? defaultExecutorType : executorType;  
  3.    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;  
  4.    Executor executor;  
  5.    if (ExecutorType.BATCH == executorType) {  
  6.      executor = new BatchExecutor(this, transaction);  
  7.    } else if (ExecutorType.REUSE == executorType) {  
  8.      executor = new ReuseExecutor(this, transaction);  
  9.    } else {  
  10.      executor = new SimpleExecutor(this, transaction);  
  11.    }  
  12.    if (cacheEnabled) {  
  13.      executor = new CachingExecutor(executor);  
  14.    }  
  15.    executor = (Executor) interceptorChain.pluginAll(executor);  
  16.    return executor;  
  17.  }  

 

上面代码的执行步骤如下:

(1)判断执行器类型,如果配置文件中没有配置执行器类型,则采用默认执行类型ExecutorType.SIMPLE。

(2)根据执行器类型返回不同类型的执行器(执行器有三种,分别是 BatchExecutor、SimpleExecutor和CachingExecutor,后面我们再详细看看)。

(3)跟执行器绑定拦截器插件(这里也是使用代理来实现)。

 

DefaultSqlSession到底是干什么的呢?

DefaultSqlSession实现了SqlSession接口,里面有各种各样的SQL执行方法,主要用于SQL操作的对外接口,它会的调用执行器来执行实际的SQL语句。

 

接下来我们看看SQL查询是怎么进行的

 

 

Java代码   收藏代码
  1. UserInfo user = (UserInfo) session.selectOne("User.selectUser""1");  

 实际调用的是DefaultSqlSession类的selectOne方法,该方法代码如下:

 

Java代码   收藏代码
  1. public Object selectOne(String statement, Object parameter) {  
  2.     // Popular vote was to return null on 0 results and throw exception on too many.  
  3.     List list = selectList(statement, parameter);  
  4.     if (list.size() == 1) {  
  5.       return list.get(0);  
  6.     } else if (list.size() > 1) {  
  7.       throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());  
  8.     } else {  
  9.       return null;  
  10.     }  
  11.   }  
 

我们再看看selectList方法(实际上是调用该类的另一个selectList方法来实现的):

 

Java代码   收藏代码
  1. public List selectList(String statement, Object parameter) {  
  2.     return selectList(statement, parameter, RowBounds.DEFAULT);  
  3.   }  
  4.   
  5.   public List selectList(String statement, Object parameter, RowBounds rowBounds) {  
  6.     try {  
  7.       MappedStatement ms = configuration.getMappedStatement(statement);  
  8.       return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);  
  9.     } catch (Exception e) {  
  10.       throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);  
  11.     } finally {  
  12.       ErrorContext.instance().reset();  
  13.     }  
  14.   }  

 

第二个selectList的执行步骤如下:

(1)根据SQL的ID到配置信息中找对应的MappedStatement,在之前配置被加载初始化的时候我们看到了系统会把配置文件中的SQL块解析并放到一个MappedStatement里面,并将MappedStatement对象放到一个Map里面进行存放,Map的key值是该SQL块的ID。

(2)调用执行器的query方法,传入MappedStatement对象、SQL参数对象、范围对象(此处为空)和结果处理方式。

 

好了,目前只剩下一个疑问,那就是执行器到底怎么执行SQL的呢?

 

上面我们知道了,默认情况下是采用SimpleExecutor执行的,我们看看这个类的doQuery方法:

 

Java代码   收藏代码
  1. public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {  
  2.     Statement stmt = null;  
  3.     try {  
  4.       Configuration configuration = ms.getConfiguration();  
  5.       StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler);  
  6.       stmt = prepareStatement(handler);  
  7.       return handler.query(stmt, resultHandler);  
  8.     } finally {  
  9.       closeStatement(stmt);  
  10.     }  
  11.   }  

 

doQuery方法的内部执行步骤:

(1) 获取配置信息对象。

(2)通过配置对象获取一个新的StatementHandler,该类主要用来处理一次SQL操作。

(3)预处理StatementHandler对象,得到Statement对象。

(4)传入Statement和结果处理对象,通过StatementHandler的query方法来执行SQL,并对执行结果进行处理。

 

 

我们看一下newStatementHandler到底做了什么?

 

 

Java代码   收藏代码
  1. public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) {  
  2.     StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler);  
  3.     statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);  
  4.     return statementHandler;  
  5.   }  

 

 

上面代码的执行步骤:

(1)根据相关的参数获取对应的StatementHandler对象。

(2)为StatementHandler对象绑定拦截器插件。

 

RoutingStatementHandler类的构造方法RoutingStatementHandler如下:

 

Java代码   收藏代码
  1. public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) {  
  2.   
  3.    switch (ms.getStatementType()) {  
  4.      case STATEMENT:  
  5.        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler);  
  6.        break;  
  7.      case PREPARED:  
  8.        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler);  
  9.        break;  
  10.      case CALLABLE:  
  11.        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler);  
  12.        break;  
  13.      default:  
  14.        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());  
  15.    }  
  16.   
  17.  }  

 

根据 MappedStatement对象的StatementType来创建不同的StatementHandler,这个跟前面执行器的方式类似。StatementType有STATEMENT、PREPARED和CALLABLE三种类型,跟JDBC里面的Statement类型一一对应。

 

我们看一下prepareStatement方法具体内容:

Java代码   收藏代码
  1. private Statement prepareStatement(StatementHandler handler) throws SQLException {  
  2.    Statement stmt;  
  3.    Connection connection = transaction.getConnection();  
  4.    //从连接中获取Statement对象  
  5.    stmt = handler.prepare(connection);  
  6.    //处理预编译的传入参数  
  7.    handler.parameterize(stmt);  
  8.    return stmt;  
  9.  }  
 
### 回答1: "Microsoft SQL Server 2008技术内幕: T-SQL查询源码" 是一本关于SQL Server 2008中T-SQL查询的技术书籍。该书深入探讨了SQL Server 2008的查询引擎和T-SQL语言的内部工作原理。 T-SQLSQL Server的编程语言,用于编写查询和操作数据库的指令。书中通过分析T-SQL查询的源代码,揭示了SQL Server 2008的查询引擎是如何解析和执行查询的。读者可以深入了解查询处理器的内部机制,包括查询优化、查询重写、查询执行计划等方面。 通过阅读这本书,读者可以学习到优化T-SQL查询的技巧和策略。了解查询优化器的工作原理可以帮助开发人员编写高效的查询,提高系统的性能和响应速度。此外,书中还介绍了T-SQL查询的执行计划,帮助读者理解查询的执行方式和流程。 除了T-SQL查询源码解析外,这本书还涵盖了SQL Server 2008的其他关键技术。读者将了解到索引、数据访问方法、事务处理和并发控制等方面的知识。这将帮助开发人员合理设计数据库架构,提高系统的稳定性和可扩展性。 总之,"Microsoft SQL Server 2008技术内幕: T-SQL查询源码"是一本有关SQL Server 2008查询引擎和T-SQL语言的深度技术书籍。它提供了对查询处理器的全面理解,并帮助读者优化T-SQL查询、提高系统性能。同时,它还涵盖了SQL Server 2008的其他关键技术,提供了全面的数据库开发和管理指导。 ### 回答2: Microsoft SQL Server 2008是一款常用的关系型数据库管理系统,具有强大的数据存储和查询能力。T-SQLSQL Server的一种查询语言,通过编写T-SQL查询语句,可以对数据库中的数据进行查询、筛选和排序等操作。 T-SQL查询源码指的是SQL Server内部实现这些查询的代码。通过查看T-SQL查询源码,可以深入了解SQL Server的执行机制,帮助我们更好地优化查询性能和进行调试。 查询源码包括了SQL Server的执行引擎、查询优化器和存储引擎等多个模块的代码。这些源码使用C++等编程语言编写,属于SQL Server的核心代码部分。通过分析源码,我们可以了解SQL Server是如何解析T-SQL查询语句、生成执行计划和执行查询的过程。同时,源码中还包含了一些关键算法和数据结构的实现细节,这对于我们理解SQL Server的内部机制非常有帮助。 然而,由于SQL Server的源码属于Microsoft的商业机密,一般用户无法直接获取到完整的源码。只有Microsoft的工程师和合作伙伴才能访问和修改源码。但是,Microsoft会通过提供一些加密的DLL文件和一些公开的接口,让用户能够使用和扩展SQL Server的功能。 总的来说,虽然我们无法直接查看SQL Server 2008的T-SQL查询源码,但是通过学习SQL Server的文档和使用工具,我们仍然能够深入了解SQL Server的内部机制,并优化我们的查询性能。 ### 回答3: Microsoft SQL Server 2008是一种关系型数据库管理系统,它为用户提供了一个强大的平台来存储、管理和检索数据。T-SQL(Transact-SQL)是SQL Server的查询语言,用于编写存储过程、触发器、视图和其他数据库对象的脚本。 要查询T-SQL源码,可以参考以下步骤: 1. 首先,确保已经安装了SQL Server Management Studio(SSMS),它是一个图形化界面的工具,用于与SQL Server进行交互。 2. 打开SSMS,并连接到SQL Server 2008数据库实例。 3. 在“对象资源管理器”中,展开“数据库”节点,找到包含要查询的T-SQL脚本的数据库。 4. 右键单击该数据库,选择“新建查询”或使用快捷键“Ctrl+N”创建一个新查询窗口。 5. 将要查询的T-SQL脚本复制并粘贴到查询窗口中。确保脚本是有效的,并且已经被正确地复制到查询窗口中。 6. 单击查询窗口工具栏上的“执行”按钮或使用快捷键“F5”来执行查询。 7. 执行查询后,将在结果窗口中显示查询的结果。结果包括与T-SQL脚本相关的数据,如行数、列名和结果集。 通过这些步骤,您可以轻松地查询T-SQL源码,并查看与之相关的数据。这是一种方便的方法,可以帮助开发人员和数据库管理员更好地理解和管理SQL Server 2008中的查询
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值