大家好,这篇博客我想分析下Spring是如何集成myBatis的,下篇博客分析集成后DataSourceTransactionManager事务管理的完整流程。spring集成mybatis,spring主要扮演了两个角色,原料供应商和事务管理者,第一个角色是必须扮演的,第二个角色可选,事务也可以由mybatis自己管理。为什么说第一个角色叫原料供应商呢?因为spring想驱动mybatis工作,要给它提供必须的原材料,其中有数据源和配置。mybatis获得了数据源和配置后,就可以生产出自己的DefaultSqlSessionFactory对象,这是一个非常重要的mybatis对象,它用于生产mybatis的DefaultSqlSession。
一. SqlSessionFactoryBean
那么,负责收取spring提供的原料然后加工成DefaultSqlSessionFactory对象的工作由谁完成呢?它就是SqlSessionFactoryBean,通常我们会在配置文件中这样配置,把SqlSessionFactoryBean定义成单例Bean,传入一个数据源对象和mybatis的配置文件,当然它还提供了一些其它属性,如用于配置mybatis的拦截器或类型处理器,不过通常还是更习惯于提供一个mybatis的配置文件路径,把mybatis的相关配置写在这个文件里。
SqlSessionFactoryBean实现了Spring的InitializingBean接口,因此它会在完成了自身的创建后获得初始化的机会,其目的就是要在初始化方法中完成mybatis的DefaultSqlSessionFactory对象的创建。我用一张图来表述如何得到DefaultSqlSessionFactory对象。
我们从左到右分析上图,最左边从上至下三个对象分别是mybatis环境名称、mybatis事务工厂和数据源。环境名称就是一个字符串,当mybatis和spring集成后,环境名称就是“SqlSessionFactoryBean”,环境名称就是起到一个标识的作用,告诉mybatis目前你被集成到spring了。接下来是SpringManagedTransactionFactory(受spring管理的事务工厂),这个类的作用是生产mybatis的事务对象,被生产的事务对象类型是SpringManagedTransaction(受spring管理的事务)。这个事务对象的特点是它调用spring的DataSourceUtils.getConnection(dataSource)获取数据库连接。照理说,spring已经把数据源传给了mybatis,mybatis为什么还要从spring获取数据库连接呢,目的就是要把可能存在的事务管理工作交由spring处理。在spring中适用于mybatis的事务管理器是DataSourceTransactionManager,在Dao类执行数据库操作前,DataSourceTransactionManager会先执行事务初始化操作,初始化操作的核心就是创建数据库连接对象,然后把连接对象置入LocalThread中,当mybatis向spring请求连接对象时,spring便从LocalThread中返回连接对象,spring控制了连接对象,从而就实现了对事务的控制。
SqlSessionFactoryBean在InitializingBean接口的afterPropertiesSet()方法中创建完DefaultSqlSessionFactory对象后,会赋值给成员变量。同时SqlSessionFactoryBean还实现了FactoryBean接口,这意味着ID是"sqlSessionFactory"的Bean,其值并非SqlSessionFactoryBean,而是其getObject()方法的返回值,返回值就是在初始化时创建的DefaultSqlSessionFactory。
二. SqlSessionTemplate
mybatis集成spring一共有4个重要的对象,上面我已经介绍了3个(加粗标红字体),第4个对象是SqlSessionTemplate,它实现了mybatis's SqlSession接口,这个接口提供了各种增删改查方法,可以说是mybatis数据库操作的入口。SqlSessionTemplate对SqlSession接口方法的实现,其实是委托给DefaultSqlSession来完成的,既然如此的话,SqlSessionTemplate负责什么呢? 它负责获取DefaultSqlSession对象,之所以要这样设计是为了实现spring事务管理。当从spring获得连接对象,完成DefaultSqlSession对象的创建后,还需要执行spring事务相关操作,具体看下面过程分析。对于未开启spring事务管理的场景,那么创建完DefaultSqlSession对象后,其它操作是不会执行的。无论是否开启spring事务管理,都是从spring获取连接对象。
SqlSessionTemplate获取DefaultSqlSession对象的完整过程如下:
1. 调用TransactionSynchronizationManager.getResource(sessionFactory)方法,尝试从当前线程获取DefaultSqlSession对象,首次访问应该是不存在的,如果存在则直接返回。
2. 调用DefaultSqlSessionFactory对象方法,创建DefaultSqlSession,在创建DefaultSqlSession过程中一个重要的步骤就是从spring获取数据库连接。
3. 创建SqlSessionHolder对象,它实现了spring的ResourceHolder(资源持有器接口),其持有的资源就是DefaultSqlSession,在创建SqlSessionHolder时会传入DefaultSqlSession。资源持有器的作用是什么?比如它会记录所持有资源的状态,调用资源的方法,可以把它理解为访问资源的标准接口。
4. 把SqlSessionHolder绑定到当前线程,key是DefaultSqlSessionFactory对象,绑定资源的操作是调用TransactionSynchronizationManager对象方法完成的,TransactionSynchronizationManager对象的作用是管理事务过程中的线程安全对象,比如资源对象、同步对象、事务名称等。
5. 创建事务同步对象SqlSessionSynchronization,它持有资源对象SqlSessionHolder,然后把它注册到当前线程。事务同步对象的作用是,在某些特定的环节,比如挂起、恢复、事务完成后,对资源持有器执行特定操作,比如解绑资源持有器。用来图来描述下事务同步对象的作用,以及几个对象之间的相互关系。
三. 总结
1. spring启动时会创建SqlSessionFactoryBean对象,它负责接收spring供给的原材料,创建DefaultSqlSessionFactory对象。
2. spring集成mybaits最重要的关键点是,由spring供给mybatis数据库连接对象,目的是要实现事务管理。