mybatis学习 关注点:Mapper绑定、Executor、Cache、Plugin
一、Mybatis初始化
DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
//初始化
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
构建 SqlSessionFactory的主要过程:
- 初始化Configuration ,包括mapperRegistry,typeAliasRegistry,typeHandlerRegistry
- 注册:解析mapper xml, 把其中的节点信息和Mapper类的方法一一对应,每个方法对应生成一个 MappedStatement(包括SQL类型、参数、结果映射、缓存等信息配置),并添加到 Configuration 中;
- 绑定:根据 Mapper xml 中的 namespace, 为每个mapperInterface创建对应的MapperProxyFactory,用于 Mapper 代理对象的生成,但此时并没有生成代理类,而是在后续调用
sqlSession.getMapper(Class<T> type);
时通过该工厂创建代理实例
mapper绑定:
二、Executor组件
数据库操作执行器,输入为数据库操作上下文、SQL参数、结果解析器,输出为执行结果,另外也可以是事务commit和rollback。
执行流程:
- 从DataSource获取Connection
- 初始化statement,用于执行SQL并返回结果集
connection.prepareStatement() or connection.createStatement()
,设置timeout、fetchSize参数 - 参数预编译
- 执行SQL并返回结果集
- 结果集映射成对象后返回
三、插件机制
目的:通过插件配置让用户可以在执行SQL、参数设置、结果解析时进行拦截,如进行监控等。
允许拦截的过程:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
插件初始化
- 初始化Configuration的过程中解析xml配置的plugins节点
- 生成拦截器Interceptor实例,注册到拦截器chain中
执行机制
以PageHelper拦截器为例
- 在获取SqlSession的过程中,生成Executor实例时将拦截器chain中的拦截器对其进行插桩,即进行动态代理,这个代理的过程是嵌套形成责任链
- 当然并不是每个拦截器都会拦截Executor,拦截器通过@Intercepts注解指定需要拦截的类、方法信息,插桩时由mybatis的Plugin类进行解析判断是否需要生成代理。
- 执行Executor的拦截机制与动态代理拦截机制一致,需要注意的是有可能有多层代理嵌套
四、mybatis-spring
使用Mybatis遇到问题
- sqlSessionFactory手动new实例,不被Spring管理
- 使用比较麻烦,需要每次生成sqlSession,Mapper实例,如下
SqlSession session = sqlSessionFactory.openSession();
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
} finally {
session.close();
}
- 对象不复用,对性能不友好
所以,通过依赖注入框架来管理对象的生命周期,mybatis-spring即进行两者之间的桥接。
功能:
- MapperScannerConfigurer 扫描配置basePackage指定的mapper,生成对应的BeanDefinition和MapperFactoryBean,MapperFactoryBean用于维护sqlSession并获取mapperInterface的代理实现类,最终使得mapperInterface可以被自动注入。
- 实现线程安全的sqlSesion,管理其生命周期,解决每次执行数据库操作时先手动openSession再close的操作,而mybatis defaultSqlSesion非线程安全,使用时为方法级别作用域,
- 将SqlSessionFactory,Mapper的实例都纳入Spring容器管理
mapper注入原理
MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor,后者是Spring IoC的扩展点之一,允许beanDefinition的自定义后置操作,如注册新的自定义beanDefinition,可用于动态注册beandefinition,从而动态注册bean。
MapperScannerConfigurer具体code(已简化):
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
//扫描该package下的class
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage);
}
//**ClassPathMapperScanner**.doScan
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
//扫描class,新增BeanDefinition,并注册到BeanDefinitionRegistry
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
for (BeanDefinitionHolder holder : beanDefinitions) {
// 设置beanClass,最终获得的bean将是MapperFactoryBean.getObject方法返回的对象
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
definition.setBeanClass(this.mapperFactoryBean.getClass());
definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
}
return beanDefinitions;
}
**MapperFactoryBean**实现FactoryBean<T>
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
参考
http://objcoding.com/2018/06/01/mybatis-mapper/