分析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的生成。

执行的结果是如何返回的(待更新)

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