分析目標代碼
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是如何加載配置文件進行初始化的
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。
與此同時,也會根據***<mapper resource=“mapper/UserMapper.xml”/>***初始化SQL語句,將其保存在Configuration的mappedStatements對象中,該對象是一個Map,key爲namespace+id(com.ttsource.config.mapper.UserMapper.selectUserInfo)。
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的mapper文件中,對SQL的描述關鍵字都被稱爲SqlNode,根節點稱爲rootSqlNode。這個rootSqlNode可以是純粹的SQL稱爲StaticTextSqlNode, 可以是判斷判斷語句<if id != null>id = #{id}</if>稱爲IfSqlNode,凡是SQL中的關鍵詞都對應着一種SqlNode。
通過執行對應的SqlNode中apply()方法將翻譯到的sql語句拼接在DynamicSqlSource的sqlBuilder(StringJoiner)中,從而完成動態SQL的生成。