// # 1.读取配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// # 2.创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// # 3.通过sqlSessionFactory创建SqlSession
try (SqlSession session = sqlSessionFactory.openSession()) {
// # 4.通过SqlSession获取Mapper,并查询
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
}
以上是Mybatis官网的demo,这次DB查询操作基本分成了上述4个步骤。
那么问题来了:在一次DB查询过程,mybatis框架具体做了什么?
直接给出结论:
1.读取mybatis-config.xml并解析,将全部配置信息和各种默认配置加载到Configuration中,包含:数据源信息(分装在Environment)、类型处理器、类型别名、mapper代理工厂等。
Configuration是SqlSessionFactory的重要属性。2.通过SqlSessionFactory创建transaction、executor、sqlSession。
3.通过sqlSession获取mapper,本质是通过mapper代理工厂创建mapper代理。mapper的crud方法实际执行的是MapperProxy的相关方法。
在这些方法中,sql会交由executor执行,而executor最终会调用jdbc的statement。
接下来通过源码分析demo中的四步。
一、读取配置文件
// == A.mybatis配置文件转换成stream
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
配置文件读取部分没什么可说的,无非把File转换成IO流。
我们把目光聚焦于mybatis-config.xml
文件本身。
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/BlogMapper.xml"/>
</mappers>
</configuration>
可以看到,最外层是一个<configuration>
标签,里面可加入很多配置,如:
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
environments(环境配置)
environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
了解这些就可以了,后面几步才是重点。
二、创建SqlSessionFactory
// == B.初始化Configuration,并加载各类默认配置(主要是类型转换器和别名映射)
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream)
org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties)
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
// ## 1.解析 mybatis-config.xml,同时创建 Configuration 对象
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// ## 2.解析XML,最终返回一个 DefaultSqlSessionFactory
return build(parser.parse());
}
1.解析 mybatis-config.xml
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
// 构造函数完成了Configuration的初始化,并将configuration的两个重要属性传递过来
super(new Configuration());
⬇⬇⬇⬇⬇
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
⬆⬆⬆⬆⬆
super(new Configuration());
}
观察传递过来的两个属性
### org.apache.ibatis.session.Configuration类 ###
// 类型处理注册器
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this);
// 类型别名注册器
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
分别查看它们的构造函数。
- 类型处理注册器TypeHandlerRegistry
为特定类型创建处理器
org.apache.ibatis.type.TypeHandlerRegistry#TypeHandlerRegistry{
register(Boolean.class, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
register(Byte.class, new ByteTypeHandler());
register(JdbcType.TINYINT, new ByteTypeHandler());
register(Short.class, new ShortTypeHandler());
register(JdbcType.SMALLINT, new ShortTypeHandler());
}
- 类型别名注册器TypeAliasRegistry
为基本类型起别名
org.apache.ibatis.type.TypeAliasRegistry#TypeAliasRegistry{
registerAlias("byte", Byte.class);
registerAlias("long", Long.class);
registerAlias("short", Short.class);
registerAlias("int", Integer.class);
}
- 再观察Configuration的构造函数
org.apache.ibatis.session.Configuration#Configuration()
// 构造函数做各种别名设置
public Configuration() {
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
}
2.解析XML
这一步做了很多事,篇幅关系不能全部分析。
org.apache.ibatis.builder.xml.XMLConfigBuilder#parse
org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration
private void parseConfiguration(XNode root) {
// 别名解析
typeAliasesElement(root.evalNode("typeAliases"));
// 注册插件(过滤器,如PageHelper)
pluginElement(root.evalNode("plugins"));
// 对象工厂解析
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
// 属性赋值(设置默认属性)
settingsElement(settings);
{
configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
}
// 初始化environment,设置事务处理工厂、数据源
environmentsElement(root.evalNode("environments"));
{
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 自定义类型转换器
typeHandlerElement(root.evalNode("typeHandlers"));
// ### 解析mapper文件
mapperElement(root.evalNode("mappers"));
}
重点看一下“解析mapper文件”部分:
org.apache.ibatis.session.Configuration#addMappers(java.lang.String)
org.apache.ibatis.binding.MapperRegistry#addMappers(java.lang.String)
org.apache.ibatis.binding.MapperRegistry#addMappers(java.lang.String, java.lang.Class<?>)
// == 存放Mapper(代理)工厂
org.apache.ibatis.binding.MapperRegistry#addMapper{
knownMappers.put(type, new MapperProxyFactory<>(type));
}
最终会将Mapper代理工厂存放在一个map集合中,把“mapper定义的接口类”作为key。
三、通过sqlSessionFactory创建SqlSession
// == D. 创建session,DefaultSqlSession
SqlSession session = sqlSessionFactory.openSession()
org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSession()
org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource
{
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 创建Executor,默认SimpleExecutor
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
}
最终创建的是DefaultSqlSession,它封装了configuration和executor。
四、通过SqlSession获取Mapper,并查询
// == E. 获取mapper,通过MapperProxyFactory创建代理对象
BlogMapper mapper = session.getMapper(BlogMapper.class);
org.apache.ibatis.session.defaults.DefaultSqlSession#getMapper
org.apache.ibatis.binding.MapperRegistry#getMapper
{
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
return mapperProxyFactory.newInstance(sqlSession);
org.apache.ibatis.binding.MapperProxyFactory
#newInstance(org.apache.ibatis.binding.MapperProxy<T>)
{
// java方式创建代理对象
Proxy.newProxyInstance(
mapperInterface.getClassLoader(),
new Class[] { mapperInterface },
mapperProxy);
}
}
最终得到的mapper,其实是一个由MapperProxyFactory创建的代理;而代理类的InvocationHandler指向了MapperProxy。
那么我们直接查看MapperProxy的invoke方法就好了。
// == F. 方法执行,会进入InvocationHandler(MapperProxy)的invoke方法
Blog blog = mapper.selectBlog(101);
org.apache.ibatis.binding.MapperProxy#invoke
org.apache.ibatis.binding.MapperMethod#execute{
// == 按操作类型区分
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(
## update执行
sqlSession.update(command.getName(), param));
break;
}
case DELETE:
...
case SELECT:
...
}
我们以update为例,观察它的具体执行
update执行
org.apache.ibatis.session.defaults.DefaultSqlSession#update(java.lang.String, java.lang.Object)
update(String statement, Object parameter) {
MappedStatement ms = configuration.getMappedStatement(statement);
// == 真正的执行动作在executor中
return executor.update(ms, wrapCollection(parameter));
}
org.apache.ibatis.executor.BaseExecutor#update
// 查看默认的SimpleExecutor实现
org.apache.ibatis.executor.SimpleExecutor#doUpdate
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
Configuration configuration = ms.getConfiguration();
// 封装成StatementHandler
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
// 准备了Statement(已经是jdbc逻辑)
stmt = prepareStatement(handler, ms.getStatementLog());
// == 最终执行
return handler.update(stmt);
}
// 最终执行我们依然查看SimpleStatementHandler,发现就是熟悉的jdbc statement执行sql逻辑
org.apache.ibatis.executor.statement.SimpleStatementHandler#update{
statement.execute(sql);
}
select执行
文章开始时,我们的计划是分析一次select执行
其实select与update的执行过程差不多,最终都会靠jdbc的statement执行。最大的差异在于select可以用到缓存,也就是人们常说的mybatis的一、二级缓存。
这部分内容放在下篇文章分析。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。