分析mybatis是如何工作的

分析目標代碼

public static void main(String[] args) {
		SqlSession sqlSession = null;
        try {
            // mybatis的配置文件
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            // 根據配置文件初始化mybatis
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            // 獲取SqlSession
            sqlSession = sqlSessionFactory.openSession();
            // 從sqlSession中獲取mapper
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            // 執行相應的sql
            HashMap<String, Object> userInfo = userMapper.selectUserInfo(1);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        	if (sqlSession != null) {
        		sqlSession.close();
        	}
        }
    }
<?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="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/test"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="mapper/UserMapper.xml"/>
  </mappers>
</configuration>
<?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.ttsource.config.mapper.UserMapper">
    <select id="selectUserInfo" resultType="hashmap">
        select * from admin_user where id = #{id}
    </select>
</mapper>
public interface UserMapper {
    HashMap<String, Object> selectUserInfo(@Param("id") Integer userId);
}

mybatis中的配置文件

!DOCTYPE描述文檔類型,告訴解析器通過哪種方式(此處dtd)解析文件。同時規定了在文件中可使用的節點。比如說mybatis的配置文件中的DOCTYPE是mybatis-3-config.dtd,那可使用的節點有dataSource/environment等節點,而不能使用select/insert/update等節點

mybatis是如何加載配置文件進行初始化的

SqlSessionFactory初始化和創建

mybatis中執行任何的SQL都是通過SqlSession進行的,SqlSession是非線程安全的,通常情況下在每次執行相應的SQL時都會通過SqlSessionFactory創建一個SqlSession。而SqlSessionFactory是由SqlSessionFactroyBuilder通過配置文件構建的。

SqlSessionFactoryBuilder通過Sax解析將mybatis的配置文件中配置的信息加載至Configuration中,然後通過Configuration配置信息創建DefaultSqlSessionFactory對象。至此SqlSessionFactoryBuilder的使命結束,關閉讀取配置文件的流,此後維護創建的SqlSessionFactory實例即可。

mybatis是如何通過接口找到對應的xml文件進行執行的

在mybatis框架中,執行SQL語句通常是採用如下的方式

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> userInfo = userMapper.selectUserInfo(1);

在上面的代碼中,通過SqlSession獲取UserMapper的動態代理,下面執行UserMapper的selectUserInfo()方法其實是通過UserMapper的動態代理MapperProxy來執行的。

UserMapper的動態代理是由MapperProxyFactory創建的。在初始化過程中,根據
<mapper resource=“mapper/UserMapper.xml”/> 獲取到UserMapper.xml中定義的namespace com.ttsource.config.mapper.UserMapper,由namespace指定的class創建MapperProxyFactory,並將其存於Configuration的mapperRegistry屬性中。mapperRegistry中含有一個Map用於存儲MapperProxyFactory,對應的key爲UserMapper.class。

MapperProxy的創建過程

與此同時,也會根據***<mapper resource=“mapper/UserMapper.xml”/>***初始化SQL語句,將其保存在Configuration的mappedStatements對象中,該對象是一個Map,key爲namespace+id(com.ttsource.config.mapper.UserMapper.selectUserInfo)。
Mapper接口如何與SQL關聯的-1

SqlSession執行getMapper()方法時,就會通過UserMapper.class從Configuration中取出對應的MapperProxyFactory來創建UserMapper的代理MapperProxy。

調用userMapper的selectUserInfo()方法,其實執行對應的MapperProxy中的invoke()。在invoke()方法中,根據被代理類(UserMapper.class)調用的方法(selectUserInfo())拼接成獲取執行sql的ID,從而接口中定義的方法與xml中執行的SQL語句就對應了。

如何解析xml文件構建sql語句進行執行的

這個問題也可說爲mybatis的動態SQL是如何實現的。爲了分析這個過程,將上述的代碼做如下調整:

<?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.ttsource.config.mapper.UserMapper">
    <select id="selectUserInfo" resultType="hashmap">
        select * from admin_user
        <trim prefix="where" prefixOverrides="OR |AND">
            <if test="id != null">and id=#{id}</if>
            <if test="nickName != null">and nick_name=#{nickName}</if>
        </trim>
    </select>
</mapper>
public interface UserMapper {
    HashMap<String, Object> selectUserInfo(@Param("id") Integer userId, @Param("nickName") String nickName);
}

從數據庫中獲取用戶信息時,將提供兩個參數,分別爲id和nick_name,任何一個參數或兩個參數都存在都可以得到結果。那這個過程是如何做到的呢!

上述已經提過,執行SQL的入口是在代理MapperProxy的invoke()方法中。在invoke()方法中創建MapperMethod並執行它的execute()方法,最終到達DynamicSqlSource的getBoundSql()方法中,
而getBoundSql()方法的執行過程就是動態的創建SQL。

Mybatis動態生成SQL執行

在mybatis的mapper文件中,對SQL的描述關鍵字都被稱爲SqlNode,根節點稱爲rootSqlNode。這個rootSqlNode可以是純粹的SQL稱爲StaticTextSqlNode, 可以是判斷判斷語句<if id != null>id = #{id}</if>稱爲IfSqlNode,凡是SQL中的關鍵詞都對應着一種SqlNode。

通過執行對應的SqlNode中apply()方法將翻譯到的sql語句拼接在DynamicSqlSource的sqlBuilder(StringJoiner)中,從而完成動態SQL的生成。

執行的結果是如何返回的(待更新)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章