MyBatis核心源码解析(三)
在前面两章,我们对MyBatis的核心源码做了深度分析,可以看到MyBatis其实对于Spring来说就变得不那么复杂,本章将会介绍Spring-MyBatis剩余的部分内容,在前面Spring的章节,笔者写过一章Spring-MyBatis,其中就核心介绍了Spring的扩展功能和MapperFactoryBean这个类,也介绍了它的核心设计思想,重点介绍了如何将代理对象交给Spring管理,那么本章就对Spring-MyBatis其他重要的点进行详细说明。
Spring整合MyBatis后除了核心的MapperFactoryBean,其实还有一部分内容,
import com.User;
import com.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;import java.io.IOException;import java.io.InputStream;
public class Test {
public static void main(String[] args){
ClassPathXmlApplicationContext applicationContext =new ClassPathXmlApplicationContext("spring-config.xml");
UserMapper userMapper= (UserMapper) applicationContext.getBean("userMapper");
userMapper.findUserById(1);
}
}
然后我们调试代码会调用下面这段代码
可以发现我们整合了Spring之后的MyBatis使用的sqlsession是SqlSessionTemplate,这种SqlSessionTemplate是Spring自己定义的,那么为什么Spring要自己搞一个SqlSessionTemplate呢?而不继续使用Mybatis的DefaultSqlSession呢?我们来看MyBatis源码
看注释我们发现这是一个不安全的类,那么为什么这里会专门提出不安全性呢?我们来对比两者的区别
SqlSessionTemplate的selectList方法
DefaultSqlSession的selectList方法
我们对比一下就发现SqlSessionTemplate会使用一个代理类来执行selectList,我们在回到DefaultSqlSession,为什么DefaultSqlSession会不安全,我们知道DefaultSqlSession真正工作的是执行器,而假如在用户访问的某一个方法中,我们使用了多线程技术,那么这里就会出现一个问题,什么问题呢?还记得MyBatis的缓存技术吗?没错,MyBatis的一级缓存是在同一个会话中操作时,会有缓存操作,如果采用了多线程那么就会出现缓存数据错误的问题,因此Spring就觉得这样不妥,connection应该和事务进行绑定,如果不和事务绑定,为了解决线程安全问题,那么干脆直接将session关闭,将缓存的功能切掉,这也就是为什么一级缓存整合Spring后没什么作用的原因,
可以看到这里关闭session,但是这里的关闭并不是立即关闭,而是会做一个判断,只有referenceCount为0才关闭,
那么为什么要做一个变量referenceCount呢?这里Spring考虑到了另外一种情况,也就是上文提到的事务和多线程问题,在多线程的情况下,我们不能将会话作为安全的依据,为什么呢?假如在一个会话中,又是多线程的情况下,我们等于一个会话多路执行,这样事务是无法保证安全的,所以只能是采用另外的办法,我们看下面的代码
没错这里Spring使用了ThreadLocal的方法,将线程和会话绑定,而不是将会话作为安全的依据。这样就满足了一个会话一个线程,这样也能保证一个会话一个事务。