1.MyBatis概述
mybatis是一款基于输入、输出映射的半自动化持久层ORM开源框架,原名Ibatis,2010年更名
*ORM(object relational mapping)对象关系映射
*半自动化主要是指mybatis并没有封装大量的API用于CRUD操作,相比于hibernate即使你不了解SQL规范,依然可以通过hibernate操作数据库
2.MyBatis的优点
1.简单易学,这是最大的优点没有之一
2.封装了使用JDBC产生的大量重复代码
3.基于输入、输出映射的ORM
4.灵活多变,做过ERP系统的同学大概都了解那纷繁复杂的业务逻辑,如果用JPA来操作这些复杂的业务查询将是一场噩梦,当然并不是说JPA不好,只是需要较强的数据库设计能力
5.解耦sql代码,解除了sql语句与程序代码的耦合,将sql语句统一写在xml,便于维护管理
6.xml里面支持动态sql,这是hibernate唯一不能匹敌的地方
3.MyBatis的缺点
mybatis的优点也正是它的缺点(文无第一,武无第二)
1.编写大量的sql语句
2.既然是编写sql语句就对数据库有强依赖,不可移植,JPA是根据配置生成对应的sql,因此移植性上完爆MyBatis
4.MyBatis原理
1.应用程序启动构建SqlSessionFactoryBuilder
2.build一个session工厂
new SqlSessionFactoryBuilder().build(Rseources.getResourceAsStream("MybatisConfig.xml"));
*Mybatis核心配置文件中进行数据源相关配置,以及引入不同的namespace的mapper.xml映射文件等等
*mapper.xml里面定义各个执行sql语句以及输入输出映射的POJO
3.创建session
sqlSessionFactory.openSession();
4.通过session调用sql执行语句操作数据库
需要注意的是:
*mybatis的openSession返回的DefaultSqlSession并不是线程安全的
*mybatis提供了SqlSessionManager来管理sqlSession和sqlSessionFactory,其构造的sqlSessionProxy是线程安全的,原理是使用了ThreadLocal维护SqlSession
*与spring整合后,通过SqlSessionTemplate来管理SqlSession,在一次http请求(即一个http线程中),每次都会创建一或多个新的事务,在同一个事务中会使用同一个sqlSession,如果是一个新事务,则新创建一个sqlSession
即一个线程->多个事务->不同的sqlSession 这样保证每个线程中的sqlSession都是新创建的,所以线程安全。
5.MyBatis映射文件中#和$的区别
1.#和$符号都用于向mapper.xml中传递参数
2.#会被mybatis解析成一个占位符?,使用preparedStatement.set设置#所占位的参数,$符号是sql语句直接拼接字符串
3.所有的条件传参都应该使用#,即所有的来自客户端输入的数据都应该以#的形式传递,程序内部自己定义的变量等可以使用$符号的形式传递,例如order by 后的排序字段名,或者from后面的表或视图名称
6.MyBatis的一二级缓存(鸡肋,除了面试几乎没用)
*一级缓存是session级别的缓存,在一个session的生命周期中有效,默认就是开启的,但是与spring整合后,一级缓存基本没用了,因为spring会在每一次请求的时候都使用新的SqlSession,如果在一次请求中重复调用了同一个查询sql,还是有点用的。
*二级缓存是mapper级别的缓存,mapper.xml中的namespace可以区别不同的mybatis二级缓存,当前mapper对应的所有请求所创建的session之间共享二级缓存。但是mybatis的二级缓存还是不要用为好,二级缓存带来的些许性能上的提升,远不如其存在的安全隐患,比如Amapper中有对A表的更新操作,Bmapper也有对A表的更新操作,如果Bmapper对A表update操作,会导致A表在数据库被更新,却无法刷新Amapper所对应的二级缓存,那么我们所查询的A表的数据其实是Amapper缓存的脏数据。
7.MyBatis传参方式
一个参数
可以直接在mapper接口的方法中直接写参数
多个参数
可以使用@Param注解
直接传一个POJO
可以把参数封装进一个Map传一个map
8.MyBatis获取自增主键的值
在insert标签上添加这两个属性useGeneratedKeys="true"、keyProperty="POJO中的主键ID"
通过pojo.getId()即可获得,java代码中insert(pojo)后返回的是影响的记录数,id是通过pojo.getId()获得
9.MyBatis的输入映射和输出映射
*输入映射:parameterType和parameterMap
parameterType指定一个输入参数的类型可以是基本类型,POJO引用类型,也可以是Map类型等等
parameterMap基本不用,和resultMap类似都是把参数与字段进行绑定
*输出映射:resultType和resultMap
resultType指定一个结果集映射的类型,可以是任意类型,当然要与sql执行结果匹配,如果是POJO类型需要属性名与sql执行结果的字段名一样
resultMap指定一个结果集映射类型为当前resultMap中定义的类型,可以进行一对一(assocation)和一对多(collection)关系映射等
10.MyBatis的动态sql
*mybatis的动态sql可以让我们很轻松的动态拼接sql,这一技术的出现才真正使得程序代码和sql语句之间解耦
*mybatis动态sql常用标签:if、choose、when、otherwise、trim、where、set、foreach、bind
*大于号转义字符:> 小于号转移字符 <
*where标签的作用是消除where后紧跟的and
*trim的属性:
prefix,表示在trim标签中的语句开始,插入该属性指定的内容
prefixOverrides,表示在trim标签中的语句开始,包含该属性指定的内容,则会删除
suffixOverrides,表示在trim标签中的语句结尾,包含该属性指定的内容,则会删除
*foreach通常用在in 条件的后面,进行范围过滤
*set标签用于update语句中
*bind标签是绑定一个变量用于参数中,其作用注要是用于拼接一些参数,示例:
<select id="selectPojos" parameterType="java.lang.String" resultType="Pojo">
<bind name="paramFmt" value="'%' + param + '%'" />
SELECT * FROM BLOG WHERE title LIKE #{paramFmt}
</select>
11.SSM中调用MyBatis的mapper接口运行原理
与spring整合后使用SqlSessionTemplate(线程安全)
*创建MapperFactoryBean<T> extends SqlSessionDaoSupport
*SqlSessionDaoSupport构造方法会构建并持有一个SqlSessionTemplate,SqlSessionTemplate是SqlSession类型的
*SqlSessionTemplate最终会通过SqlSessionUtils构建一个SqlSession实例,并赋值给自己持有的sqlSessionHolder
*SqlSessionUtils构建SqlSession的过程为从当前事务中有就获取,没有就创建新的sqlsession,相关代码如下:
*可能我们会思考,为什么创建sqlSession也搞一个代理模式,上图代码里已经很清楚了,为了提交或关闭sqlSession,spring基于aop管理事务差不多底层就是这样。
*SqlSessionTemplate会把自己赋值给mapper接口的代理对象所持有的SqlSession
*MapperFactoryBean.getObject方法获取一个mapper的代理对象。mapperInterface就是我们的mapper的clazz,由MapperFactoryBean构造传入
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
*getSqlSession() 返回的即是SqlSessionTemplate
*SqlSessionTemplate.getMapper(Class<T> type)方法如下:
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
*Configuration.getMapper(Class<T> type, SqlSession sqlSession)方法如下:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
*...
然后走向这一步
还是动态代理创建了持有一个sqlSession的我们的mapper代理对象
*接下来当我们业务层执行mapper的某个方法时,就会进入下面的invoke,最后执行mapperMethod.execute方法
*最终走向MapperMethod这个类就是mybatis的核心,该类会通过sqlsession来操作xml中的sql语句
以上是与spring整合后的大致原理,因为sm的源码非常复杂,最重要的是记住一点,如果不在同一个事务中都会创建新的SqlSession,否则从当期事务中获取sqlsession,由此得以保证sqlSession的线程安全
12.Mapper接口方法可以重载吗
不可以,因为默认每一个方法名对应唯一一个statementId,如果重载会导致xml与接口映射冲突
13Mybatis延迟加载
支持延迟加载,但是mybatis的延迟加载只能使用在assocation和collection标签上
14.MyBatis分页
mybatis本身提供的RowBounds对象是对结果集在内存中的分页,也提供了分页插件接口,PageHelper是一个非常好的分页插件,只需设置一下数据库类型,然后将PageHelper通过sqlSessionFactoryBean以plugin形式注入给mybatis即可使用
15.MyBatis联合查询和嵌套查询?
*联合查询是几个表联合查询,只查询一次,通过在resultMap里面配置collection标签,再配置子resultMap即可
嵌套查询是先查一个表,根据这个表里面的 结果的外键id,再去另外一个表里面查询数据,通过collection或assocation标签里的select属性去执行对应的statemenId里的sql语句
*association一对一,collection一对多都可以进行联合查询或嵌套查询
嵌套查询用法:<collection column="columnID" select="selectID">
联合查询用法:
<association property="javaBeanPropertyName" column="javaBeanID" javaType="javaBeanTypeAlias">
<id property="id" column="table_id"></id>
<result property="property" column="column_name"></result>
</association>
*那么什么时候用嵌套查询或联合查询呢?
这其实是一个sql优化的问题:
嵌套查询是拿外层的结果去循环查询内层,如果外层结果集较小,那么考虑使用这种方式,例如大多数场景的分页查询,这种方式的性能肯定都优于联合查询。
反之如果循环次数过大,对数据库造成压力,性能还不如联合查询,所以如何取舍我们就一目了然了
*discriminator鉴别器标签
用法:当sex属性=1时,使用resultMap1映射结果集,当sex=2时用resultMap2映射结果集
<discriminator column="sex">
<case value="1" resultMap="resultMap1" />
<case value="2" resultMap="resultMap2" />
</discriminator>
<resultMap id="resultMap1">
//省略...
</resultMap>
<resultMap id="resultMap2">
//省略...
</resultMap>
16.MyBatis加载配置属性的优先级
依次降低
java代码中设置>>外部引入的properties文件中的属性>>xml里面property元素直接设置
17.MyBatis如何自定义结果集处理器
通常可以继承BaseTypeHandler并实现其setNonNullParameter和getNullableResult方法