mybatis是大家经常用到的ORM框架,之前一直使用却没有静下来心来好好整理下,今天抽空来整理整理框架以及流程,以便后续回顾学习使用。
本文参考的mybatis版本为:mybatis-3.5.2
1、首先来看下核心架构图
核心模块图
接口层:主要是sqlSession封装增删改查接口,提供给应用调用
核心层:核心层主要功能为配置解析、参数映射处理、动态SQL、核心执行引擎、以及结果映射处理等
基础层:包括缓存的封装、日志、反射机制、连接池管理等
2、执行流程图
1、贴一段正确调用的代码,方便理解流程图:
public static void main(String[] args) {
SqlSession sqlSession =null;
try {
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sqlSessionFactory =new SqlSessionFactoryBuilder().build(inputStream);
// 打开session
sqlSession = sqlSessionFactory.openSession();
//查询数据库中数据
sqlSession.selectOne("XXX.getOrder",1);
}catch (Exception e) {
e.printStackTrace();
}finally {
if (sqlSession !=null) {
sqlSession.close();
}
}
}
下面是主要流程:
3、主要类、接口以及职责
上面的流程一些同学看了觉得怎么那么简单?是的,主流程就是这么简单,但是里面的执行逻辑还是有点复杂,下面咱们分析主要核心流程内部是怎么处理的。
加载XML主流程入口
1、解析mybatis.xml,通过一组ClassLoaders以及文件路径,解析mybatis.xml并返回InputStream流
通过classload解析XML
2、创建SqlSessionFactory,通过SqlSessionFactoryBuilder的名字可以看出这是一个建造者设计模式。
进入核心代码:
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) {
}
}
}
通过XMLConfigBuilder的parse方法,生成Configuration对象,然后生成SqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
3、打开SqlSession
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(),null,false);
}
通过getDefaultExecutorType我们可以跟踪到,默认使用的执行器类型是simple
protected ExecutorTypedefaultExecutorType = ExecutorType.SIMPLE;
下面是核心的获取sqlSession的过程:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level,boolean autoCommit) {
Transaction tx =null;
try {
//从configuration中获取以及解析完成的环境变量信息,Environment中包含事务 工厂以及数据源对象
final Environment environment =configuration.getEnvironment();
//如果environment的事务工厂对象为空,则新建一个,否则返回environment中的事务对象
final TransactionFactory transactionFactory =getTransactionFactoryFromEnvironment(environment);
//根据数据源,事务级别以及是否自动提交标识,创建事务对象
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//创建核心SQL执行器
final Executor executor =configuration.newExecutor(tx, execType);
//返回SqlSession
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();
}
}
下面看下final Executor executor =configuration.newExecutor(tx, execType);具体做了哪些事情
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
//下面2行代码没太看明白,第一行已经判断是非为空并设置默认值了,第二行executorType肯定不为空,这目的是什么?哪位老铁看懂了奥妙可以留言,不胜感激!!!
executorType = executorType ==null ?defaultExecutorType : executorType;//1
executorType = executorType ==null ? ExecutorType.SIMPLE : executorType;//2
Executor executor;
if (ExecutorType.BATCH == executorType) {
//是否批量处理
executor =new BatchExecutor(this, transaction);
}else if (ExecutorType.REUSE == executorType) {
//ReuseExecutor为可重用执行器,无需释放
executor =new ReuseExecutor(this, transaction);
}else {
//最后兜底的就是SimpleExecutor啦,也是默认类型
executor =new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
//如果开启了缓存,则使用缓存执行器,上面的流程就忽略了
executor =new CachingExecutor(executor);
}
//此处需要注意,是将执行器添加到所有插件拦截器里,所有的拦截器都能拦截到excutor的执行信息,具体看源码是通过JDK的动态代理实现的,此处就不展开讲解了
executor = (Executor)interceptorChain.pluginAll(executor);
return executor;
}
4、通过DefaultSqlSession执行SQL,以查询一条数据为例
@Override
public T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List list =this.selectList(statement, parameter);
if (list.size() ==1) {
return list.get(0);
}else if (list.size() >1) {
//是不是经常看到这个错误^_^
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
}else {
return null;
}
}
public List selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms =configuration.getMappedStatement(statement);
//executor终于开始干活了
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
}catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
}finally {
ErrorContext.instance().reset();
}
}
@Override
public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)throws SQLException {
//获取装订好的SQL
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
执行查询
public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack ==0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List list;
try {
queryStack++;
list = resultHandler ==null ? (List)localCache.getObject(key) :null;
if (list !=null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
}else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
}finally {
queryStack--;
}
if (queryStack ==0) {
for (DeferredLoad deferredLoad :deferredLoads) {
deferredLoad.load();
}
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
clearLocalCache();
}
}
return list;
}
小结:
核心类以及职责:
SqlSessionFactoryBuilder:创建SqlSessionFactory工厂,此类可以被实例化、使用和丢弃,最佳使用就是用在局部方法变量
SqlSessionFactory:创建SqlSession,SqlSessionFactory最佳实践是在应用运行期间不要重复创建多次。
SqlSession :包含了面向数据库执行 SQL 命令所需的所有方法,增删改查等操作。另外SqlSession 非线程安全,所以不能线程间共享
Configuration:配置类,目的是为了在调用过程中从JVM的Heap内存中获取,防止每次都需要加载XML文件
Executor:SQL执行器,包括:BatchExecutor、ReuseExecutor、SimpleExecutor、CachingExecutor。 SqlSession是对外提供接口使用,Executor则是对DB使用
StatementHandler:Statement处理器,封装了Statement的各种数据库操作方法execute()
ResultSetHandler:返回结果集处理器,对返回的结果包装成具体的对象,完成ORM操作