源码执行流程
- 通过@MapperScan导入MapperScannerRegistrar类
- MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,Spring启动会调MapperScannerRegistrar类中的registerBeanDefinitions方法
- 在registerBeanDefinitions方法中注册一个MapperScannerConfigurer类型的BeanDefinition
- 而MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,所以Spring在启动过程中时会调用postProcessBeanDefinitionRegistry()方法
- 在postProcessBeanDefinitionRegistry方法中会生成一个ClassPathMapperScanner对象,然后进行扫描Mapper, 得到BeanDefinition
- 修改扫描到的BeanDefinition,把BeanClass修改为MapperFactoryBean,把AutowireMode修改为byType
- 扫描完成后,Spring就会基于FactoryBean的getObject去创建Bean,得到Mapper代理对象
流程图
一级缓存和二级缓存
一级缓存:
一级缓存是SqlSession级别的缓存,在我们执行查询操作时,会先从该缓存中取数据,如果没有找到,就从数据库中查询出来,然后缓存到一级缓存中。每个SqlSession中都有一个一级缓存,彼此间不共享,所以一级缓存的作用范围就是它自己的SqlSession,当SqlSession结束后,一级缓存也就没有了。所以,一级缓存也称为本地缓存。
二级缓存:
二级缓存是mapper级别的缓存,多个SqlSession可以共享同一个二级缓存,它的作用范围是mapper的namespace,不同namespace下的mapper不共享二级缓存。当一个SqlSession查询完数据后,会将数据缓存到二级缓存中,其他SqlSession就可以使用。二级缓存是跨SqlSession的,一个SqlSession查询的数据会被缓存到二级缓存中,其他SqlSession可以使用。
实现机制:
Mybatis的一级缓存是基于PerpetualCache的HashMap本地缓存,其存储作用域为Transaction,即必须手动提交以后,才能把数据写入到一级缓存中。二级缓存与一级缓存其机制相同,默认也是采用PerpetualCache,HashMap存储,不同在于其存储作用域为Mapper(Namespace),并且可自定义存储源,如Ehcache。在Mapper.xml中的<select>,<insert>,<update>,<delete>标签中加入flushCache="true",则当前操作后,会清空当前Mapper下的二级缓存,如果在<select>标签中加入useCache="false",则不使用二级缓存。
一级缓存失效问题:
Spring整合Mybatis后,如果执行某个方法时,该方法上没有加@Transactional注解,也就是没开启Spring事务,那么后面在执行具体sql时,每执行一个sql时都会新生成一个SqlSession对象来执行该sql,这就是一级缓存失效(也就是没有使用同一个SqlSession对象),而如果开启了Spring事务,那么该Spring事务中的多个sql,在执行时会使用同一个SqlSession对象,从而一级缓存生效