MyBatis源码解析
1. MyBatis主要核心部件:
Configuration
包含MyBatis所有的配置信息,除此之外还负责穿件一些MyBatis内部使用对象;如:Executor等;
SqlSession
与数据库交互的会话,MyBatis顶层API,完成增删改查功能;如:selectOne、selectList、update、delete等方法;
StatementHandler
封装了JDBC Statement操作,负责对JDBC Statement操作;负责设置参数,将JDBC Statement结果集转换成指定对象;ParameterHandler
负责将用户传递的参数转换成JDBC Statement所需要的参数;ResultSetHandler
负责将JDBC返回的ResultSet结果集对象转换成List类型集合;
TypeHandler
负责JAVA数据类型与JDBC数据类型只拿的映射与转换;
MappedStatement
MappedStatement维护了一条select|update|delete|insert节点的封装【简单点讲就是一条sql的配置信息】;SqlSource
负责根据用户传递的阐述信息,动态生成sql语句,封装到BoundSql对象中;BoundSql
表示动态生成的sql语句,以及相应的参数信息;
下图为MyBatis类层次结构,摘自网上
2. MyBatis初始化过程–Configuration
2.1. 初始化方式:
a. xml:将MyBatis的所有配置信息放在xml中,MyBatis通过加载xml配置文件,将配置文件组装成Configuration对象;如:
<?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>
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="false"/>
</settings>
<typeAliases>
<typeAlias type="cn.test.mybatis.App" alias="App"/>
</typeAliases>
<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/auth" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="cn/AppMapper.xml"/>
</mappers>
</configuration>
b.java api: 跳开xml配置,通过手动创建Configuration对象,针对配置项set进入Configuration对象;【不推荐】
2.2. 基于XML创建Configuration对象过程
简单运行demo, conf.xml见上节所示:
package cn.test.mybatis;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.InputStream;
public class TestMain {
public static void main(String[] args) {
InputStream is = TestMain.class.getResourceAsStream("/conf.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession();
App app = null;
try {
// AppDao appDao = sqlSession.getMapper(AppDao.class);
// app = appDao.getById(1l);
String statement = "cn.test.mybatis.dao.AppDao.getById";
app = sqlSession.selectOne(statement, 1l);
System.out.println(app);
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
}
Configuration创建时序图,来源网上:
整个Configuration初始化从SqlSessionFactory的build方法开始,调用XMLConfigBuilder的parse方法,构建Configuration对象,也就是MyBatis的配置信息:
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.parse解析xml文件的
configuration
节点下settings
、properties
、typeAliases
、plutins
、enviroments
、typeHandlers
、mappers
等子节点;PS:用XMLMapperBuilder来解析mappers
节点,其中包含MappedStatement
信息;
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
Properties settings = settingsAsPropertiess(root.evalNode("settings"));
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
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);
}
}
- 将xml解析出的配置设置到Configuration对象中,以
typeAliases
为例:
private void typeAliasesElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String typeAliasPackage = child.getStringAttribute("name");
configuration.getTypeAliasRegistr