mybatis面试题

mybatis面试题

一、基础知识

1.ORM

在这里插入图片描述

4.动态sql语句

我们根据实体类的不同取值,使用不同的 SQL语句来进行查询。比如在 id如果不为空时可以根据id查询,如果username 不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。

<select id="findByCondition" parameterType="student" resultType="student">
    select * from student
    <where>
        <if test="id!=0">
            and id=#{id}
        </if>
        <if test="username!=null">
            and username=#{username}
        </if>
    </where>
</select>

5.#{}和${}的区别

在这里插入图片描述

二、mybatis功能架构和执行流程

1.功能架构

在这里插入图片描述
1.API接口层:提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。

2.数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。

3.基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。

2.执行流程

mybatis运行时要先通过resources把核心配置文件也就是mybatis.xml文件加载进来,然后通过xmlConfigBulider来解析,解析完成后把结果放入configuration中,并把它作为参数传入到build()方法中,并返回一个defaultSQLSessionFactory。我们再调用openSession()方法,来获取SqlSession,在构建SqlSession的同时还需要transaction和executor用于后续执行操作。
在这里插入图片描述

三、mybatis一级缓存和二级缓存

1.概念

一级缓存

也叫做会话级缓存,生命周期仅存在于当前会话,不可以直接关关闭。但可以通过flushCache和localCacheScope对其做相应控制,1级缓存无法跨线程使用

二级缓存

应用级缓存,缓存对象存在于整个应用周期,而且可以跨线程使用,二级缓存有更高的命中率,适合缓存一些修改较少的数据。在流程上是先访问二级缓存,在访问一级缓存。
使用场景:静态表,字典表,权限表一般用来放在二级缓存中

2.两者区别

在这里插入图片描述

3.一级缓存执行流程

在这里插入图片描述
每个SqlSession中持有了Executor,每个Executor中有一个LocalCache。当用户发起查询时,MyBatis根据当前执行的语句生成MappedStatement,在Local Cache进行查询,如果缓存命中的话,直接返回结果给用户,如果缓存没有命中的话,查询数据库,结果写入Local Cache,最后返回结果给用户。

4.一级缓存特点

在这里插入图片描述

5.二级缓存执行流程

在上文中提到的一级缓存中,其最大的共享范围就是一个SqlSession内部,如果多个SqlSession之间需要共享缓存,则需要使用到二级缓存。开启二级缓存后,会使用CachingExecutor装饰Executor,进入一级缓存的查询流程前,先在CachingExecutor进行二级缓存的查询,具体的工作流程如下所示。

在这里插入图片描述
二级缓存开启后,同一个namespace下的所有操作语句,都影响着同一个Cache,即二级缓存被多个SqlSession共享,是一个全局的变量。

当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库。

6.二级缓存特点

在这里插入图片描述

7.Mybatis的mapper.xml配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace:填写映射当前的Mapper接口,所有的增删改查的参数和返回值类型,
		就可以直接填写缩写,不区分大小写,直接通过方法名去找类型-->
<mapper namespace="com.itheima.mapper.UserMapper">
	<!-- sql:里面可以写入一个共同的sql代码,用于提取重复的代码。
		要使用该代码的时候就直接使用<include>标签
		id:为提取的sql代码,取一个id,起标识作用
		 -->
	<sql id="select">
		select * from user
	</sql>
	<!-- public User findUserById(int id); 
		id:填写在XxxMapper接口中的方法名
		parameterType:填写参数的类型
		resultType:填写方法中返回值的类型,不用写全路径,不区分大小写
	-->
	<select id="findUserById" parameterType="int" resultType="user">
		<!-- include:用于加载提取公共的sql语句,与<sql>标签对应
		       refid:填写<sql>标签中的id属性
		 -->
		<include refid="select"></include>
		where id = #{id}
	</select>
	<!-- resultMap属性:与resultMap标签一起使用,填写resultMap标签中定义的id属性 -->
	<select id="findAllOrders" resultMap="orders">
		select * from orders
	</select>
	<!-- resultMap标签:用于自定义封装结果
		type:最终结果还是封装到实体类中,type就是指定封装到哪一个类中
		id:<select>标签中的resultMap中的属性一直,一定要唯一
		<id>:该标签是指定主键封装到实体类中的哪一个属性(可以省略)
		<result>:该标签是其他的列封装到实体类中,一般只需填写实体类中的属性与表中列不同的项即可
			property:填写实体类中的属性,column:填写表中的列名
	 -->
	<resultMap type="Orders" id="orders">
		<id property="id" column="id"/>
		<result property="userId" column="user_id"/>
	</resultMap>
	<!--  public void addUser(User user);
		insert:用于执行添加语句;update:执行更新语句
	   同样 delete:执行删除语句
	 -->
	<insert id="addUser" parameterType="user">
		<!-- selectKey配置主键信息的标签
			keyColumn:对应数据库表中的主键列
			keyProperty:对应实体类中的属性
			after:代表执行下面代码之前,先执行当前里面的代码
		 -->
		 <selectKey keyColumn="id" keyProperty="id" order="AFTER" resultType="int">
		 	select LAST_INSERT_ID()
		 </selectKey>
		insert into user
		(username,sex,address)
		values(#{username},#{sex},#{address})
	</insert>
	<!-- public List<User> findUserBySexAndUsername(User user); -->
	<select id="findUserBySexAndUsername" parameterType="User" resultType="user">
		<!--select * from user  where 1=1 -->
		<include refid="select"></include>
		<!-- where标签:一个where条件语句,通常和<if>标签混合使用 -->
		<where>
		<!-- if标签:执行一个判断语句,成立才会执行标签体内的sql语句
			test:写上条件判断语句
			注意:这里每一个if前面都尽量加上and,如果你是第一个条件,框架会自动帮你把and截取,如果是第二个if就不能省略and
		 -->
		<if test="sex != null and sex != ''">
			and sex = #{sex} 
		</if>
		<if test="username != null and username != ''">
			and username like '%${username}%'
		</if>
		</where>
	</select>
	
	<!-- public List<User> findUserByIds(QueryVo vo); -->
	<!-- QueryVo:是一个实体包装类,通常用于封装实体类之外的一些属性-->
	<select id="findUserByIds" parameterType="QueryVo" resultType="user">
		<include refid="select"></include>
		<where>
		<!-- foreach:循环语句,通常多用于参数是集合时,需要对参数进行遍历出来,再进行赋值查询
			collection:参数类型中的集合、数组的名字,例:下面的ids就是QueryVo这个类中的list集合的名字
			item:为遍历该集合起一个变量名,遍历出来的每一个字,都赋值到这个item中
			open:在sql语句前面添加的sql片段
			close:在sql语句后面添加的sql片段
			separator:指定遍历元素之前用什么分隔符
		 -->
		<foreach collection="ids" item="id" open="id in(" close=")" separator=",">
			#{id}
		</foreach>
		</where>
	</select>
</mapper>

四、mybatis用到的设计模式

1.建造者模式

概述

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。相对于工厂模式会产出一个完整的产品,建造者模式应用于更加复杂的对象的构建,甚至只会构建产品的一个部分。

框架中应用

在这里插入图片描述
在这里插入图片描述

2.代理模式

代理模式可以认为是Mybatis的核心使用的模式,正是由于这个模式,我们只需要编写Mapper.java接口,不需要实现,由Mybatis后台帮我们完成具体SQL的执行。

当我们使用Configuration的getMapper方法时,会调用mapperRegistry.getMapper方法,而该方法又会调用mapperProxyFactory.newInstance(sqlSession)来生成一个具体的代理:

在这里,先通过T newInstance(SqlSession sqlSession)方法会得到一个MapperProxy对象,然后调用T newInstance(MapperProxy mapperProxy)生成代理对象然后返回。

非常典型的,该MapperProxy类实现了InvocationHandler接口,并且实现了该接口的invoke方法。

通过这种方式,我们只需要编写Mapper.java接口类,当真正执行一个Mapper接口的时候,就会转发给MapperProxy.invoke方法,而该方法则会调用后续的sqlSession.cud>executor.execute>prepareStatement等一系列方法,完成SQL的执行和返回。

3.装饰器模式

概念

装饰模式(Decorator Pattern) :动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。

框架中应用

在mybatis中,缓存的功能由根接口Cache(org.apache.ibatis.cache.Cache)定义。整个体系采用装饰器设计模式,数据存储和缓存的基本功能由PerpetualCache(org.apache.ibatis.cache.impl.PerpetualCache)永久缓存实现,然后通过一系列的装饰器来对PerpetualCache永久缓存进行缓存策略等方便的控制。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值