背景
我们在开发过程中,常常不会直接通过mysql-client 直接去和数据库交互,
而会采用一些第三方的类orm工具包去简化操作流程,mybatis
是一款敏捷度非常高的数据访问层工具。
结构
version:3.5.3
部分其他功能就没有画出了,比如反射,事务,插件,语言等。
暴露接口
很多时候我们都不会脱离spring去使用mybatis,但是今天我们重点在分析流程上面,所以单独去运行它,要单独运行mybatis需要配置信息,mapper接口,mapper xml映射 例子如下。
用例
public static void main(String[] args) throws IOException {
//获取配置资源
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
//构建sqlsessionfactory
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory build = sqlSessionFactoryBuilder.build(resourceAsStream);
//获取会话session 之后调用数据库
SqlSession sqlSession = build.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<UserInfo> list = mapper.list();
System.out.println(JSON.toJSONString(list));
}
mybatis-config.xml
<?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://127.0.0.1:3306/db_user?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>
</configuration>
UserMapper.xml
<?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.poizon.study.sb.mybatis.UserMapper">
<select id="list" resultType="com.poizon.study.sb.mybatis.UserInfo">
SELECT * from user_info;
</select>
</mapper>
UserMapper.java , UserInfo.java
public interface UserMapper {
@Select("SELECT * from user_info;")
List<UserInfo> list();
}
@Data
public class UserInfo {
private Long id;
private String username;
private Date createAt;
}
案例分析
案例中我们涉及的主要对象有SqlSessionFactory、SqlSession,主要方法有sqlSessionFactoryBuilder.build()、build.openSession()、sqlSession.getMapper(),在分析之前我们根据之前分析源码的经验,在框架启动过程中都会进行配置文件加载,没有build()启到的作用便是配置加载,下面给的是一张配置映射图(包含mybatis-config,与mapper.xml)。
对应关系还是很清晰的,
configuration 在其中充当了配置中心的角色,和项目中容器的功能,下图是执行流程。
应用层通过SqlSessionFactory 获取到SqlSession会话,会话通过Executor执行器去执行具体的调用,调用层和返回层通过StatementHandler 进行参数和返回的包装。
配置文件解析
mybatis 解析配置文件采用的是DOM解析,(一般有4种解析方式dom,sax,dom4j,jdom),如果文件大很多专业框架都采用dom4j,因为dom解析会加载整个文档,如果文档太大会导致内存溢出问题,我们从入口SqlSessionFactoryBuilder#build() 进去。
public SqlSessionFactory build( inputStream, environment, properties) {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());//继续点进去
}
org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration
private void parseConfiguration(XNode root) {
//...
typeAliasesElement(root.evalNode("typeAliases"));
mapperElement(root.evalNode("mappers"));
}
这里解析的子标签很多,我们找2个具有代表性的分析下,typeAliases,语义是指类型别名,我们在配置文件中可以这样配置<typeAliases><typeAlias type="com.poizon.study.sb.mybatis.UserInfo" alias="myUserType" /></typeAliases>,这样在解析文件的时候便会走到此解析流程中,代表的含义是,类型UserInfo 可以简写成 myUserType,我们经常用在Mapper的resultType, parameterType 中运用比较多,需要时还可以以包的维度去配置 <typeAliases><package name=""/></typeAliases>。
#org.apache.ibatis.builder.xml.XMLConfigBuilder#typeAliasesElement
private void typeAliasesElement(XNode parent) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
//...
} else {
String alias = child.getStringAttribute("alias");
String type = child.getStringAttribute("type");
Class<?> clazz = Resources.classForName(type);
typeAliasRegistry.registerAlias(alias, clazz);
}
}
}
#org.apache.ibatis.type.TypeAliasRegistry#registerAlias()
public void registerAlias(String alias, Class<?> value) {
String key = alias.toLowerCase(Locale.ENGLISH);
typeAliases.put(key, value);
}
最后将别名存储到TypeAliasRegistry 的map中,大家可以点看这个类看看,里面内置了很多基础数据类型的别名,比如string=>String.class、date=>Date.class,等到要查询时候通过resolveAlias(string) 返回对应类型,可以通过getTypeAliases() 返回所有的别名列表,这里要注意的是返回的map是不可写的。
public Map<String, Class<?>> getTypeAliases() {
return Collections.unmodifiableMap(typeAliases);
}