用了好几年的mybatis了,但是很少来钻研mybatis原理所在,最近抽出空来,就把这一整套源码都研究了下,然后发现就是这些东西,mybatis没啥难度,于是决定把研究的这一整套写一个mybatis系列,记录一下,在这些完了以后,顺便写一个小的mybatis框架。
还是从用法开始吧,就不开始就从源码说了,mybatis的conf配置文件不再多讲,这里就是定义了一个domain,对应mapper文件,main程序,具体如下:
Domain
package cn.com.domain;
/**
* @author xiaxuan
* @date 2018/4/10.
*/
public class User {
private Long id;
private String username;
private String password;
private Integer age;
private String mobile;
private String hotelAddress;
....
}
Mapper
import cn.com.domain.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.ResultMap;
import org.apache.ibatis.annotations.Select;
/**
* @author xiaxuan
* @date 2018/4/10.
*/
public interface UserMapper {
User getUser(int id);
@ResultMap("BaseResultMap")
@Select("select id, username, password, age, mobile, hotel_address from user where id=#{id};")
User getUser2(@Param("id") Long id);
void updateUser(@Param("age") Integer age, @Param("id") Long id);
void save(User user);
void deleteById(@Param("id") Long id);
}
xml文件:
<mapper namespace="cn.com.mapper.UserMapper">
<resultMap id="BaseResultMap" type="cn.com.domain.User">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<result column="age" property="age"/>
<result column="mobile" property="mobile"/>
<result column="hotel_address" property="hotelAddress"/>
resultMap>
<select id="getUser" parameterType="int" resultMap="BaseResultMap">
select id, username, password, age, mobile, hotel_address from user where id=#{id}
select>
<update id="updateUser">
update user set age = #{age} WHERE id = #{id};
update>
<insert id="save">
insert into user(username, password, age, mobile, hotel_address) values(#{username}, #{password}, #{age}, #{mobile}, #{hotelAddress});
insert>
<delete id="deleteById" parameterType="java.lang.Long">
delete from user where id = #{id};
delete>
mapper>
想了想还是把conf文件也贴出来,因为等会分析时需要对照conf文件来分析,conf如下:
main程序如下:
public class Main {
public static void main(String[] args) {
//mybatis的配置文件
String resource = "conf.xml";
//使用类加载器加载mybatis的配置文件(它也加载关联的映射文件)
InputStream is = Main.class.getClassLoader().getResourceAsStream(resource);
//构建sqlSession的工厂
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = sessionFactory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
//执行查询返回一个唯一user对象的sql
User user = userMapper.getUser(1);
System.out.println(user);
User user2 = userMapper.getUser2(1L);
System.out.println(user2);
userMapper.updateUser(23, 1L);
User user3 = userMapper.getUser2(1L);
System.out.println(user3);
session.close();
}
}
以上就是一个demo的完整演示,但是运行结果就不再贴出来了,毕竟不是使用指南,下面就就上面的演示程序具体讲解mybatis源码。
接下来开始对mybatis源码进行分析与讲解,首先说下配置文件是如何加载的,这里最主要的就是上图main程序中的前几行代码,如下:
//mybatis的配置文件
String resource = "conf.xml";
//使用类加载器加载mybatis的配置文件(它也加载关联的映射文件)
InputStream is = Main.class.getClassLoader().getResourceAsStream(resource);
//构建sqlSession的工厂
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
将xml配置文件转换成stream流后,解析stream生成SqlsesssionFactory对象,进入到build源码中,如下:
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
这里的XMLConfigBuilder无非是把stream转换成XML解析对象,这一步跳过,重点在于**parser.parse()**方法,进入到其方法中。
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
在parseConfiguration(parser.evalNode("/configuration"))这行中,首先获得了configuration节点,这个就对应上了上面配置文件conf.xml中的configuration配置,在获取configuration节点后开始解析configuration中内容,进入到parseConfiguration方法。
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
这里开始就是对各种配置的解析,后面对properties、environments、甚至是重点的mappers的配置解析太多,单独放在这一篇里面未免篇幅过长,我这把配置解析分成两部分,这一部分源码讲解放在下一篇中。