Java修炼指南:核心框架精讲
上QQ阅读APP看书,第一时间看更新

1.1 接口层

SqlSession是MyBatis接口的核心组件,这个接口是MyBatis中最重要的接口,能够让用户执行命令、获取映射、管理事务。SqlSession对外提供MyBatis常用的API。SqlSession接口对象用于执行持久化操作。一个SqlSession接口对应一次数据库会话,一次会话以SqlSession对象的创建开始,以SqlSession对象的关闭结束。SqlSession接口对象是线程,它是不安全的,所以每次数据库会话结束前,需要马上调用其close()方法将其关闭。再次需要会话时重新创建。在关闭时会判断当前的SqlSession是否被提交:若没有被提交,则会执行回滚后关闭;若已被提交,则直接将SqlSession关闭。所以,SqlSession无须手工回滚。

1.1.1 SqlSession接口

MyBatis提供了两个SqlSession接口的实现,如图1-1所示,这里使用的是工厂方法模式,其中最常用的是DefaultSqlSession实现。

通过如下代码构建SqlSession。

●图1-1 SqlSession接口的实现

SqlSessionFactory是通过SqlSessionFactoryBuilder的build方法构建的,下面开始分析SqlSessionFactoryBuilder类。

1.1.2 SqlSessionFactoryBuilder类

SqlSessionFactoryBuilder类代码如下。

build方法中的XMLConfigBuilder类代码如下。

SqlSessionFactoryBuilder类有多个build方法,但最终都是调用build(InputStream input-Stream、String environment、Properties properties)方法。

在build方法中,创建了XMLConfigBuilder对象,读取文件流加载XML配置文件,调用parse方法解析XML文件中的节点,然后返回一个Configuration对象。

根据返回的Configuration对象可以生成DefaultSqlSessionFactory对象,因为SqlSession-Factory只是一个接口,DefaultSqlSessionFactory实现了SqlSessionFactory接口,可以作为Sql-SessionFactory返回,完成SqlSessionFactory的构建。

1.1.3 SqlSessionFactory接口

SqlSessionFactory负责创建SqlSession对象,其中包含了多个openSession方法的重载,可以通过其参数进行是否自动提交、连接池、事务的隔离级别以及底层Executor的类型等方面的配置。SqlSessionFactory接口定义的代码如下。

1.1.4 DefaultSqlSessionFactory类

DefaultSqlSessionFactory实现了SqlSessionFactory接口中定义openSeesion的方法,调用openSession时又调用了自身的openSessionFromDataSource()或者openSessionFromConnection()方法,完成SqlSession的构建。

DefaultSqlSessionFactory是一个具体工厂类,主要提供了两种创建DefaultSqlSession对象的方式,一种是通过数据源获取数据库连接,并创建Excutor对象以及DefaultSqlSession对象,该方法名为penSessionFromDataSource();另一种方式是用户提供数据库连接对象,De-faultSqlSessionFactory会使用该数据库连接创建Excutor对象以及DefaultSqlSession对象,该方法是openSessionFromConnection()。

DefaultSqlSessionFactory类源代码如下。

1.1.5 DefaultSqlSession类

SqlSession中定义了常用的数据库操作以及事务的相关操作,为了方便使用,每种类型的操作都提供了多种重载。SqlSession接口的定义如下。

DefaultSqlSession是最常用的SqlSession接口实现,创建过程如图1-2所示。

●图1-2 DefaultSqlSession创建过程

DefaultSqlSession中实现了SqlSession接口中定义的方法,并且为每种数据库操作提供了多个重载。各个select重载方法之间的调用关系如图1-3所示。

上述select方法都是通过调用Executor.query(MappedStatement ms、Object parameter、RowBounds rowBounds、ResultHandler resultHandler)方法实现数据库查询操作的,各自对结果对象进行了封装。

insert()、update()、delete()方法也有多个重载,最后都是通过update(MappedStatement ms、Object parameter)方法实现的。

commit()、rollback()以及close()方法都会调用Executor中相应的防范,其中就会涉及清空缓存的操作,之后就会将dirty字段设置为false。

●图1-3 select重载方法之间的调用关系

dirty字段主要在isCommitOrRollbackRequired()方法中,与autoCommit字段以及用传入的参数force共同判断是否提交回滚事务。

DefaultSqlSession代码如下。

1.1.6 SqlSessionManager

SqlSessionManager既实现了SqlSessionFactory接口,也实现了SqlSession接口。具备生产SqlSession的能力,也具备SqlSession操作数据库的能力。

SqlSessionManager有三个成员变量如下。

SqlSessionManager如果要创建SqlSessionManager对象,需要调用其newInstance()方法(注意这里不是单例模式),代码如下。

sqlSessionFactory作为入参,直接赋值给成员变量,而sqlSessionProxy的赋值使用的是动态代理的方式,动态代理的三个入参:第一个是appClassLoader;第二个是需要被代理的类,这里是SqlSession;第三个是代理拦截器,新建了一个SqlSessionInterceptor,它是SqlS-essionManager的内部类。

在调用SqlSession时,会触发动态代理,先到成员变量localSqlSession中获取SqlSession,第一次到达该变量是空的,于是需要通过openSession()方法获得一个SqlSession,然后使用这个SqlSession去完成其他事情,成员变量的任何方法调用都是对localSqlSession的反射。

SqlSessionManager源码内容如下。

为避免资源浪费,SqlSessionManager还可以作为线程安全类,所以SqlSessionManager也提供了很多设置localSqlSession的方法。

SqlSessionManager与DefaultSqlSessionFactory的主要不同点体现在SqlSessionManager有两种模式。

第一种模式与DefaultSqlSessionFactory的行为相同,同一线程每次通过SqlSession Manager对象访问数据库时,都会创建DefaultSession对象完成数据库操作。

第二种模式是SqlSessionManager通过localSqlSession这个ThreadLocal成员变量,记录与当前线程绑定的SqlSession对象,供当前线程复用,从而避免在同一线程多次创建SqlSession对象带来性能损失。

这里需要注意的是,每个线程独享一份SqlSession,可以保证SqlSession的方法都是线程安全的,但是非SqlSession的方法,并不是线程安全的。

SqlSessionManager中实现SqlSession的接口方法,例如select ∗()、insert()、update()、delete(),都是直接调用SqlSessionProxy记录的SqlSession代理对象实现的。

当需要提交、回滚事务或者是关闭localSqlSession中记录的SqlSession对象时,可通过SqlSessionManager.commit()、rollback()、close()方法实现,这些方法会先检测当前线程是否绑定SqlSession对象,如果绑定则调用SqlSession对象的方法,否则抛出异常。