原文地址:http://blog.csdn.net/luanlouis/article/details/40422941
MyBatis是目前非常流行的ORM框架,它的功能很強大,然而其實現卻比較簡單、優雅。本文主要講述MyBatis的架構設計思路,并且討論MyBatis的幾個核心部件,然后結合一個select查詢實例,深入代碼,來探究MyBatis的實現。
一、MyBatis的框架設計
?? ? ???注:上圖很大程度上參考了iteye 上的chenjc_it???所寫的博文原理分析之二:框架整體設計?中的MyBatis架構體圖,chenjc_it總結的非常好,贊一個!
1.接口層---和數據庫交互的方式
MyBatis和數據庫的交互有兩種方式:
a.使用傳統的MyBatis提供的API;
b. 使用Mapper接口
????1.1.使用傳統的MyBatis提供的API
????? 這是傳統的傳遞Statement Id?和查詢參數給?SqlSession?對象,使用?SqlSession對象完成和數據庫的交互;MyBatis?提供了非常方便和簡單的API,供用戶實現對數據庫的增刪改查數據操作,以及對數據庫連接信息和MyBatis?自身配置信息的維護操作。
? ? ? ? ? ? ?? ????
????? 上述使用MyBatis?的方法,是創建一個和數據庫打交道的SqlSession對象,然后根據Statement Id?和參數來操作數據庫,這種方式固然很簡單和實用,但是它不符合面向對象語言的概念和面向接口編程的編程習慣。由于面向接口的編程是面向對象的大趨勢,MyBatis?為了適應這一趨勢,增加了第二種使用MyBatis?支持接口(Interface)調用方式。
1.2. 使用Mapper接口
?MyBatis?將配置文件中的每一個<mapper>?節點抽象為一個?Mapper?接口,而這個接口中聲明的方法和跟<mapper>?節點中的<select|update|delete|insert>?節點項對應,即<select|update|delete|insert>?節點的id值為Mapper?接口中的方法名稱,parameterType?值表示Mapper?對應方法的入參類型,而resultMap?值則對應了Mapper?接口表示的返回值類型或者返回結果集的元素類型。
?根據MyBatis?的配置規范配置好后,通過SqlSession.getMapper(XXXMapper.class)?方法,MyBatis?會根據相應的接口聲明的方法信息,通過動態代理機制生成一個Mapper?實例,我們使用Mapper?接口的某一個方法時,MyBatis?會根據這個方法的方法名和參數類型,確定Statement Id,底層還是通過SqlSession.select("statementId",parameterObject);或者SqlSession.update("statementId",parameterObject);?等等來實現對數據庫的操作,(至于這里的動態機制是怎樣實現的,我將準備專門一片文章來討論,敬請關注~)
MyBatis?引用Mapper?接口這種調用方式,純粹是為了滿足面向接口編程的需要。(其實還有一個原因是在于,面向接口的編程,使得用戶在接口上可以使用注解來配置SQL語句,這樣就可以脫離XML配置文件,實現“0配置”)。
2.數據處理層
????? 數據處理層可以說是MyBatis?的核心,從大的方面上講,它要完成三個功能:
a. 通過傳入參數構建動態SQL語句;
b. SQL語句的執行以及封裝查詢結果集成List<E>
?????2.1.參數映射和動態SQL語句生成
?????? 動態語句生成可以說是MyBatis框架非常優雅的一個設計,MyBatis?通過傳入的參數值,使用?Ognl?來動態地構造SQL語句,使得MyBatis?有很強的靈活性和擴展性。
參數映射指的是對于Java?數據類型和jdbc數據類型之間的轉換:這里有包括兩個過程:查詢階段,我們要將java類型的數據,轉換成jdbc類型的數據,通過?preparedStatement.setXXX()?來設值;另一個就是對resultset查詢結果集的jdbcType?數據轉換成java?數據類型。
(至于具體的MyBatis是如何動態構建SQL語句的,我將準備專門一篇文章來討論,敬請關注~)
?????2.2. SQL語句的執行以及封裝查詢結果集成List<E>
????????????? 動態SQL語句生成之后,MyBatis?將執行SQL語句,并將可能返回的結果集轉換成List<E>?列表。MyBatis?在對結果集的處理中,支持結果集關系一對多和多對一的轉換,并且有兩種支持方式,一種為嵌套查詢語句的查詢,還有一種是嵌套結果集的查詢。
3. 框架支撐層
?????3.1. 事務管理機制
????????? 事務管理機制對于ORM框架而言是不可缺少的一部分,事務管理機制的質量也是考量一個ORM框架是否優秀的一個標準,對于數據管理機制我已經在我的博文《深入理解mybatis原理》 MyBatis事務管理機制?中有非常詳細的討論,感興趣的讀者可以點擊查看。
????3.2. 連接池管理機制
由于創建一個數據庫連接所占用的資源比較大, 對于數據吞吐量大和訪問量非常大的應用而言,連接池的設計就顯得非常重要,對于連接池管理機制我已經在我的博文《深入理解mybatis原理》 Mybatis數據源與連接池?中有非常詳細的討論,感興趣的讀者可以點擊查看。
???3.3. 緩存機制
為了提高數據利用率和減小服務器和數據庫的壓力,MyBatis?會對于一些查詢提供會話級別的數據緩存,會將對某一次查詢,放置到SqlSession中,在允許的時間間隔內,對于完全相同的查詢,MyBatis?會直接將緩存結果返回給用戶,而不用再到數據庫中查找。(至于具體的MyBatis緩存機制,我將準備專門一篇文章來討論,敬請關注~)
? 3. 4. SQL語句的配置方式
傳統的MyBatis?配置SQL?語句方式就是使用XML文件進行配置的,但是這種方式不能很好地支持面向接口編程的理念,為了支持面向接口的編程,MyBatis?引入了Mapper接口的概念,面向接口的引入,對使用注解來配置SQL?語句成為可能,用戶只需要在接口上添加必要的注解即可,不用再去配置XML文件了,但是,目前的MyBatis?只是對注解配置SQL?語句提供了有限的支持,某些高級功能還是要依賴XML配置文件配置SQL?語句。
?
4 引導層
引導層是配置和啟動MyBatis?配置信息的方式。MyBatis?提供兩種方式來引導MyBatis?:基于XML配置文件的方式和基于Java?API?的方式,讀者可以參考我的另一片博文:Java Persistence with MyBatis 3(中文版) 第二章 引導MyBatis
????
二、MyBatis的主要構件及其相互關系
? 從MyBatis代碼實現的角度來看,MyBatis的主要的核心部件有以下幾個:
- SqlSession????????????作為MyBatis工作的主要頂層API,表示和數據庫交互的會話,完成必要數據庫增刪改查功能
- Executor??????????????MyBatis執行器,是MyBatis 調度的核心,負責SQL語句的生成和查詢緩存的維護
- StatementHandler???封裝了JDBC Statement操作,負責對JDBC statement 的操作,如設置參數、將Statement結果集轉換成List集合。
- ParameterHandler???負責對用戶傳遞的參數轉換成JDBC Statement 所需要的參數,
- ResultSetHandler????負責將JDBC返回的ResultSet結果集對象轉換成List類型的集合;
- TypeHandler??????????負責java數據類型和jdbc數據類型之間的映射和轉換
- MappedStatement???MappedStatement維護了一條<select|update|delete|insert>節點的封裝,?
- SqlSource????????????負責根據用戶傳遞的parameterObject,動態地生成SQL語句,將信息封裝到BoundSql對象中,并返回
- BoundSql?????????????表示動態生成的SQL語句以及相應的參數信息
- Configuration????????MyBatis所有的配置信息都維持在Configuration對象之中。
(注:這里只是列出了我個人認為屬于核心的部件,請讀者不要先入為主,認為MyBatis就只有這些部件哦!每個人對MyBatis的理解不同,分析出的結果自然會有所不同,歡迎讀者提出質疑和不同的意見,我們共同探討~)
它們的關系如下圖所示:
?
?
三、從MyBatis一次select 查詢語句來分析MyBatis的架構設計
一、數據準備(非常熟悉和應用過MyBatis 的讀者可以迅速瀏覽此節即可)
?????1. 準備數據庫數據,創建EMPLOYEES表,插入數據:??????
[sql]?view plaincopy print?
?????????create??table?"EMPLOYEES"(?????????"EMPLOYEE_ID"?NUMBER(6)?not?null,????????"FIRST_NAME"?VARCHAR2(20),????????"LAST_NAME"?VARCHAR2(25)?not?null,????????"EMAIL"?VARCHAR2(25)?not?null?unique,????????"SALARY"?NUMBER(8,2),?????????constraint?"EMP_EMP_ID_PK"?primary?key?("EMPLOYEE_ID")?????);?????comment?on?table?EMPLOYEES?is?'員工信息表';?????comment?on?column?EMPLOYEES.EMPLOYEE_ID?is?'員工id';?????comment?on?column?EMPLOYEES.FIRST_NAME?is?'first?name';?????comment?on?column?EMPLOYEES.LAST_NAME?is?'last?name';?????comment?on?column?EMPLOYEES.EMAIL?is?'email?address';?????comment?on?column?EMPLOYEES.SALARY?is?'salary';??????????insert?into?EMPLOYEES?(EMPLOYEE_ID,?FIRST_NAME,?LAST_NAME,?EMAIL,?SALARY)??values?(100,?'Steven',?'King',?'SKING',?24000.00);????insert?into?EMPLOYEES?(EMPLOYEE_ID,?FIRST_NAME,?LAST_NAME,?EMAIL,?SALARY)??values?(101,?'Neena',?'Kochhar',?'NKOCHHAR',?17000.00);????insert?into?EMPLOYEES?(EMPLOYEE_ID,?FIRST_NAME,?LAST_NAME,?EMAIL,?SALARY)??values?(102,?'Lex',?'De?Haan',?'LDEHAAN',?17000.00);????insert?into?EMPLOYEES?(EMPLOYEE_ID,?FIRST_NAME,?LAST_NAME,?EMAIL,?SALARY)??values?(103,?'Alexander',?'Hunold',?'AHUNOLD',?9000.00);????insert?into?EMPLOYEES?(EMPLOYEE_ID,?FIRST_NAME,?LAST_NAME,?EMAIL,?SALARY)??values?(104,?'Bruce',?'Ernst',?'BERNST',?6000.00);????insert?into?EMPLOYEES?(EMPLOYEE_ID,?FIRST_NAME,?LAST_NAME,?EMAIL,?SALARY)??values?(105,?'David',?'Austin',?'DAUSTIN',?4800.00);????insert?into?EMPLOYEES?(EMPLOYEE_ID,?FIRST_NAME,?LAST_NAME,?EMAIL,?SALARY)??values?(106,?'Valli',?'Pataballa',?'VPATABAL',?4800.00);????insert?into?EMPLOYEES?(EMPLOYEE_ID,?FIRST_NAME,?LAST_NAME,?EMAIL,?SALARY)??values?(107,?'Diana',?'Lorentz',?'DLORENTZ',?4200.00);?????? ??
?
??2. 配置Mybatis的配置文件,命名為mybatisConfig.xml:
[html]?view plaincopy print?
<?xml?version="1.0"?encoding="utf-8"?>??<!DOCTYPE?configuration?PUBLIC?"-//mybatis.org//DTD?Config?3.0//EN"??"http://mybatis.org/dtd/mybatis-3-config.dtd">??<configuration>????<environments?default="development">??????<environment?id="development">????????<transactionManager?type="JDBC"?/>????????<dataSource?type="POOLED">???????<property?name="driver"?value="oracle.jdbc.driver.OracleDriver"?/>?????????????<property?name="url"?value="jdbc:oracle:thin:@localhost:1521:xe"?/>?????????????<property?name="username"?value="louis"?/>?????????????<property?name="password"?value="123456"?/>????????</dataSource>??????</environment>????</environments>??????<mappers>?????????<mapper??resource="com/louis/mybatis/domain/EmployeesMapper.xml"/>??????</mappers>??</configuration>?? ?
3.???? 創建Employee實體Bean 以及配置Mapper配置文件
[java]?view plaincopy print?
package?com.louis.mybatis.model;????import?java.math.BigDecimal;????public?class?Employee?{??????private?Integer?employeeId;????????private?String?firstName;????????private?String?lastName;????????private?String?email;????????private?BigDecimal?salary;????????public?Integer?getEmployeeId()?{??????????return?employeeId;??????}????????public?void?setEmployeeId(Integer?employeeId)?{??????????this.employeeId?=?employeeId;??????}????????public?String?getFirstName()?{??????????return?firstName;??????}????????public?void?setFirstName(String?firstName)?{??????????this.firstName?=?firstName;??????}????????public?String?getLastName()?{??????????return?lastName;??????}????????public?void?setLastName(String?lastName)?{??????????this.lastName?=?lastName;??????}????????public?String?getEmail()?{??????????return?email;??????}????????public?void?setEmail(String?email)?{??????????this.email?=?email;??????}????????public?BigDecimal?getSalary()?{??????????return?salary;??????}????????public?void?setSalary(BigDecimal?salary)?{??????????this.salary?=?salary;??????}??}?? [html]?view plaincopy print?
<?xml?version="1.0"?encoding="UTF-8"??>??<!DOCTYPE?mapper?PUBLIC?"-//mybatis.org//DTD?Mapper?3.0//EN"?"http://mybatis.org/dtd/mybatis-3-mapper.dtd"?>??<mapper?namespace="com.louis.mybatis.dao.EmployeesMapper"?>??????<resultMap?id="BaseResultMap"?type="com.louis.mybatis.model.Employee"?>??????<id?column="EMPLOYEE_ID"?property="employeeId"?jdbcType="DECIMAL"?/>??????<result?column="FIRST_NAME"?property="firstName"?jdbcType="VARCHAR"?/>??????<result?column="LAST_NAME"?property="lastName"?jdbcType="VARCHAR"?/>??????<result?column="EMAIL"?property="email"?jdbcType="VARCHAR"?/>??????<result?column="SALARY"?property="salary"?jdbcType="DECIMAL"?/>????</resultMap>????????<select?id="selectByPrimaryKey"?resultMap="BaseResultMap"?parameterType="java.lang.Integer"?>??????select???????????EMPLOYEE_ID,?FIRST_NAME,?LAST_NAME,?EMAIL,?SALARY??????????from?LOUIS.EMPLOYEES??????????where?EMPLOYEE_ID?=?#{employeeId,jdbcType=DECIMAL}????</select>??</mapper>?? ?
4. 創建eclipse 或者myeclipse 的maven項目,maven配置如下:
[html]?view plaincopy print?
<project?xmlns="http://maven.apache.org/POM/4.0.0"?xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"????xsi:schemaLocation="http://maven.apache.org/POM/4.0.0?http://maven.apache.org/xsd/maven-4.0.0.xsd">????<modelVersion>4.0.0</modelVersion>??????<groupId>batis</groupId>????<artifactId>batis</artifactId>????<version>0.0.1-SNAPSHOT</version>????<packaging>jar</packaging>??????<name>batis</name>????<url>http://maven.apache.org</url>??????<properties>??????<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>????</properties>??????<dependencies>??????<dependency>????????<groupId>junit</groupId>????????<artifactId>junit</artifactId>????????<version>3.8.1</version>????????<scope>test</scope>??????</dependency>????????<dependency>??????????????<groupId>org.mybatis</groupId>??????????????<artifactId>mybatis</artifactId>??????????????<version>3.2.7</version>??????</dependency>????????????<dependency>??????????<groupId>com.oracle</groupId>??????????<artifactId>ojdbc14</artifactId>??????????<version>10.2.0.4.0</version>??????</dependency>??????????</dependencies>??</project>?? ?5. 客戶端代碼:
?
[java]?view plaincopy print?
package?com.louis.mybatis.test;????import?java.io.InputStream;??import?java.util.HashMap;??import?java.util.List;??import?java.util.Map;????import?org.apache.ibatis.io.Resources;??import?org.apache.ibatis.session.SqlSession;??import?org.apache.ibatis.session.SqlSessionFactory;??import?org.apache.ibatis.session.SqlSessionFactoryBuilder;????import?com.louis.mybatis.model.Employee;????public?class?SelectDemo?{????????public?static?void?main(String[]?args)?throws?Exception?{??????????????????InputStream?inputStream?=?Resources.getResourceAsStream("mybatisConfig.xml");??????????SqlSessionFactoryBuilder?builder?=?new?SqlSessionFactoryBuilder();??????????SqlSessionFactory?factory?=?builder.build(inputStream);????????????????????????????SqlSession?sqlSession?=?factory.openSession();????????????????????????Map<String,Object>?params?=?new?HashMap<String,Object>();????????????????????params.put("min_salary",10000);??????????????????List<Employee>?result?=?sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary",params);??????????????????List<Employee>?result1?=?sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary");??????????System.out.println("薪資低于10000的員工數:"+result.size());??????????????????System.out.println("所有員工數:?"+result1.size());??????????????}????}?? ?
二、SqlSession 的工作過程分析:
?1. 開啟一個數據庫訪問會話---創建SqlSession對象:
?
[java]?view plaincopy print?
SqlSession?sqlSession?=?factory.openSession();?? ?
?????? ? ??MyBatis封裝了對數據庫的訪問,把對數據庫的會話和事務控制放到了SqlSession對象中。
?
??????
?
2. 為SqlSession傳遞一個配置的Sql語句 的Statement Id和參數,然后返回結果:
?
[java]?view plaincopy print?
List<Employee>?result?=?sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary",params);?? 上述的"com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary",是配置在
EmployeesMapper.xml?的Statement ID,params 是傳遞的查詢參數。
讓我們來看一下sqlSession.selectList()方法的定義:?
?
[java]?view plaincopy print?
public?<E>?List<E>?selectList(String?statement,?Object?parameter)?{????return?this.selectList(statement,?parameter,?RowBounds.DEFAULT);??}????public?<E>?List<E>?selectList(String?statement,?Object?parameter,?RowBounds?rowBounds)?{????try?{??????????MappedStatement?ms?=?configuration.getMappedStatement(statement);??????????List<E>?result?=?executor.query(ms,?wrapCollection(parameter),?rowBounds,?Executor.NO_RESULT_HANDLER);??????return?result;????}?catch?(Exception?e)?{??????throw?ExceptionFactory.wrapException("Error?querying?database.??Cause:?"?+?e,?e);????}?finally?{??????ErrorContext.instance().reset();????}??}?? MyBatis在初始化的時候,會將
MyBatis的配置信息全部加載到內存中,使用
org.apache.ibatis.session.Configuration實例來維護。使用者可以使用
sqlSession.getConfiguration()方法來獲取。MyBatis的配置文件中配置信息的組織格式和內存中對象的組織格式幾乎完全對應的。上述例子中的
?
?
[html]?view plaincopy print?
<select?id="selectByMinSalary"?resultMap="BaseResultMap"?parameterType="java.util.Map"?>????select???????EMPLOYEE_ID,?FIRST_NAME,?LAST_NAME,?EMAIL,?SALARY??????from?LOUIS.EMPLOYEES??????<if?test="min_salary?!=?null">??????????where?SALARY?<?#{min_salary,jdbcType=DECIMAL}??????</if>??</select>?? 加載到內存中會生成一個對應的
MappedStatement對象,然后會以
key="com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary"?,
value為
MappedStatement對象的形式維護到
Configuration的一個
Map中。當以后需要使用的時候,只需要通過
Id值來獲取就可以了。
從上述的代碼中我們可以看到SqlSession的職能是:
SqlSession根據Statement ID, 在mybatis配置對象Configuration中獲取到對應的MappedStatement對象,然后調用mybatis執行器來執行具體的操作。
?
3.MyBatis執行器Executor根據SqlSession傳遞的參數執行query()方法(由于代碼過長,讀者只需閱讀我注釋的地方即可):
?
[java]?view plaincopy print?
public?<E>?List<E>?query(MappedStatement?ms,?Object?parameter,?RowBounds?rowBounds,?ResultHandler?resultHandler)?throws?SQLException?{??????????????????BoundSql?boundSql?=?ms.getBoundSql(parameter);??????????CacheKey?key?=?createCacheKey(ms,?parameter,?rowBounds,?boundSql);??????return?query(ms,?parameter,?rowBounds,?resultHandler,?key,?boundSql);???}??????@SuppressWarnings("unchecked")????public?<E>?List<E>?query(MappedStatement?ms,?Object?parameter,?RowBounds?rowBounds,?ResultHandler?resultHandler,?CacheKey?key,?BoundSql?boundSql)?throws?SQLException?{??????ErrorContext.instance().resource(ms.getResource()).activity("executing?a?query").object(ms.getId());??????if?(closed)?throw?new?ExecutorException("Executor?was?closed.");??????if?(queryStack?==?0?&&?ms.isFlushCacheRequired())?{????????clearLocalCache();??????}??????List<E>?list;??????try?{????????queryStack++;????????list?=?resultHandler?==?null???(List<E>)?localCache.getObject(key)?:?null;????????if?(list?!=?null)?{??????????handleLocallyCachedOutputParameters(ms,?key,?parameter,?boundSql);????????}?else?{??????????????????list?=?queryFromDatabase(ms,?parameter,?rowBounds,?resultHandler,?key,?boundSql);????????}??????}?finally?{????????queryStack--;??????}??????if?(queryStack?==?0)?{????????for?(DeferredLoad?deferredLoad?:?deferredLoads)?{??????????deferredLoad.load();????????}????????deferredLoads.clear();???????if?(configuration.getLocalCacheScope()?==?LocalCacheScope.STATEMENT)?{??????????clearLocalCache();???????}??????}??????return?list;????}???private?<E>?List<E>?queryFromDatabase(MappedStatement?ms,?Object?parameter,?RowBounds?rowBounds,?ResultHandler?resultHandler,?CacheKey?key,?BoundSql?boundSql)?throws?SQLException?{??????List<E>?list;??????localCache.putObject(key,?EXECUTION_PLACEHOLDER);??????try?{????????????????????????list?=?doQuery(ms,?parameter,?rowBounds,?resultHandler,?boundSql);??????}?finally?{????????localCache.removeObject(key);??????}??????localCache.putObject(key,?list);??????if?(ms.getStatementType()?==?StatementType.CALLABLE)?{????????localOutputParameterCache.putObject(key,?parameter);??????}??????return?list;????}?? [java]?view plaincopy print?
??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.<E>query(stmt,?resultHandler);??????}?finally?{????????closeStatement(stmt);??????}????}?? 上述的
Executor.query()方法幾經轉折,最后會創建一個
StatementHandler對象,然后將必要的參數傳遞給
StatementHandler,使用
StatementHandler來完成對數據庫的查詢,最終返回
List結果集。
從上面的代碼中我們可以看出,Executor的功能和作用是:
(1、根據傳遞的參數,完成SQL語句的動態解析,生成BoundSql對象,供StatementHandler使用;
(2、為查詢創建緩存,以提高性能(具體它的緩存機制不是本文的重點,我會單獨拿出來跟大家探討,感興趣的讀者可以關注我的其他博文);
(3、創建JDBC的Statement連接對象,傳遞給StatementHandler對象,返回List查詢結果。
4. StatementHandler對象負責設置Statement對象中的查詢參數、處理JDBC返回的resultSet,將resultSet加工為List 集合返回:
????? 接著上面的Executor第六步,看一下:prepareStatement()?方法的實現:
?
[java]?view plaincopy print?
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);?????private?Statement?prepareStatement(StatementHandler?handler,?Log?statementLog)?throws?SQLException?{??????Statement?stmt;??????Connection?connection?=?getConnection(statementLog);??????stmt?=?handler.prepare(connection);??????????handler.parameterize(stmt);??????return?stmt;????}?? ?
????? 以上我們可以總結StatementHandler對象主要完成兩個工作:
???????? (1. 對于JDBC的PreparedStatement類型的對象,創建的過程中,我們使用的是SQL語句字符串會包含 若干個? 占位符,我們其后再對占位符進行設值。
StatementHandler通過parameterize(statement)方法對Statement進行設值;???????
???????? (2.StatementHandler通過List<E> query(Statement statement, ResultHandler resultHandler)方法來完成執行Statement,和將Statement對象返回的resultSet封裝成List;
?
5.?? StatementHandler 的parameterize(statement) 方法的實現:
?
[java]?view plaincopy print?
public?void?parameterize(Statement?statement)?throws?SQLException?{??????????parameterHandler.setParameters((PreparedStatement)?statement);????}?? [java]?view plaincopy print?
public?void?setParameters(PreparedStatement?ps)?throws?SQLException?{????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)?{??????????Object?value;??????????String?propertyName?=?parameterMapping.getProperty();??????????if?(boundSql.hasAdditionalParameter(propertyName))?{???????????value?=?boundSql.getAdditionalParameter(propertyName);??????????}?else?if?(parameterObject?==?null)?{????????????value?=?null;??????????}?else?if?(typeHandlerRegistry.hasTypeHandler(parameterObject.getClass()))?{????????????value?=?parameterObject;??????????}?else?{????????????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();??????????????????typeHandler.setParameter(ps,?i?+?1,?value,?jdbcType);????????}??????}????}??}?? 從上述的代碼可以看到,StatementHandler 的parameterize(Statement) 方法調用了 ParameterHandler的setParameters(statement) 方法,
ParameterHandler的setParameters(Statement)方法負責 根據我們輸入的參數,對statement對象的 ? 占位符處進行賦值。
?
?
?
6.?? StatementHandler 的List<E> query(Statement statement, ResultHandler resultHandler)方法的實現:
?
?
[java]?view plaincopy print?
??public?<E>?List<E>?query(Statement?statement,?ResultHandler?resultHandler)?throws?SQLException?{?????PreparedStatement?ps?=?(PreparedStatement)?statement;?????ps.execute();????????return?resultSetHandler.<E>?handleResultSets(ps);???}?? [java]?view plaincopy print?
public?List<Object>?handleResultSets(Statement?stmt)?throws?SQLException?{??????final?List<Object>?multipleResults?=?new?ArrayList<Object>();????????int?resultSetCount?=?0;??????ResultSetWrapper?rsw?=?getFirstResultSet(stmt);????????List<ResultMap>?resultMaps?=?mappedStatement.getResultMaps();??????int?resultMapCount?=?resultMaps.size();??????validateResultMapsCount(rsw,?resultMapCount);????????????while?(rsw?!=?null?&&?resultMapCount?>?resultSetCount)?{????????ResultMap?resultMap?=?resultMaps.get(resultSetCount);??????????????????????handleResultSet(rsw,?resultMap,?multipleResults,?null);????????rsw?=?getNextResultSet(stmt);????????cleanUpAfterHandlingResultSet();????????resultSetCount++;??????}????????String[]?resultSets?=?mappedStatement.getResulSets();??????if?(resultSets?!=?null)?{????????while?(rsw?!=?null?&&?resultSetCount?<?resultSets.length)?{??????????ResultMapping?parentMapping?=?nextResultMaps.get(resultSets[resultSetCount]);??????????if?(parentMapping?!=?null)?{????????????String?nestedResultMapId?=?parentMapping.getNestedResultMapId();????????????ResultMap?resultMap?=?configuration.getResultMap(nestedResultMapId);????????????handleResultSet(rsw,?resultMap,?null,?parentMapping);??????????}??????????rsw?=?getNextResultSet(stmt);??????????cleanUpAfterHandlingResultSet();??????????resultSetCount++;????????}??????}????????return?collapseSingleResultList(multipleResults);????}?? 從上述代碼我們可以看出,StatementHandler?的List<E> query(Statement statement, ResultHandler resultHandler)方法的實現,是調用了ResultSetHandler的handleResultSets(Statement)?方法。ResultSetHandler的handleResultSets(Statement)?方法會將Statement語句執行后生成的resultSet?結果集轉換成List<E>?結果集:
?
[java]?view plaincopy print?
??public?List<Object>?handleResultSets(Statement?stmt)?throws?SQLException?{????final?List<Object>?multipleResults?=?new?ArrayList<Object>();??????int?resultSetCount?=?0;????ResultSetWrapper?rsw?=?getFirstResultSet(stmt);??????List<ResultMap>?resultMaps?=?mappedStatement.getResultMaps();????int?resultMapCount?=?resultMaps.size();????validateResultMapsCount(rsw,?resultMapCount);????????while?(rsw?!=?null?&&?resultMapCount?>?resultSetCount)?{??????ResultMap?resultMap?=?resultMaps.get(resultSetCount);????????????????handleResultSet(rsw,?resultMap,?multipleResults,?null);??????rsw?=?getNextResultSet(stmt);??????cleanUpAfterHandlingResultSet();??????resultSetCount++;????}??????String[]?resultSets?=?mappedStatement.getResulSets();????if?(resultSets?!=?null)?{??????while?(rsw?!=?null?&&?resultSetCount?<?resultSets.length)?{????????ResultMapping?parentMapping?=?nextResultMaps.get(resultSets[resultSetCount]);????????if?(parentMapping?!=?null)?{??????????String?nestedResultMapId?=?parentMapping.getNestedResultMapId();??????????ResultMap?resultMap?=?configuration.getResultMap(nestedResultMapId);??????????handleResultSet(rsw,?resultMap,?null,?parentMapping);????????}????????rsw?=?getNextResultSet(stmt);????????cleanUpAfterHandlingResultSet();????????resultSetCount++;??????}????}??????return?collapseSingleResultList(multipleResults);??}?? ?
?
由于上述的過程時序圖太過復雜,就不貼出來了,讀者可以下載MyBatis源碼, 使用Eclipse、Intellij IDEA、NetBeans 等IDE集成環境創建項目,Debug MyBatis源碼,一步步跟蹤MyBatis的實現,這樣對學習MyBatis框架很有幫助~
轉載于:https://www.cnblogs.com/davidwang456/p/6634241.html
總結
以上是生活随笔為你收集整理的MyBatis的架构设计以及实例分析--转的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。