分析目标代码
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的生成。