1。Mybatis架构及核心API如果不出问题,在后续的源码分析相关文章中,我们将对Mybatis源码进行一次大扫除,挖掘出每一个值得深入理解/记忆的知识点。在本文中,我们主要先展开Mybatis的架构/层级,略过Mybatis架构的整体设计,然后详细消化几个硬核API。整体顺序,希望大家有所期待~先简单揭开神秘的Mybatis源码包,看看Ta的大致目录结构:apache.ibatis目录,基本设计目的我简单整理成上图,方便大家直观理解。当然,只看源码包的目录结构难免显得乏味,还是再看一遍吧。其实Mybatis的源码包的功能可以这样划分:我们对Mybatis的架构有了一个抽象的认识。但实际上,对于核心API的具体功能划分和场景应用,会呈现出怎样的流程呢?看看下面的功能架构设计,或许你能更好的理解。根据Mybatis的功能架构,我们将其分为三层:接口层:该层提供了一系列接口供用户直接参与使用,包括信息配置和实际数据操作调用。配置方式包括:基于XML的配置方式和基于JavaAPI的配置方式。用户也可以通过接口层API向数据库发起增、删、改、查询等操作请求。该层接口将接收到的调用请求交给数据处理层组件处理。数据处理层:该层是Mybatis的核心层,负责数据处理,主要包括SQL参数映射分析、SQL语句的实际执行、执行结果集的映射处理等框架支撑层:该层属于物流Mybatis的支撑层,包括数据库连接管理、事务控制管理、配置加载和缓存处理、日志处理、异常处理等,提供基础支撑能力,保障上层数据处理。我们知道Mybatis框架允许用户只提供配置信息,专注于编写SQL。对于连接管理数据库/事务,或者实际的SQL参数映射/语句执行/结果集映射等操作,作为用户,是没有必要关心和介入的。然而好奇的我们其实很想知道Mybatis核心部分的数据处理在整个过程中是如何支持用户请求的呢?同时,各个组件之间的交互是如何流动的?巧合的是,我这里有一张图介绍总体流程:按照上面的框架流程图来解释:创建配置和调用API:这个环节发生在应用端,是开发者在实际应用中进行的两步操作。第一步创建核心配置文件Configuration.xml和映射文件mapper.xml(以上两个配置也可以通过注解创建),准备好基本配置和SQL语句后;第二步,直接调用Mybatis框架中的数据库操作接口。加载配置并初始化:Mybatis框架会根据应用端提供的核心配置文件和SQL映射文件的内容,使用资源辅助类Resources将配置文件读入输入流,然后解析封装成Configuration对象通过对应的parser和MappedStatement对象,最终将对象存储在内存中。创建会话并接收请求:Mybatis框架加载配置并初始化配置对象后,会话工厂构建器SqlSessionFactoryBuilder同时创建会话工厂SqlSessionFactory。会话工厂会根据应用端的请求创建会话SqlSession,以便应用端与数据库进行交互。处理请求:SqlSession接收到请求后,并不真正进行处理,而是将请求转发给执行器,执行器再派发给语句处理器StatementHandler。语句处理器会结合参数处理器ParameterHandler进行数据库操作(底层封装了JDBCStatement操作)。返回处理结果:每个语句处理器StatementHandler完成数据库操作后,会配合ResultSetHandler和类型处理器TypeHandler对底层JDBC返回的结果集进行映射封装,最终返回封装后的对象。对于上面整体框架流程涉及到的这些硬核API,下面我们会一一介绍,但是我们不会详细分析源码和原理,包括构建细节,因为我们会在后续详细分析这些源代码分析章节。2.Configuration——全局配置对象对于Mybatis的全局配置对象Configuration,相信无论是初学者还是资深玩家,都不会陌生。整个Mybatis宇宙都是围绕着Configuration展开的。Configuration对象的结构和config.xml配置文件的内容几乎一样,涵盖了properties(属性)、settings(设置)、typeAliases(类型别名)、typeHandlers(类型处理器)、objectFactory(对象工厂)、mappers(映射器)等等,我们之前有专门的文章详细介绍过,有兴趣的可以上目录列表找文章《Mybatis系列全解(四):全网最全!Mybatis配置文件XML全貌详解》详细品尝。配置对象Configuration由解析器XMLConfigBuilder解析,将全局配置文件Config.xml和映射器配置文件Mapper.xml中的所有配置信息构建成一个完整的Configuration对象。我们将在后续的源码分析中详细分析整个过程。3.Resources——资源辅助类我们知道,Configuration、Mapper等配置信息是保存在XML文件中的。Mybatis框架在构建配置对象时,首先要将XML文件信息加载到一个流中,然后再进行后续的解析和封装。资源作为资源的辅助类,它正是做这个工作的。无论是通过加载本地资源还是加载远程资源,最终都会通过类加载器访问资源文件,并输出文件流。//加载核心配置文件InputStreamresourceAsStream=Resources.getResourceAsStream("Config.xml");Resources其实提供了一系列方法分分钟解决你的文件读取和加载问题:4.SqlSessionFactoryBuilder——我们认识的会话工厂构建器xxxBuilder,可以大致知道它是某类对象的构建器。这里的SqlSessionFactoryBuilder也是一样。它是Mybatis中的会话工厂构建器。资源辅助类Resources读取文件流信息后,负责解析文件流信息并构建会话工厂SqlSessionFactory。(解析的配置文件包括:全局配置Configuration和mapperMapper)在程序的应用端,我们一般使用SqlSessionFactoryBuilder直接构建session工厂://获取sqlSession工厂对象SqlSessionFactorysqlSessionFactory=newSqlSessionFactoryBuilder().build(resourceAsStream);当然,如果你对于集成了Spring框架的项目,不需要手动建立session工厂,只需要在Spring的配置文件中指定即可,比如指定一个bean对象,id为sqlSessionFactory,类类指定为org.mybatis.spring.SqlSessionFactoryBean。SqlSessionFactoryBuilder内部通过解析器XMLConfigBuilder解析文件流,同时封装成一个配置对象Configuration,然后传递Configuration对象并构建实例。publicSqlSessionFactorybuild(InputStreaminputStream,Stringenvironment,Propertiesproperties){//Configuration解析器解析XMLConfigBuilderparser=newXMLConfigBuilder(inputStream,environment,properties);//最终实例会话工厂returnbuild(parser.parse());}最终实例会话工厂,其实是Mybatis默认的该实现是DefaultSqlSessionFactory的新实例。//最终实例会话工厂publicSqlSessionFactorybuild(Configurationconfig){returnnewDefaultSqlSessionFactory(config);}会话工厂构建器SqlSessionFactoryBuilder应用了构建器模式,主要目的是构建SqlSessionFactory对象,用于后续生产SqlSession对象,这个构造器基本是Mybatis框架的入口的构建器,提供一系列多态方法build(),支持用户使用XML配置文件或JavaAPI(Properties)构建会话工厂SqlSessionFactory实例。SqlSessionFactoryBuilder的生命只是为了实现SqlSessionFactory。一旦SqlSessionFactory被实例化并且SqlSessionFactoryBuilder的任务完成,它就可以死亡并被丢弃。因此,SqlSessionFactoryBuilder实例的最佳作用域是方法作用域(即局部方法变量)。可以复用SqlSessionFactoryBuilder来创建多个SqlSessionFactory实例,但最好不要一直保留它,以确保可以释放所有XML解析资源以用于更重要的事情。SqlSessionFactoryBuilder中灵活构建会话工厂的一系列接口:5.SqlSessionFactory——会话工厂会话工厂SqlSessionFactory是一个接口,用于产生数据库会话对象SqlSession。有两个实现类:**DefaultSqlSessionFactory**(默认实现)**SqlSessionManager**(只多实现了一个Sqlsession接口,已经弃用)在介绍会话工厂构建器SqlSessionFactoryBuilder时,我们了解到构建器创建了一个DefaultSqlSessionFactory默认情况下,sessionfactory本身会绑定一个重要的属性Configuration对象,在产生session时,Configuration配置对象最终会被传递并设置为session的SqlSession。会话工厂可以简单地创建一个SqlSession实例://CreateaSqlSessioninstanceSqlSessionsession=sqlSessionFactory.openSession();会话工厂创建SqlSession时,会绑定数据源、事务处理、执行器等,默认会话工厂实现类DefaultSqlSessionFactory创建会话对象,最终会调用openSessionFromDataSource方法,是这样实现的://每一个openSession最终都会在这里调用.getDataSource(),level,autoCommit);//executorfinalExecutorexecutor=configuration.newExecutor(tx,execType);//最终生成session灵活产生sessionSqlSession的接口,可以指定:事务处理:要在session作用域内使用/启用事务作用域(即不自动提交事务),或者使用auto-commit,sqlSession不commit默认为事务,增删改查操作需要手动提交事务。数据库连接:是想让MyBatis帮你从配置好的数据源中获取连接,还是使用自己提供的连接,你可以动态创建数据源对象Connection。Executortype:您想指定某种类型的执行器来创建/执行/预处理语句。可以有一个普通的执行器(SimpleExecutor),一个可重用的执行器(ReuserExecutor),或者一个批处理的执行器(BatchExecutor)等。下面描述的执行器会详细描述。事务隔离级别支持:支持JDBC的五种隔离级别(NONE、READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ和SERIALIZABLE)。关于交易相关的内容,我们会在《spring 系列全解》中详细说到。这里简单说明一下,事务隔离级别主要是为了解决例如脏读、不可重复读、幻读等,使用不同的事务隔离级别,必然导致数据库执行效率不同,所以我们对不同系统/功能中的隔离级别。一旦创建,只要应用程序运行,SqlSessionFactory就应该存在,没有理由丢弃它或创建另一个实例。使用SqlSessionFactory的最佳实践是不要在应用程序运行期间多次创建它。多次重建SqlSessionFactory被认为是代码“坏习惯”。所以SqlSessionFactory的最佳作用域是应用程序作用域。最简单的是使用单例模式或静态单例模式。记得创建SqlSessionFactory一次!每个数据库有一个SqlSessionFactory实例。SqlSessionFactory一旦被创建,它的生命周期应该与应用程序的生命周期相同。所以,如果你想连接两个数据库,你需要创建两个SqlSessionFactory实例,每个数据库一个;如果您有三个数据库,则需要三个实例,依此类推。6.SqlSession–SessionSqlSession是一个接口,有两个实现类:DefaultSqlSession(默认实现)SqlSessionManager(deprecated)简单的说就是通过session工厂构建一个SqlSession实例后,我们可以进行增删改查。例子DefaultSqlSession提供了这么多方法供用户使用,有30多个:除了CURD,sqlSession的方法还提供了commit/close/rollback等事务控制,提供了getConfiguration(),提供提供flushStatements()等批量语句的执行更新,提供clearCache()等缓存清除,提供getMapper()等映射器的使用,等等。对于客户端应用层面,如果熟悉sqlSession的API,基本上可以对数据库进行任意操作,但是我们更想知道的是sqlSession内部是如何执行sql的呢?其实sqlSession是Mybatis中用来与数据库交互的顶级类,通常是绑定到本地线程ThreadLocal,一个session使用一个SqlSession,用完就关闭。SqlSession之所以被称为数据交互的顶级类,是因为它实际上并不完成实际的数据库操作。根据前面的架构设计流程,我们已经清楚的知道SqlSession对数据库的操作都会转发给具体的执行器Executor来完成;当然,执行器也是一个不插手的掌柜,执行器Executor会被赋值给语句处理器StatementHandler,语句处理器会结合参数处理器ParameterHandler共同完成最终的数据库执行处理操作(底层还是封装了JDBCStatement操作)。并且每个语句处理器StatementHandler完成数据库操作后,通过结果处理器ResultSetHandler和类型处理器TypeHandler将底层JDBC返回的结果集进行映射封装,最终返回预期的封装对象。注意下图中sqlSession红色高亮的位置,详细描述了session的实际执行路径:SqlSession可以理解为一个数据库session。在一个session中,SQL可以执行一次,也可以批量执行多次。但是,一旦session关闭,如果要再次执行sql,就必须重新创建session。每个线程都应该有自己的SqlSession实例,SqlSession实例不是线程安全的,所以不能共享,所以它最好的作用域是请求(request)或方法(method)作用域。永远不要将对SqlSession实例的引用放在类的静态字段中,即使是类的实例变量也不行。也不要将对SqlSession实例的引用放在任何类型的托管范围内,例如Servlet框架中的HttpSession。如果您现在正在使用Web框架,请考虑将SqlSession放在类似于HTTP请求的范围内。也就是说,每收到一个HTTP请求,就可以打开一个SqlSession,返回响应后,就可以关闭了。这个关闭操作非常重要。为了保证每次都能执行关闭操作,应该把这个关闭操作放在finally块中。
