MyBatis是一个实现了JPA规范的用来连接数据库并对其进行增删改查操作的开源框架 ,其实,它的底层就是一个JDBC封装的组件,因此MyBatis在使用上免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
那接下来,就来看一下MyBatis到底是如何执行的。
1、获取sqlsessionFactory
解析文件的信息保存到Configuration中,返回Configuraation的DefaultSqlSession对象;【mappedStatement】代表一个增删改查的详细信息
2、获取sqlSession。返回sqlSession的实现类DefaultSqlSession,里面维护了Excutor(执行器)和Configuration(配置对象)。
3、获取接口代理对象MapperProxy,返回的是动态代理对象,里面维护了上面的sqlsession,也是真正执行增删改查的对象。
4、执行增删改查
一、获取sqlsessionFactory。SqlSessionFactoryBuilder最终会走到下图的方法。
下一步,SqlSessionFactoryBuilder走到build()方法,并带着configuration对象执行DefaultSqlSessionFactory的有参构造创建工程对象。
那有朋友就会说,configuration对象包含哪些属性,他有事怎样赋值的呢,别着急,下面就一起来看一下。
我们知道,关键在于上面提到的parse()方法,那就看看他里面都有什么?
可以看到,实际上parseConfiguration()才是parse()方法的核心,我们进到里面看
这里,可以看到,是将文档对象中的节点也就是xml配置文件中的标签的数据赋到了configuration的对应属性上
我们着重看一下mapperElement()方法:
可以看到,mapper映射文件的配置有多种形式,扫描包,解析url路径等等,形式虽然不同,但是做的事情都是一样的,那就以package为例, 进入到他的addMappers()方法中一探究竟:
addMappers()扫描到的mapper接口一一遍历,作为参数执行到了下面的方法:
可以看到,这里是为每个mapper接口创建了对应的代理对象工厂MapperProxyFactory,是为了后面创建mapper代理对象用,并将工厂对象存放到configuration维护的一个集合中。下面是将每个mapper接口和其对应的映射文件以方法为粒度封装到了mappedstatements对象中,其中包含了操作数据库需要用的如方法名称、sql语句等信息
进行到这里,可以说mybatis执行流程的第一步就完成了。
二、获取sqlsession对象
在拿到sqlsessionFactory对象后,紧接着的一个操作就是获取真正操作数据库的sqlsession,那么,到底是如何获取以及获取的同时还做了什么事情,我们往下看
首先,openSession()调用了openSessionFromSource()方法,后面传的参数其实是维护在configuration中默认执行器的类型,这个参数的用途稍后就可以看到;
进入到openSessionFromSource()方法中,可以看到,这个才是openSession的核心所在,mybatis在这里初始化了一些sqlSession所需成员变量,并且创建了DefaultSqlSession对象
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 获取环境信息
final Environment environment = configuration.getEnvironment();
// 获取environment节点下的事务配置信息 默认情况下是ManagedTransactionFactory,
// 如果配置了transactionManager节点并且type为JDBC,则返回JdbcTransactionFactory事务工厂
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//通过事务工厂来产生一个事务,
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//生成一个执行器(事务包含在执行器里)
final Executor executor = configuration.newExecutor(tx, execType);
//然后产生一个DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
//如果打开事务出错,则关闭它
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
//最后清空错误上下文
ErrorContext.instance().reset();
}
这样,sqlSession也就"open"出来了。
三、获取mapper接口代理对象
很多朋友初用mybtis是一定有这样的疑问,我明明只是创建了mapper接口,并没有实现,为什么却能拿到可执行的对象呢,这就是我们接下来要探讨的代理对象的创建过程:
上面第一步时,我们分析过configuration对象里维护了mapper接口和mapper映射文件的信息属性和生成mapper接口代理对象的工厂MapperProxyFactory,到这一步时,就是拿到对应接口的工厂,利用JDK动态代理技术生成我们最后真正使用的MapperProxy代理对象。
DefaultSqlSession.getMapper()方法调用的其实是维护在DefaultSqlSession对象中的configuration对象的同名方法,第一步解析配置文件的时候,我们提到MapperProxyFactory最终是存放到configuration下的MapperRegistry下的Map
取出之后,当然就是用工厂来创建代理对象了,也mapperProxyFactory.newInstance(sqlSession)
它里面是这样的,我们可以看到,先是调用有参构造创建MapperProxy,其实MapperProxy是实现了InvocationHandler接口的,熟悉JDK动态代理的朋友,看到这个类就非常明白接下来的操作了,没错,就是调用Proxy.newProxyInstance方法最终生成指定的mapper接口代理对象。
这样呢,第三步生成代理对象也就完成了,最后一步因为过程稍复杂,有时间再和朋友们一起研究;