赞
踩
pageHelper插件是作用在Executor层的代理,在构建Executor的时候生成代理类,若有多个插件,则嵌套生成代理类(责任链模式)
接着我们看下mybatis中Plugin类构建的代理类逻辑
可以看到执行的是inteceptor的intercept方法,而inteceptor就是配置文件中声明的interceptor参数的实例化对象,在初始化xml解析的时候放入configuration当中InterceptorChain数组当中
因此方法调用实际上是调用了PageInterceptor的intercept方法
该方法中持有传入的方法名、参数、sql等信息,就可以进行sql查询了,包括缓存处理也是在这里面,因为该方法持有存二级缓存的statement对象以及存一级缓存的Exetutor对象
内部机制:分页插件内部实际上执行了两个sql,一个count语句,一个获取实际数据的语句,在源码或者实操中都可以验证
那么分页参数是怎么来的呢,因为PageInterceptor物理分页,因此需要page等参数,执行sql前,若用分页插件,我们都会添加该语句PageHelper.startPage(1,2)
可以看出分页参数是放在了ThreadLocal当中,那么有个问题,若ThreadLocal没有及时清除的话,会影响该线程后续sql查询。我们看下PageInterceptor类当中是否有释放逻辑
可以看出在afterAll方法里面执行了Threadlocal的remove操作,因此线程后续操作需要分页,需要重新执行PageHelper.startPage(1,2)
看了PageHelper的原理分析,那么最快的了解插件原理的方式,便是查看PageHelper的写法,首先是继承interceptor接口
然后是生命插件的作用域,因为插件可以作用在多个地方。PageHelper作用在Executor上面
实现interceptor的三个接口方法(mybatis约定的接口实现类)
最后需要在mybatis-config.xml中注册插件
应用场景:
该类实现了FactoryBean、InitializingBean接口,在类初始化bean属性值设置完后会调用afterPropertiesSet方法
该方法就是原生mybatis中sqlsessionFactory的构建过程
接下来就是构建DefaultSqlSession,与原生mybatis不同的是,spring对其封装了
常问的重点问题:
为什么原生mybatis中DefaultSqlSession是非线程安全的?
主要问题还是在一级缓存当中
如图,线程执行到到第一步的时候,localCache存入的是EXECUTION_PLACEHOLDER,之后执行到第二个红框内的时候才是list,那么我们在来看一下外层的判断
可以看出,并发下,多个线程如果持有同一个Sqlsession,可能导致这里转换异常。还有就是若是同个Sqlsession下。一个线程插入未完成,另外个线程写入缓存,就会导致数据的不一致性。还有就是多个Sqlsession共享一个connection的线程安全性
并发下,若每个请求创建一个Sqlsession是没有线程安全性问题,这样就导致Sqlsession无法作为单例,也没法复用的问题
spring做的处理就是持有一个单例sqlsessionTemplate,然后通过threadLocal维护线程的sqlsession
mybatis原生不用SqlSessionManager的原因是:未集成spring的情况下管理动态代理效率不高,因此直接分开管理。每次请求都自己创建sqlsession。
SqlSessionTemplate更为高效,有做引用计数等复用
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
那么我们看sqlSessionProxy是如何生成的:
basedao的两种实现思路
那么我们要如何拿到sqlsessionTemplate这个对象呢:
mybatis提供了一个扩展类SqlSessionDaoSupport,里面持有sqlsessionTemplate对象
有了这个。我们就可以自定义basedao通过反射去组装statementId,然后通过SqlSessionTemplate去查询
原理上是自己组成sql语句,mybatis解析的话跟xml解析的时候一致的。这种方式的sql底层也是通过调用@SelectProvider中type对象中相应的方法生成的sql语句。
总结:
#{}是生成预编译sql(?替换占位符):PreparedStatement的set来赋值
${}就是替换变量的值,直接的静态string替换,可能88会有sql注入
约定里,接口的权限定名称以及方法名要对应xml中的namespace以及MappedStatement的id值。那么根据反射就能获取到接口名+方法名就能获取对应的mybatis解析xml文件的MappedStatement对象。里面包括了sql语句。方法调用是通过代理模式去做的
分为物理分页和逻辑分页
mybatis可通过插件进行干预的4个对象有:ParameterHandler、ResultSetHandler、StatementHandler、Executor
实际执行的是mybatis的Interceptor接口,需要实现的方法有3个
mybatis关联查询有association和collection:区别在与一对一和一对多
延迟加载的原理:可对比hibernate的延迟加载
mybatis的延迟加载时通过代理模式。当获取对象的成员属性的时候,若为空,会去执行事先存储的sql
根据mybatis源码存储MapperStatement对象的map来看,key是nameSpace+id组成。因此相同id不影响数据存入
mybatis的3个处理器:simple、reuse(重复利用Statement)、batch,其中批处理采用batchExecutor,通过一个对象存储批处理sql
List statementList
mybaits可以自定义typeHandler的,通过集成BaseTypeHandler可以实现
参考资料:之前的源码自己debug跑通一遍的话,主流问题也是很容易理解的
主流问题参考地址:https://github.com/Snailclimb/JavaGuide/blob/master/docs/system-design/framework/mybatis/mybatis-interview.md
穷尽所有可以提升自己的机会。—共勉
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。