目录
4、mybaits中的mapper的#{}和${}有什么区别?哪种可以防止sql注入?
5、Xml映射文件中,除了常见的select|insert|update|delete标签之外,还有哪些标签?
6、每一个xml映射文件都会有一个对应的dao,Dao的工作原理是什么?
9.2、SqlSessionFactory
10、Mybatis插件原理(动态代理+责任链)
1、Mybatis架构
2、Mapper接口的实现原理(JDK动态代理)
Mybatis使用JDK动态代理,会为Mapper接口生成一个代理Proxy对象,代理对象会拦截接口方法,转而执行MappedStatement所代表的SQL,然后将SQL执行结果返回。
3、Mybatis缓存
3.1、一级缓存
sqlSession级别缓存,操作数据库时需要构建sqlSession对象,在对象中有一个HashMap用以存储缓存结构,保存在本地。
当sqlSession做增加、修改、删除、commit()、close()任意一个操作时,都会清空一级缓存
3.2、二级缓存
- 一级缓存缓存的是对象,二级缓存缓存的是数据。
- Mapper级别缓存,多个sqlSession操作同一个mapper(xml映射文件)时,可共享同一个二级缓存,同一个接口里面的相同方法,都可以共享缓存。
- 进行缓存POJO类需要实现serializable接口,因为二级缓存有可能存储在内存中,也有可能存储在硬盘中,实现该接口是为了对数据进行序列化和反序列化操作。
- SqlMapConfig.xml需要增加如下配置
- 使用xml映射文件时,需要在相应xml文件中增加<cache>标签开启二级缓存;使用注解开发时,需要在相应dao接口上新增@CacheNameSpace注解以开启二级缓存。
- 查询操作从二级缓存中取数据时,会将数据反序列化为一个新的对象,所以与一级缓存不同的是,多次从二级缓存中获取相同数据时,虽然数据相同,但是对象是不同的。
- 注意:在开启二级缓存时,对于查询操作,只有在commit或close后才能将数据刷新到二级缓存;而对于增删改操作,默认会清空二级缓存(flushCache = true)。
3.3、mybatis的二级缓存有什么问题?
二级缓存是保存在Mapper对象中的,现在有一张user表,两个Mapper文件,AMapper.xml和BMapper.xml,B修改了user表中的内容,A是感知不到的,那么再从A里查询如果用到了缓存,就是旧的数据。
4、mybaits中的mapper的#{}和${}有什么区别?哪种可以防止sql注入?
- #{}是预编译处理,$ {}是字符串替换。
- MyBatis在处理#{}时,会将SQL中的#{}替换为?号,使用PreparedStatement的set方法来赋值;MyBatis在处理 $ { } 时,就是把 ${ } 替换成变量的值。
- 使用 #{} 可以有效的防止SQL注入,提高系统安全性。
5、Xml映射文件中,除了常见的select|insert|update|delete标签之外,还有哪些标签?
动态sql标签:trim|where|set|foreach|if|choose|when|otherwise|bind;
<resultMap>、<parameterMap>、<sql>、<include>、<selectKey>;
其中<sql>为sql片段标签,通过<include>标签引入sql片段;
<selectKey>为不支持自增的主键生成策略标签。
6、每一个xml映射文件都会有一个对应的dao,Dao的工作原理是什么?
Dao接口,即Mapper接口,没有实现类;接口名+方法名,唯一定位一个MappedStatement。
原理:
JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生成代理proxy对象,代理对象proxy会拦截接口方法,转而执行MappedStatement所代表的sql,然后将sql执行结果返回。
接口的全限定名->就是xml映射文件中的namespace值。
接口的方法名->就是xml映射文件中的id值。
接口的方法参数->就是传递给sql的入参。
7、动态SQL
动态sql可以让我们在映射文件中,根据不同的逻辑,动态拼接不同的SQL;
trim|where|set|foreach|if|choose|when|otherwise|bind。
原理:使用OGNL从sql参数对象中计算表达式的值,根据表达式的值动态拼接sql,以此来完成动态sql的功能。
8、Mybatis延迟加载
仅支持 association(@One)关联对象和collection(@Many)关联对象的延迟加载。
延迟加载基于动态代理实现,当执行连表查询时,针对 association 和 collection 的查询语句,不会立即去数据库查询,而是会生成关联目标类的代理对象,并保存起来。之后会在调用关联目标类对象的方法时,才会去执行关联对象涉及的查询语句。
9、核心对象的生命周期
9.1、SqlSessionFactoryBuilder
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSessionFactoryBuilder 是用来创建SqlSessionFactory的,可以被实例化、使用和丢弃,一旦创建了SqlSesionFactory,就不再需要它了。因此,SqlSessionFactoryBuilder实例的最佳作用域是方法作用域,也就是局部方法变量。
9.2、SqlSessionFactory
SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。使用SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次(单例)。因此 SqlSessionFactory 的最佳作用域是应用作用域。
9.3、SqlSession
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
9.4、Mapper
映射器是一些由你创建的、绑定你映射的语句的接口。映射器接口的实例是从 SqlSession 中获得的。因此从技术层面讲,任何映射器实例的最大作用域是和请求它们的 SqlSession 相同的。尽管如此,映射器实例的最佳作用域是方法作用域。 也就是说,映射器实例应该在调用它们的方法中被请求,用过之后即可丢弃。
10、Mybatis插件原理(动态代理+责任链)
对mybatis来说,插件就是拦截器,支持用插件对四大核心对象尽心拦截。
- 执行器Executor (update、query、commit、rollback等方法);
- SQL语法构建器StatementHandler (prepare、parameterize、batch、updates query等方 法);
- 参数处理器ParameterHandler (getParameterObject、setParameters方法);
- 结果集处理器ResultSetHandler (handleResultSets、handleOutputParameters等方法)
- 在创建Mybatis四大组件的时候,首先会生成四大组件的一个原生对象;
- 然后通过interceptorChain.pluginAll(target),调用拦截器链(插件)对原生对象进行拦截和功能加强,返回被重重代理(多插件)后的一个代理对象;
- 返回的代理对象可以拦截到目标对象的每一个执行。
以ParameterHandler的创建为例:
11、Mybatis设计模式
- 工厂模式:SqlSessionFactory,产生SqlSession对象;
- 建造者模式:SqlSessionFactoryBuilder,将构建逻辑从对象本身抽离出来,让对象只关注功能;
- 单例模式:ErrorContext,线程级别单例,用于记录该线程的执行环境的错误信息;
- 适配器模式:Log,对jdbc、log4j等各种日志框架的适配实现;
- 代理模式:Mybatis实现的核心,如MapperProxy,采用jdk动态代理,为mapper生成一个具体的代理对象来实现Mybatis功能;
- 模板方法模式:BaseExecutor,该类实现了大部分SQL执行的逻辑,然后再把几个方法交给几个子类来实现;
- 装饰器模式:Cache
以上内容为个人学习总结,仅供学习参考,如有问题,欢迎在评论区指出,谢谢!