java面试总结(8)MyBatis

本文深入讲解MyBatis框架,涵盖其工作原理、优缺点、动态SQL、缓存机制、参数处理、结果映射及SSM整合等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值