【原創】Mybaitis生命週期原始碼解析-XML配置啟動--轉載請註明出處
一、準備基本程式碼
注:本文的一切內容都是基於XML配置啟動進行的分析,不適用與Spring-mybatis組合使用場景。
1.建立基本類
package com.zhou; import com.zhou.mapper.BlogMapper; import com.zhou.pojo.Blog; 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 java.io.IOException; import java.io.InputStream; import java.util.ArrayList; public class Demo1SessionFactory { public static void main(String[] args) throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); //從 XML 中構建 SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session = sqlSessionFactory.openSession(); try { BlogMapper mapper = session.getMapper(BlogMapper.class); Blog blog = mapper.selectBlog(1, new ArrayList<>()); System.out.println(blog.getId()); blog = mapper.selectBlog(10, new ArrayList<>()); System.out.println(blog.getId()); } finally { session.close(); } } }
2.建立基本的xml配置檔案
<?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> <!-- 引入外部資原始檔 --> <!-- 設定駝峰匹配 --> <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <!-- 設定包掃描(別名) <typeAliases> <package name="com.zhou.pojo"/> </typeAliases>--> <!-- 配置環境:可以配置多個環境,default:配置某一個環境的唯一標識,表示預設使用哪個環境 --> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <!-- 配置連線資訊 --> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://192.168.1.251:3306/welian"/> <property name="username" value="welian"/> <property name="password" value="welian"/> </dataSource> </environment> </environments> <!-- 配置對映檔案:用來配置sql語句和結果集型別等 --> <mappers> <mapper resource="BlogMapper.xml"/> </mappers> </configuration>
3.建立查詢sql的mapper.xml檔案
<?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.zhou.mapper.BlogMapper"> <select id="selectBlog" parameterType="java.lang.Integer" resultType="com.zhou.pojo.Blog"> SELECT <if test="id != null"> <foreach collection="ids" item="item"> 1 AS str, </foreach> #{id} AS id </if> </select> </mapper>
4.根據上方的mapper.xml建立對應的POJO類以及mapper介面。
package com.zhou.pojo;
public class Blog {
private Integer id;
private String str;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
package com.zhou.mapper;
import java.util.List;
import com.zhou.pojo.Blog;
import org.apache.ibatis.annotations.Param;
public interface BlogMapper {
Blog selectBlog(@Param("id") Integer id, @Param("ids") List<Integer> ids);
}
二、SqlSessionFactory的構建
在進行構建sqlSessionFactory時,進行了以下的兩步主要操作
1.讀取config配置的資訊到快取中,並將其設定到Configuration類。在這個示例中,我們採用的是xml配置的方式因此,在載入配置時會使用xml的解析器解析配置檔案中的資訊,使用校驗檔案來進行校驗輸入配置引數的有效性,然後建立一個xml的dom物件。
2.將mapper的sql描述資訊新增到Configuration類中。然後使用讀取出來的xml資訊快取,呼叫XMLConfigBuilder格式化工具,來建立基本的mybatis的Configuration類資訊。在完成了基本配置後,將mapper.xml中的資訊讀取出來,然後進行結構化處理,再儲存到Configuration的mappedStatements中。
在mappedStatements中的資料結構,以下方的資料結構進行儲存:
由上方的debug資訊可以看出,這裡將每一個sql,拆分成了多個不同的節點,並且每一個標籤均為不同的節點型別,節點原本的屬性資訊,都已經被轉換為了物件的屬性。利用這些屬性,就可以迅速的將sql加載出來。
三、Mapper例項的構建與呼叫
1.建立SqlSession。在呼叫SqlSessionFactory進行開啟SqlSession時,首先建立了一個SqlSession物件,然後像這個物件中添加了executor的資訊。但是要注意的是,在一開始開啟SqlSession的時候,並沒有進行資料庫連線。
下方的debug資訊,是執行查詢前的SqlSession中的屬性資訊,可以看到在executor物件下的transaction物件中,雖然具有dataSource物件但是並沒有connection物件,即資料庫連線。
在執行過一次查詢以後,便擁有了一個數據庫連線在executor物件中,而之後,在這個session提交前,都會使用這個connection來進行sql的執行了。
2.利用SqlSession開啟一個MapperProxy類物件。
首先從SqlSession物件中,獲取到Configuration的物件,然後從該物件的MapperRegistry中獲取相應的類的描述資訊,再使用MapperProxyFactory構造出響應的MapperProxy物件,再從該物件中獲取mapper介面的實現類返回給呼叫端。
DefaultSqlSession類中的getMapper方法,這裡在getMapper的時候,將SqlSession自身作為引數傳入了,所以在每個MapperProxy物件中都是具有一個SqlSession物件資訊的。
MapperRegistry類中的getMapper方法:
MapperProxyFactory類中,進行構造MapperProxy物件的方法:
3.執行查詢時,是利用java的反射來對類進行的實現,利用反射來實現類以後,再使用這個物件來呼叫sqlSession來執行查詢操作。
首先呼叫MapperProxy類的invoke方法,該方法中,首先利用method來構建出所需要使用的MapperMethod類物件,這個類中,只會包含基本的method描述,如入參出參等。
在構造完成MapperMethod資訊後,MapperMethod會使用MapperProxy類中持有的sqlSession物件,來執行sql。在這裡查詢的結果只有一個物件,因此使用的是下圖中的方式來執行sql。
最後再進行實際的sql執行,返回查詢到的資料。
四、總結
1.初始化
mybatis在進行初始化時,首先會由SqlSessionFactoryBuilder來建立SqlSessionFactory,在建立成功後,SqlSessionFactoryBuilder物件就會結束生命週期。SqlSessionFactory中,持有mybatis的Config資訊,而Config資訊中,除mybatis本身的資訊外,還持有資料庫連線池,以及mapper.xml中的資訊,這些資訊都在mybatis進行初始化時進行結構化快取到了Configuration類中。其中,mapper.xml中的資訊,被進行了良好的節點分割,每一個標籤都被分割成獨立的節點,來供mybatis進行快速組裝sql資訊。
2.執行sql
在進行sql的執行時,首先需要的是開啟SqlSession,SqlSession由SqlSessionFactory進行建立。開啟SqlSession後,由SqlSession來進行mapper的資訊初始化,所使用的則是MapperRegistry類,這個類在每次完成mapper的初始化後,就會結束生命週期,在反覆建立mapper時,會需要多次的建立MapperRegistry物件。mapper物件構建完成後,在mapper中,不會直接具有JdbcConnection物件,而是引用了SqlSession物件。在SqlSession中,採用的是懶漢的單例模式來處理JdbcConnection,以此來達到事物處理的效果。