Mybatis实现原理:
概括一句话:约定配置参数mybatis-config.xml,映射关系JavaBean-mapper.xml,用SqlSessionFactoryBuilder构建应用程序运行期间需要的SqlSessionFactory实例对象,当请求或方法需要执行CURD操作时,通过SqlSessionFactory创建一个SqlSession对象,来进行对数据库的操作。
核心类
- SqlSessionFactoryBuilder
- SqlSessionFactory
- SqlSession
Mybatis核心类作用:
SqlSessionFactoryBuilder:
- 作用?
- 只做一件事情:用来构建SqlSessionFactory实例
- 基于什么构建?
- mybatis-config xml配置,抑或是自定义Configuration实例对象
- 生命周期?
- 构建完Factory实例,即可销毁,作用域一般在方法中作为局部变量使用
SqlSessionFactory:
- 作用?
- 用来“加工生产”SqlSession实例对象
- 基于什么构建?
- 基于SqlSessionFactoryBuilder在建造时解析出来的Configuration对象(会包含environment, properties, setting, mappers映射关系等)
-
而这个Configuration对象就是在
SqlSessionFactoryBuilder
构造build时,通过XmlConfigBuilder
将我们定义好的mybatis-config.xml
配置文件解析成程序对象,具体是通过这个方法org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration
处理的:- 从这里可以看出,在xml中的xml标签(properties, typeAliases等等),这就理解了设置标签时的规则【Q1】:
-
private void parseConfiguration(XNode root) {
try {
// issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfsImpl(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginsElement(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"));
typeHandlersElement(root.evalNode("typeHandlers"));
mappersElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
- 生命周期?
- 一旦被构建,应该在程序运行期间一直存在,且一般情况下,都是以单例存在,不需要创建多个。作用域应该为应用作用域。
SqlSession
- 作用?
- 用来处理执行sql CURD操作,事务管理,以及缓存功能
- 基于什么构建?
- 每当SqlSessionFactory对象调用openSession方法时,就会创建一个DefaultSqlSession实例对象;
- 该SqlSession对象会有一个属性为Executor对象,而这个Executor对象是基于Environment配置中定义的transactionManager 类型type创建起来的(一般情况下是:JdbcTransaction)。最终的代码逻辑为:
-
获取JavaBean Mapper对象,通过SqlSession的Configuration对象中获取预先定义好的mappers,根据映射id,找到MappedStatement;
-
执行CURD操作,通过SqlSession的Executor【Q2】对象来受理数据库操作,最终是交给JDBCTransaction事务对象完成的
-
拿到JavaBean的Mapper对象后,根据method方法名查找mapper映射信息:
-
在mapper映射中创建一个个CURD,都会被解析成一个个以 id 为key,以MappedStatement为value的map.entry对象:
-
而这里的MappedStatement对象:属于Prepared类型的,且将我们在mapper定义的信息加载出来,在这里可以理解为是一个信息库,供后续sql交互时使用:
-
最终执行sql时,还是回到了熟悉的JDBC配方:
这个SimpleExecutor继承了我们SqlSession的Executor对象,通过transaction去创建Connection,去创建preparedStatement,得到结果集ResultSet:
-
- 画个图更清晰些:
- 生命周期?
- 每个线程都应该有它自己的 SqlSession 实例。一般情况下,作用域是请求或方法作用域,一次Http请求,或一个方法的CURD操作。注意一点的是,这里需要显式的事务管理,缺省值autocommit为false。另外一点的是,用完需要关闭资源。
【Q1】: 这里标签的编写是有顺序要求的:
- 必须按照要求的顺序来:
The content of element type "configuration" must match
"(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".
【Q2】: 这里Excutor,在OpenSqlSession创建时,就可以看到:(具体会在后续总结到)
- SqlSession -> Executor = CachingExecutor(在Cache中查询信息)
- CachingExecutor -> Delegate(委托代理) = BaseExecutor(真正去DB操作的执行器对象)
- 这里就涉及到Mybatis的缓存功能:
- Mybatis分为:
- 一级缓存: 默认开启,SqlSession级别;
- 二级缓存:Mapper级别,需要在mapper映射上定义
<cache>
标签来开启
- Mybatis分为:
- 这里就涉及到Mybatis的缓存功能: