MyBatis面试(记录学习笔记)
1.MyBatis是什么?
MyBatis是一款优秀的持久层框架,一个半ORM框架,它支持定制化SQL,存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis可以使用简单的XML或者注解来配置和映射原生类型,接口和Java的POJO为数据库中的记录。
2.ORM是什么?
ORM,对象关系映射,是为了解决关系型数据库与简单Java对象的映射关系。ORM是通过使用描述对象和数据库之间映射的元素数据,将程序中的对象自动持久化到关系型数据中。
3.MyBatis优缺点
优点:基于SQL语句编程,相当灵活,不会对应用程序或者数据的现有设计造成任何影响,SQL语句写在XML中,解除了SQL与程序代码的耦合,便于统一管理;提供XML标签,支持编写动态SQL;消除了JDBC大量冗余代码,不需要手动开关连接;很好的与各种数据库相兼容;提供映射标签,支持对象与数据库字段关系映射,提供对象关系映射标签,支持对象关系组件维护;能够与Spring很好继承。
缺点:SQL语句的编写工作量较大;SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。
4.MyBatis的执行步骤:
1.创建SQLSessionFactory
2.通过SqlSessionFactory创建SQLSession
3.通过SQLSession执行数据库操作
4.调用Session.commit方法提交事务
5.调用Session.close关闭会话
5.MyBatis的工作原理
1)读取MyBatis配置文件:mybatis-config.xml的全局配置文件,配置了MyBatis的运行环境等信息,例如数据库连接信息。
2)加载映射文件。映射文件即是SQL映射文件,该文件中配置了操作数据库的SQL语句,需要在MyBatis配置文件mybatis-config.xml中加载。mybatis-config.xml文件可以加载多个映射文件,每个文件对应数据库中一张表。
3)构建会话工厂:通过MyBatis的环境等配置信息构建会话工厂SQLSessionFactory。
4)创建会话对象:由会话工厂创建SQLSession对象,该对象中包含执行SQL语句的所有方法。
5)Executor执行器:MyBatis底层定义了一个Executor接口来操作数据库,它将根据SQLSession传递的参数动态生成多需要执行的SQL语句,同时负责查询缓存的维护。
6)MappedStatement对象:在Executor接口的执行方法中有一个MappedStatement类型的参数,该参数是对映射信息的封装,用于存储要映射的SQL语句的id,参数信息。
7)输入参数映射:输入参数类型可以是Map,List等集合类型,也可以是基本数据类型和POJO类型。
8)输出结果映射:输出结果类型可以是Map,List等集合类型,也可以死是基本数据类型和POJO类型。
6.MyBatis的功能架构是怎么样的
我们把MyBatis的功能架构分为三层:
API接口:提供给外部使用的接口API,开发人员通过这些本地API来操作数据库。接口层:接收到调用请求就会调用数据处理层来完成具体的数据处理。
数据处理层:负责具体的SQL查找,SQL解析,SQL执行和执行结果映射处理等。他主要目的是根据调用的请求完成一次数据库操作。
基础支撑层:负责最基础的功能支撑,包括连接管理,事务管理,配置加载和缓存处理,这些都是公用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。
7.MyBatis映射器,#{ }和$ { }的区别
#{}是占位符,预编译处理;$ { }是拼接符,字符串替换,没有预编译处理。MyBatis在处理#{ }时,#{ }传入参数是以字符串传入,会将SQL中的# { }替换为?,调用PreparedStatement的set方法赋值。变量替换后,#{ }对应的变量自动加上单引号;$ { }对应的变量不会加上单引号。# { }可以有效的防止SQL注入,提高系统安全性。# { }的变量替换是在DBMS中,$ { }的变量替换是在DBMS外。
8.模糊查询like语句怎么写?
'%${userName}%' 可能会引起SQL注入
"%"#{userName}"%" 因为#{}解析成SQL语句时,会在变量外侧自动添加单引号,所以%需要使用双引号。
CONCAT('%',#{userName},'%') 使用CONCAT函数
使用bind标签
<select id="findUserByUserName" parameterType="com.pojo.User" resultType="com.pojo.User">
<bind name="userName" value="'%' + userName + '%'" />
select id,userName,password from user where userName like #{userName}
</select>
9.在mapper中如何传递多个参数
顺序传参法:
这种方法SQL层表达不直观,一旦 顺序调整容易出错
public User selectUser(String name, int deptId);
<select id="selectUser" resultMap="UserResultMap">
select * from user
where user_name = #{0} and dept_id = #{1}
</select>
@param注解传参法
这种方法在参数不多的情况下比较直观
public User selectUser(@Param("userName") String name, int @Param("deptId") deptId);
<select id="selectUser" resultMap="UserResultMap">
select * from user
where user_name = #{userName} and dept_id = #{deptId}
</select>
Map传参法
#{ }里面的名称对应的是Map里面的key名称,这种方式适合传递多个参数,且参数易变能灵活传递的情况。
public User selectUser(Map<String, Object> params);
<select id="selectUser" parameterType="java.util.Map" resultMap="UserResultMap">
select * from user
where user_name = #{userName} and dept_id = #{deptId}
</select>
Java Bean传参法
#{ }里面的名称对应的是User类里面的成员属性,这种方式直观,需要新建一个实体类,拓展不容易,需要增加属性,但是代码可读性强,业务逻辑处理方便。
public User selectUser(User user);
<select id="selectUser" parameterType="com.jourwon.pojo.User" resultMap="UserResultMap">
select * from user
where user_name = #{userName} and dept_id = #{deptId}
</select>
10.MyBatis如何执行批量操作
使用foreach标签
foreach主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合,foreach标签的属性主要有item,index,collection,open,separator,close。
item:表示集合中每一个元素进行迭代时的别名。
index:指定一个名字,用于表示在迭代过程中,每次迭代到的位置,不常用。
open:表示该语句以什么开始,常用“(”。
separator:表示在每次进行迭代之间以什么符号作为分隔符,常用“,”。
close:表示以什么结束,常用“)”.
在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:
1.如果传入的是但参数且参数类型是一个List的时候,collection属性值为list。
2.如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array。
3.如果传入的参数是多个的时候,我们就需要把它们封装成一个Map,单参数也可以封装成Map,实际上如果你在传入参数的时候,在MyBatis里面也是把它封装成一个Map,Map的key就是参数名,所以这个时候collection属性值就是传入的List或者array对象在自己封装的Map里面的key。
11.当实体类中的属性名和表中的字段名不一样,怎么办?
第一种:通过在查询的SQL语句中定义字段名的别名,让字段名的别名和实体类中的属性名一致。
<select id="getOrder" parameterType="int" resultType="com.jourwon.pojo.Order">
select order_id id, order_no orderno ,order_price price form orders where order_id=#{id};
</select>
第二种:通过< resultMap >来映射字段名和实体类属性名的一一对应的关系。
<select id="getOrder" parameterType="int" resultMap="orderResultMap">
select * from orders where order_id=#{id}
</select>
<resultMap type="com.jourwon.pojo.Order" id="orderResultMap">
<!–用id属性来映射主键字段–>
<id property="id" column="order_id">
<!–用result属性来映射非主键字段,property为实体类属性名,column为数据库表中的属性–>
<result property ="orderno" column ="order_no"/>
<result property="price" column="order_price" />
</reslutMap>
12.MyBatis实现一对一,一对多有几种方式,怎么操作?
有联合查询和嵌套查询,联合查询就是几个表联合查询,只查询一次,通过在resultMap里面的association,collection节点配置一对一,一对多的类就可以完成;嵌套查询是先查一个表,根据这个表的结果的外键id,再去另外一个表里查询数据,也是通过配置association,collection,但另外一个表的查询通过select节点配置。
13.动态SQL
MyBatis的动态SQL可以让我们在XML映射文件内,以标签的形式编写动态SQL,完成逻辑判断和动态拼接SQL的功能,MyBatis提供了9中动态SQL标签:trim|where|set|foreach|if|choose|when|otherwise|bind
其执行原理为,使用OGNL从SQL参数对象中计算表达式的值,根据表达式的值动态拼接SQL,以此来完成动态SQL的功能。
14.MyBatis的一级缓存,二级缓存
一级缓存:基于PerpetualCache的HashMap本地缓存,其存储作用域为Session,当Session flush 或者close之后,该Session中的所有Cache就将清空,默认打开一级缓存。
二级缓存与一级缓存其机制相同,默认也是采用PerpetualCache,HashMap存储,不同在于其存储作用域为Mapper,并且可以自定义存储元,如Ehcache。默认不打开二级缓存,要开启二级缓存,使用耳机缓存属性类需要实现Serializable序列化接口,可在它的映射文件中配置< cache/ >。
对于缓存数据更新机制,当某一个作用域进行了增删改操作后,默认该作用域下的所有select中的缓存被clear。