Mybatis知识点总结

时间:2022-01-10 17:33:33

---恢复内容开始---

Mybatis知识点总结

1、#{}和${}的区别是什么?

答:#{}的使用场景:在表的sql映射文件中如下使用:

 <mapper namespace="com.mybatis.test2.userMapper">
<select id="insertUser" parameterType="_User">
INSERT INTO users(name,age) values(#{name},#{age})
</select>
</mapper>

#{}是sql的参数占位符,Mybatis会将sql中的#{}替换为?号,在sql执行前会使用PreparedStatement的参数设置方法,按序给sql的?号占位符设置参数值。

${}的使用场景:先在配置文件中定义好变量,再在conf.xml中如下使用:

 <environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbcDriver}" />
<property name="url" value="${jdbcUrl}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>

${}是Properties文件中的变量占位符,它可以用于标签属性值和sql内部,属于静态文本替换,比如${jdcDriver}会被静态替换为com.mysql.jdbc.Driver。

2、Xml映射文件中,除了常见的select|insert|updae|delete标签之外,还有哪些标签?

答:还有很多其他的标签:<resultMap>、<parameterMap>、<sql>、<include>、<selectKey>,

加上动态sql的9个标签,trim|where|set|foreach|if|choose|when|otherwise|bind等,

其中<sql>为sql片段标签,通过<include>标签引入sql片段,<selectKey>为不支持自增的主键生成策略标签。

3、最佳实践中,通常一个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?

答:1 Dao接口,就是人们常说的Mapper接口,---这是在基于注解的方式才会创建此接口,接口内定义了增删该查的抽象方法;

 /**
* 接口: 通过注解的方法操作数据表的CRUD
* @version 2.2 2016/9/24
* @author WGS
*
*/
public interface UserMapper { @Insert("insert into users(name,age) values(#{name},#{age})")
public int insertUser(User user); @Delete("delete from users where id=#{id} ")
public int deleteUser(int id); @Update("update users set name=#{name},age=#{age} where id=#{id}")
public int updateUser(User user); @Select("select * from users where id=#{id}")
public User getUserById(int id); @Select("select * from users")
public List<User> getAllUsers();
}

接口的全限名,就是映射文件中的namespace的值:

 <mapper namespace="com.mybatis.test3.orderMapper">
<select id="selectUser" parameterType="int" resultType="Order">
select * from users where id=#{id}
</select>
</mapper>

接口的方法名(getUserById),就是映射文件中MappedStatement 的id值(selectUser),接口方法内的参数,就是传递给sql的参数(#{id} >>> #{id})。

Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MappedStatement,举例:com.mybatis.test2.userMapper.insertUser,可以唯一找到namespace为 com.mybatis.test2.userMapper  下面  id =insertUser  的 MappedStatement。

在Mybatis中,每一个<select>、<insert>、<update>、<delete>标签,都会被解析为一个MappedStatement对象。

2 Dao接口里的方法,是不能重载的,因为是全限名+方法名的保存和寻找策略。

3 Dao接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生成代理proxy对象,代理对象proxy会拦截接口方法,转而执行MappedStatement所代表的sql,然后将sql执行结果返回。

4、Mybatis动态sql是做什么的?都有哪些动态sql?能简述一下动态sql的执行原理不?

答:Mybatis动态sql可以让我们在Xml映射文件内,以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能,Mybatis提供了9种动态sql标签                                                                          trim|where|set|foreach|if|choose|when|otherwise|bind。

 <!--
5 模糊查询:根据条件查询用户(姓名模糊匹配,年龄在指定的最小值和最大值之间)
SQL:select * from d_user where name like '%o%' and age>=13 and age<=18
-->
<!--定义操作 classes 表的sql 映射文件:userMapper2.xml -->
<mapper namespace="com.mybatis.test6.userMapper2"> <select id="getUser" parameterType="User" resultType="User">
select * from d_user where age between #{minAge} and #{maxAge} <!--动态sql语句:当输入的name!=null 时就执行下面的语句 -->
<if test=' name != "%null%" '>
and name like #{name}
</if> </select> </mapper>

其执行原理为,使用OGNL从sql参数对象中计算表达式的值,根据表达式的值动态拼接sql,以此来完成动态sql的功能。

5、Mybatis是如何解决sql字段名与实体类名不一致导致的冲突?

答:有两种方式:

1 在sql语句中定义别名:

2 通过<resultSet>标签:

 <!--
解决字段名和实体类属性名不相同的冲突
-->
<!--定义操作 users 表的sql 映射文件:orderMapper.xml -->
<mapper namespace="com.mybatis.test3.orderMapper">
<!-- 方式一:通过在sql语句中定义别名 -->
<!-- <select id="selectOrder" parameterType="int" resultType="Order">
select order_id id,order_no orderNo,order_price price from orders where order_id=#{id}
</select> --> <!-- 方式二: 通过resultMap -->
<select id="selectOrderResultMap" parameterType="int" resultMap="orderResultMap">
select * from orders where order_id = #{id}
</select> <resultMap type="Order" id="orderResultMap">
<id property="id" column="order_id"/>
<result property="orderNo" column="order_no"/>
<result property="price" column="order_price"/>
</resultMap>
</mapper>

6、Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别。

selectOne

  一对一:association

 <!--
4、关联表查询:一对一关联
如何根据id查询班级信息(包括老师信息)
Class封装了Teacher属性,即是相互关联的
-->
<!--定义操作 users 表的sql 映射文件:orderMapper.xml -->
<mapper namespace="com.mybatis.test4.classesMapper">
<!--
方式一:嵌套结果
使用嵌套结果映射来处理重复的联合结果的子集来封装联表查询的数据(去除重复的数据)
-->
<select id="getClassInfo" parameterType="int" resultMap="ClassResultMap">
select * from class c,teacher t where c.teacher_id = t.t_id and c.c_id = #{id}
</select>
<!-- 解决字段名属性名不一致问题 -->
<resultMap type="Classes" id="ClassResultMap">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<!-- 一对一关联查询 -->
<association property="teacher" column="teacher_id" javaType="Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
</resultMap> <!--
方式二:嵌套查询
通过执行另外一个SQL映射语句来返回预期的复杂类型:
SELECT * FROM class WHERE c_id =1;
SELECT * FROM teacher WHERE t_id =1;//1 是上一个查询得到的teacher_id的值
-->
<select id="getClassInfo2" parameterType="int" resultMap="ClassResultMap2" >
select * from class where c_id = #{id}
</select> <!-- 解决字段名属性名不一致问题 -->
<resultMap type="Classes" id="ClassResultMap2">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" column="teacher_id"
javaType="Teacher" select="getTeacher">
</association>
</resultMap> <select id="getTeacher" parameterType="int" resultType="Teacher">
select t_id id,t_name name from teacher where t_id=#{id}
</select> </mapper>

一对多:collection

 <?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">
<!--
4、关联表查询:一对多关联
如何根据class_id查询班级信息(包括老师和学生信息),学生信息为集合List
Class封装了Teacher和学生属性,即一张class表中包含teacher表和student表
-->
<!--定义操作 classes 表的sql 映射文件:classesMapper.xml -->
<mapper namespace="com.mybatis.test5.classesMapper2">
<!--
方式一:嵌套结果
使用嵌套结果映射来处理重复的联合结果的子集
SELECT * FROM class c, teacher t,student s
WHERE c.teacher_id=t.t_id AND c.C_id=s.class_id AND c.c_id=1
-->
<select id="getClassInfo3" parameterType="int"
resultMap="getClassMap">
SELECT * FROM class c, teacher t,student s
WHERE c.teacher_id=t.t_id AND c.C_id=s.class_id AND c.c_id=#{id}
</select>
<!-- 解决字段名和属性不一致冲突 -->
<resultMap type="Classes" id="getClassMap">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/> <!-- 关联的教师信息 -->
<association property="teacher" column="teacher_id"
javaType="Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
<!-- 关联的学生信息,是集合 -->
<collection property="students"
ofType="Student">
<id property="id" column="s_id"/>
<result property="name" column="s_name"/>
</collection>
</resultMap> <!--
方式二:嵌套查询
通过执行另外一个SQL映射语句来返回预期的复杂类型
SELECT * FROM class WHERE c_id =1 //查询后获取到teacher_id,c_id值,传入下两条语句
SELECT * FROM teacher WHERE t_id =1 //t_id=1 是上条查询得到的teacher_id值
SELECT * FROM student WHERE class_id =1 //c_id = 1 是上条查询得到的c_id值
-->
<select id="getClassInfo4" parameterType="int" resultMap="getClassMap2">
SELECT * FROM class WHERE c_id =#{id}
</select> <resultMap type="Classes" id="getClassMap2">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<association property="teacher" column="teacher_id" javaType="Teacher"
select="getTeacher"></association>
<collection property="students" column="c_id" ofType="Student"
select="getStudent"></collection>
</resultMap> <select id="getTeacher" resultType="Teacher">
SELECT t_id id,t_name name FROM teacher WHERE t_id =#{id}
</select> <select id="getStudent" resultType="Student">
SELECT s_id id,s_name name FROM student WHERE class_id =#{id}
</select>
</mapper>

7、Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?

答:Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。

在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。

它的原理是,使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用a.getB().getName(),拦截器invoke()方法发现a.getB()是null值,

那么就会单独发送事先保存好的查询关联B对象的sql,把B查询上来,然后调用a.setB(b),于是a的对象b属性就有值了,接着完成a.getB().getName()方法的调用。这就是延迟加载的基本原理。

当然了,不光是Mybatis,几乎所有的包括Hibernate,支持延迟加载的原理都是一样的。

8、为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里?

答:Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。

而Mybatis在查询关联对象或关联集合对象时,需要手动编写sql来完成,所以,称之为半自动ORM映射工具。

4、Mybatis是如何进行分页的?分页插件的原理是什么?

注:我出的。

答:Mybatis使用RowBounds对象进行分页,它是针对ResultSet结果集执行的内存分页,而非物理分页,可以在sql内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。

分页插件的基本原理是使用Mybatis提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql,根据dialect方言,添加对应的物理分页语句和物理分页参数。

举例:select * from student,拦截sql后重写为:select t.* from (select * from student)t limit 0,10

5、简述Mybatis的插件运行原理,以及如何编写一个插件。

注:我出的。

答:Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。

实现Mybatis的Interceptor接口并复写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。

6、Mybatis执行批量插入,能返回数据库主键列表吗?

注:我出的。

答:能,JDBC都能,Mybatis当然也能。

11、Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复?

注:我出的。

答:不同的Xml映射文件,如果配置了namespace,那么id可以重复;如果没有配置namespace,那么id不能重复;毕竟namespace不是必须的,只是最佳实践而已。

原因就是namespace+id是作为Map<String, MappedStatement>的key使用的,如果没有namespace,就剩下id,那么,id重复会导致数据互相覆盖。有了namespace,自然id就可以重复,namespace不同,namespace+id自然也就不同。

12、Mybatis中如何执行批处理?

注:我出的。

答:使用BatchExecutor完成批处理。

13、Mybatis都有哪些Executor执行器?它们之间的区别是什么?

注:我出的

答:Mybatis有三种基本的Executor执行器,SimpleExecutor、ReuseExecutor、BatchExecutor。

SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。

ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String, Statement>内,供下一次使用。简言之,就是重复使用Statement对象。

BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。

作用范围:Executor的这些特点,都严格限制在SqlSession生命周期范围内。

14、Mybatis中如何指定使用哪一种Executor执行器?

注:我出的

答:在Mybatis配置文件中,可以指定默认的ExecutorType执行器类型,也可以手动给DefaultSqlSessionFactory的创建SqlSession的方法传递ExecutorType类型参数。

15、Mybatis是否可以映射Enum枚举类?

注:我出的

答:Mybatis可以映射枚举类,不单可以映射枚举类,Mybatis可以映射任何对象到表的一列上。映射方式为自定义一个TypeHandler,实现TypeHandler的setParameter()和getResult()接口方法。TypeHandler有两个作用,一是完成从javaType至jdbcType的转换,二是完成jdbcType至javaType的转换,体现为setParameter()和getResult()两个方法,分别代表设置sql问号占位符参数和获取列查询结果。

16、Mybatis映射文件中,如果A标签通过include引用了B标签的内容,请问,B标签能否定义在A标签的后面,还是说必须定义在A标签的前面?

注:我出的

答:虽然Mybatis解析Xml映射文件是按照顺序解析的,但是,被引用的B标签依然可以定义在任何地方,Mybatis都可以正确识别。

原理是,Mybatis解析A标签,发现A标签引用了B标签,但是B标签尚未解析到,尚不存在,此时,Mybatis会将A标签标记为未解析状态,然后继续解析余下的标签,包含B标签,待所有标签解析完毕,Mybatis会重新解析那些被标记为未解析的标签,此时再解析A标签时,B标签已经存在,A标签也就可以正常解析完成了。

---恢复内容结束---