MyBatis面经

MyBatis常见面试题

!!本文主要是博主总结看着玩的,不具有很高的参考价值,慎重

1、MyBatis是什么?MyBatis工作原理?MyBatis的使用场景有哪些?

MyBatis是一款优秀的持久层框架,它是一个基于Java语言的ORM框架,通过XML或注解的方式将Java对象和数据库中的表进行映射,实现持久化操作。

mybatis工作原理:
大家可以看一下源码。推荐文章MyBatis底层原理小白版本

mybatis配置文件,包括Mybatis全局配置文件和Mybatis映射文件,其中全局配置文件配置了数据源、事务等信息;映射文件配置了SQL执行相关的信息。

mybatis通过读取配置文件信息(全局配置文件和映射文件),构造出SqlSessionFactory,即会话工厂。

通过SqlSessionFactory,可以创建SqlSession即会话。

Mybatis是通过SqlSession来操作数据库的。SqlSession本身不能直接操作数据库,它是通过底层的Executor执行器接口来操作数据库的。

Executor接口有两个实现类,一个是普通执行器,一个是缓存执行器(默认)。Executor执行器要处理的SQL信息是封装到一个底层对象MappedStatement中。

MyBatis在以下场景下都有着广泛的应用:

1、数据库访问操作:MyBatis提供了灵活的SQL语句编写和自定义的SQL映射,可以更加细粒度地控制SQL语句的执行,因此适用于需要对数据库进行复杂操作的场景。

2、大数据量操作:MyBatis的批量操作和分页查询功能能够很好地处理大量数据的访问和操作,因此适用于需要对大量数据进行操作的场景。

3、高并发访问:MyBatis的缓存机制可以有效地减少数据库访问的次数,提高系统的并发能力,因此适用于需要支持高并发访问的场景。

4、数据库事务处理:MyBatis支持事务处理机制,可以对多个操作进行事务管理,因此适用于需要进行数据库事务处理的场景。

5、多数据源操作:MyBatis可以支持多数据源的操作,可以同时连接多个数据库进行操作,因此适用于需要同时操作多个数据库的场景。
综上所述,MyBatis可以广泛应用于各种需要进行数据库访问和ORM操作的场景,特别是在需要高并发、大数据量和复杂操作的场景下更加适用。

2、MyBatis具有哪些优点?

MyBatis具有以下优点:

1、灵活性高:MyBatis提供了丰富的映射语句和灵活的配置,可以满足不同的需求。

2、易于掌握:MyBatis的学习曲线比较平稳,上手比较容易。

3、性能较好:MyBatis通过对SQL语句和结果集的缓存,可以提高系统的性能。

4、可以自由控制SQL:MyBatis支持使用动态SQL来构建复杂的SQL语句,可以自由控制SQL的执行。

5、易于集成:MyBatis可以与Spring等框架进行集成,方便与其他框架一起使用。

总之,MyBatis是一款功能强大、易于使用、性能出色的ORM框架,被广泛应用于各种Java项目中。

3、MyBatis的工作原理以及工作流程是什么?

依旧建议看底层原理MyBatis底层原理小白版本

MyBatis的工作原理可以简单地概括为以下三个步骤:

1、通过SqlSessionFactoryBuilder创建SqlSessionFactory对象;

2、通过SqlSessionFactory对象的openSession方法创建SqlSession对象;

3、通过SqlSession对象操作数据库,包括执行SQL语句、提交事务等。

MyBatis的工作流程如下:

1、加载MyBatis的配置文件,包括数据库连接信息、映射文件的位置等;

2、解析配置文件,创建Configuration对象,用于保存MyBatis的配置信息;

3、创建SqlSessionFactory(线程安全)对象,是用于创建SqlSession对象的工厂;

4、创建SqlSession对象,用于执行SQL语句的,一个SqlSession对象代表一次数据库连接;

5、通过SqlSession对象执行SQL语句,包括查询、插入、更新、删除等操作;

6、提交事务,如果需要的话;

7、关闭SqlSession对象,释放资源。

4、什么是MyBatis的接口绑定?有哪些实现方式?

接口绑定,就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定,我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置。

接口绑定有两种实现方式

  1. 通过注解绑定,就是在接口的方法上面加上 @Select、@Update等注解,里面包含Sql语句来绑定;
  2. 通过xml里面写SQL来绑定, 在这种情况下,要指定xml映射文件里面的namespace必须为接口的全路径名。当Sql语句比较简单时候,用注解绑定, 当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多。

5、当实体类中的属性名和表中的字段名不一样,怎么办?

  1. 查询sql的时候给查询出来的列起别名: 我们应尽量给列起别名而不是修改对应类的属性名,后者牵扯的太多

    <!-- User{ int id; String pwd} -->
    <select id = "getUser" parameterType="int" resultType="user">
        select id, password pwd from user where id = #{id}
    </select>
    
  2. 通过来映射字段名和实体类属性名的一一对应的关系。

    <select id = "getUser" parameterType="int" resultMap="userMap">
        select * from user where id = #{id}
    </select>
    <resultMap type="user" id="userMap">
        <!–用result属性来映射非主键字段,property为实体类属性名,column为数据库表中的属性–>
        <result property ="id" column ="id"/>
        <result property="pwd" column="password" />
    </reslutMap>
    

6、在mapper中如何传递多个参数?

1. 顺序传参

public User selectUser(String name, int deptId);
<select id="selectUser" resultMap="UserResultMap">
    select * from user
    where user_name = #{0} and dept_id = #{1}
</select>
#{}里面的数字代表传入参数的顺序。
这种方法不建议使用,sql层表达不直观,且一旦顺序调整容易出错。
  1. @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> 
    #{}里面的名称对应的是注解@Param括号里面修饰的名称。
    这种方法在参数不多的情况还是比较直观的,推荐使用。
    
  2. Map传参法

    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>
    #{}里面的名称对应的是Map里面的key名称。
    这种方法适合传递多个参数,且参数易变能灵活传递的情况。
    
  3. Java Bean传参法

    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>
    #{}里面的名称对应的是User类里面的成员属性。
    这种方法直观,需要建一个实体类,扩展不容易,需要加属性,但代码可读性强,业务逻辑处理方便,推荐使用。
    

7、MyBatis中的#{}和${}有什么区别?

  1. #{}是占位符,预编译处理;${}是拼接符,字符串替换,没有预编译处理。

  2. Mybatis在处理#{}时,#{}传入参数是以字符串传入,会将SQL中的#{}替换为?号,调用PreparedStatement的set方法来赋值。

  3. Mybatis在处理 ${}时,是原值传入,就是把${}替换成变量的值,相当于JDBC中的Statement编译

  4. 变量替换后,#{} 对应的变量自动加上单引号 ‘ ’;变量替换后,${} 对应的变量不会加上单引号 ‘ ’

  5. #{} 可以有效的防止SQL注入,提高系统安全性;${} 不能防止SQL 注入

    -- 假如 有个 id = 5 or 1=1
    delete from 表名 where id = #{id} 
    delete from 表名 where id = ${id}
    
    -- 执行上面两条sql
    第一个会变成这样-> delete from 表名 where id = '5 or 1=1';--执行肯定是失败的
    第一个会变成这样-> delete from 表名 where id = 5 or 1=1;--这种情况就是sql注入,如果没有id为5的,那么就会判断 1=1,
    这样结果是必然成立的,那么 delete 就会将整个表的数据全都删掉。对于任意一行 where 1=1 都是成立的
    
  6. #{} 的变量替换是在DBMS 中;${} 的变量替换是在 DBMS 外

怎么理解第六点:

#{}mybaits 中的一种动态sql的用法#{} 用于预编译,由DBMS进行处理。

${} 是一种在很多基于文本的配置文件(比如属性文件、XML配置文件等)中常用的变量替换语法。

在这种情况下,${} 用于将变量的值直接替换到SQL语句或者其他配置中。这种替换发生在应用程序加载配置文件的时候,而不是在DBMS中

8、模糊查询like语句该怎么写?

  1. '%${question}%' 可能引起SQL注入,不推荐

    <select id="getStudent2" parameterType="String" resultMap="StudentTeacher">
        select * from student where name like '%${name}%'
    </select>
    
  2. "%"#{question}"%" 注意:因为#{…}解析成sql语句时候,会在变量外侧自动加单引号 ’ ',所以这里 % 需要使用双引号" ",不能使用单引号 ’ ’ ,不然会查不到任何结果。

    <select id="getStudent2" parameterType="String" resultMap="StudentTeacher">
        select * from student where name like "%"#{name}"%"
    </select>
    
  3. CONCAT(‘%’,#{question},‘%’) 使用CONCAT()函数,推荐

    oracle的concat函数只支持两个参数,但是mysql的concat函数支持好几个,更换数据库就要重新sql

    <select id="getStudent2" parameterType="String" resultMap="StudentTeacher">
        select * from student where name like concat('%',#{name},'%')
    </select>
    
  4. 使用bind标签

    bind元素的作用是通过OGNL表达式去自定义一个上下文变量,这样更方便使用。在进行模糊查询时,如果是Mysql数据库,常常会用到的是一个concat,它用%和参数相连。然而在Oracle数据库则没有,Oracle数据库使用||进行连接。这样SQL就需要提供两种形式去实现。但是有了bind元素,就不必使用数据库的语言,而是使用MyBatis的动态SQL实现。

    <select id="getStudent2" parameterType="String" resultMap="StudentTeacher">
        <bind name="pattern" value="'%'+name+'%'"/>
        select * from student where name like #{pattern}
    </select>
    

9、MyBatis的动态SQL有哪些?如何使用动态SQL?

1、IF

<select id="findUserById" parameterType="int" resultType="User">
    select * from user where 1 = 1
    <if test="id != null">
        and id = #{id}
    </if>
    <if test="username != null">
        and username = #{username}
    </if>
</select>

2、Where元素: Where元素用于将多个条件拼接在一起,如果第一个条件为真,则拼接"WHERE"关键字,否则拼接"AND"关键字,二人且若子句的开头为 ‘and’ 或者 ‘or’ 则也会删掉

<select id="findUser" parameterType="User" resultType="User">
    select * from user
    <where>
        <!--只有当下面的条件有任何一个返回值的时候才会拼接 where-->
        <if test="id != null">
            and id = #{id}
        </if>
        <if test="username != null">
            and username = #{username}
        </if>
    </where>
</select>

3、choose (when, otherwise) :Choose元素类似于Java中的switch语句,用于选择一个分支执行。例如:

<select id="findUser" parameterType="User" resultType="User">
    select * from user
    <where>
        <choose>
            <when test="id != null">
                and id = #{id}
            </when>
            <when test="username != null">
                and username = #{username}
            </when>
            <otherwise>
                and 1 = 1
            </otherwise>
        </choose>
    </where>
</select>

4、Set: Set元素用于更新操作,通过Set元素可以动态地设置更新语句中的字段。例如:

<update id="updateUser" parameterType="User">
    update user
    <set>
        <if test="username != null">
            username = #{username},
        </if>
        <if test="password != null">
            password = #{password},
        </if>
    </set>
    where id = #{id}
</update>

10、Mybatis如何执行批量操作

使用 <foreach> 标签

<!--
select * from blog where 1=1 and (id = 1 or id=2 or id=3)
我们现在传递一个万能的map,map中存在一个集合
-->
<select id="queryBlogForeach" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <foreach collection="ids" item="id" open="and (" separator="or" close=")">
            id = #{id}
        </foreach>
    </where>
</select>

11、MyBatis实现一对一(多对一)有几种方式?具体怎么操作的?

第一种,按照结果嵌套查询

这种就简单的理解为 :

我们已经把需要的表并在一起了,我们只需要选择我们需要的属性进行映射就行了。

<select id="getStudent2" resultMap="StudentTeacher2">
    select  s.id sid,s.name sname, t.name tname
    from student s, teacher t
    where s.tid = t.id
</select>

<resultMap id="StudentTeacher2" type="student">
    <result property="id" column="sid"/>
    <result property="name" column="name"/>
    <association property="teacher" javaType="teacher">
        <result property="name" column="tname"/>
    </association>
</resultMap>
<!--
<association property="teacher" javaType="teacher">
        <result property="name" column="tname"/>
</association>
这种比较简单,<association>用来映射Student类的teacher属性 这个属性的类型也就是 javaType="teacher";

<select id="getStudent2">中我们需要的是 teacher的name属性,那么我们只需要映射一下tname就行
<result property="name" column="tname"/>
-->    

第二种:按查询进行嵌套处理

这中就简单的理解为子查询

我们先查学生表的所有信息,将查到的tid 给子查询,让子查询找到对应的数据,再把结果返回

<select id="getStudent" resultMap="StudentTeacher">
    select * from student
<!-- select id,name,tid from student -->    
</select>

<resultMap id="StudentTeacher" type="student">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <association property="teacher" javaType="teacher" select="getTeacher"column="tid"/>
</resultMap>
<!--
<association property="teacher" javaType="teacher" select="getTeacher"column="tid"/>
我们可以这样理解这句话:
我需要映射Student类的teacher属性,这个属性的java类型是Teacher,这个属性的数据从哪里来呢,我们需要调用 getTeacher 这个sql语句去查询,同时要传递给这个sql语句 参数进行查询。这个参数就是我们从 getStudent 查询语句中查出来的 tid 
-->

<select id="getTeacher" resultType="teacher">
    select * from teacher where id = #{??};
</select>
<!--
我们发现 id = #{??} #{}里面的内容即使改变了,对查询语句的结果是没有任何影响的
我们可以将理解为这样  select * from teacher where id = ?; 
我们知道sql中有预编译,也许本质就是下面这样呢 
preparedStatement.setInt(1, resuleSet.getInt("tid"));
当然,具体流程是啥我不清楚。只是想让大家明白 #{??} 里面的内用没啥影响,但不能为空,大家可以自己试一下。
-->

12、MyBatis实现一对多有几种方式,怎么操作的?

第一种,按照结果嵌套查询

<select id="getTeacher" resultMap="TeacherStudent">
    select s.id sid, s.name sname, t.name tname, t.id tid
    from student s,teacher t
    where s.tid = t.id and t.id = #{tid}
</select>

<resultMap id="TeacherStudent" type="Teacher2">
    <result property="id" column="tid"/>
    <result property="name" column="tname"/>

    <collection property="students" javaType="ArrayList" ofType="Student2">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <result property="tid" column="tid"/>
    </collection>
</resultMap>
<!--
只要理解了上面的association,这个也就很好理解了
我们使用<collection> 去映射teacher类的students属性。
students 的 java类型是个集合 , 集合里面存放的数据类型也就是泛型,是 ofType="Student2"
-->    

第二种:按照查询进行嵌套

<select id="getTeacherById" resultMap="TeacherStudent">
	select id ddd, name from teacher where id = #{tid}
</select>

<resultMap id="TeacherStudent" type="Teacher2">
	<result property="id" column="ddd"/>
	<result property="name" column="name"/>
	<collection property="students" javaType="ArrayList" ofType="Student2"
            select="getStudentByTid" column="ddd"/>
</resultMap>

<select id="getStudentByTid" resultType="student2">
	select * from student where tid = #{hh}
</select>
<!--
同样, getStudentByTid 中的参数仍旧跟 #{}中的内容没有关系
而且,切记 ! column一定是跟我们从数据库中查出来的列名保持一致
-->

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

依旧建议看一下源码

Dao接口,就是人们常说的Mapper接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名,就是映射文件中MappedStatement的id值,接口方法内的参数,就是传递给sql的参数。Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可唯一定位一个MappedStatement,举例:

com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到namespace为com.mybatis3.mappers.StudentDao下面id = findStudentById的MappedStatement。在Mybatis中,每一个、、、标签,都会被解析为一个MappedStatement对象。

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

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

另一种有争议的答案:
mybatis层面可以实现有条件的重载,但xml配置文件中的id是唯一不可重复的,所以对于重载的多个(同名)方法,都将指向同一个MapperStatement,但有潜在的异常风险,一般不建议使用

public Data getOne()
public Data getOne(@Parme("id") long id)
<select id="getOne" resultType="xxx.xxx.Data">
	SELECT * FROM data_table
	<if test="id != null">
		WHERE id = #{id}
	</if>
</select>
<!--
同时满足以下条件可以实现重载
a. 只有一个无参方法和一个有参方法
b. 有多个有参方法时,参数的数量需要相同,且使用相同的@Param值
-->

14、Mybatis是如何进行分页的?

MyBatis的分页方式有五种:基于关键字的分页、基于MyBatis物理分页插件的分页、基于MyBatis逻辑分页插件的分页、基于数据库的分页、基于应用程序的分页。

1、基于limit语句的分页

<select id="getStudent" parameterType="map" resultMap="StudentTeacher">
    select * from student limit #{offset},#{count};
</select>

2、基于RowBounds:RowBounds是将所有符合条件的数据全都查询到内存中,然后在内存中对数据进行分页,若数据量大,千万别使用RowBounds

mappep.xml里面正常配置,不用对rowBounds任何操作。mybatis的拦截器自动操作rowBounds进行分页。

XML<select id="getUserByRowBounds"  resultType="student2" >
        select * from student
</select>
接口:
List<Student2> getUserByRowBounds(RowBounds rowBounds);
使用:
RowBounds rowBounds = new RowBounds(0,2);
List<Student2> userList = mapper.getUserByRowBounds(rowBounds);

3、基于插件PageHelper(其实自定义插件也行)

导入相应依赖(注意版本匹配问题)

<dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper</artifactId>
            <version>5.2.0</version>
</dependency>

配置文件(注意依赖的版本)

<plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
<!--            <property name="dialect" value="mysql"/>-->
        </plugin>
</plugins>
XML<select id="getUserByPageHelper"  resultType="student2" >
        select * from student
</select>
底层:
List<Student2> getUserByPageHelper();
使用:
int pageNo = 1;//第一页
int pageSize = 2;//每页两条数据
PageHelper.startPage(pageNo,pageSize);//注意,只会匹配最近的一条sql语句,每次使用,都要重新声明
List<Student2> userList = mapper.getUserByPageHelper();

15、MyBatis插件原理以及自定义插件

MyBatis的插件机制是一种扩展机制,可以在MyBatis执行SQL语句的各个阶段中插入自定义的逻辑,用于扩展或修改MyBatis的功能。

MyBatis的插件机制主要包括以下几个接口:

Interceptor:Interceptor是MyBatis插件的核心接口,所有的插件都要实现该接口,Interceptor中定义了四个方法,分别是intercept、plugin、setProperties和getProperties。

Executor:Executor是MyBatis的执行器,用于执行SQL语句。

MyBatis提供了三种执行器:SimpleExecutor、ReuseExecutor和BatchExecutor,Executor中定义了一些方法,如query、update等,可以被Interceptor拦截。

ParameterHandler:ParameterHandler用于处理JDBC ParameterStatement的参数,同样可以被Interceptor拦截。

ResultSetHandler:ResultSetHandler用于处理JDBC ResultSet的接口,同样可以被Interceptor拦截。

StatementHandler:StatementHandler是用于处理JDBC Statement的接口,同样可以被Interceptor拦截。

编写MyBatis插件的步骤如下:

1、实现Interceptor接口,并实现intercept方法,该方法中可以插入自定义的逻辑。

2、实现plugin方法,该方法用于返回一个代理对象,该代理对象包含了原始对象和Interceptor对象的逻辑,用于拦截原始对象的方法调用。

3、在mybatis-config.xml中配置插件,将Interceptor的实现类添加到plugins节点中。

下面是一个简单的插件实现示例:

@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class MyPlugin implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 在执行update方法前,输出日志
        System.out.println("Before executing update method.");
        Object result = invocation.proceed();
        // 在执行update方法后,输出日志
        System.out.println("After executing update method.");
        return result;
    }
    @Override
    public Object plugin(Object target) {
        // 生成代理对象
        return Plugin.wrap(target, this);
    }
    @Override
    public void setProperties(Properties properties) {
        // 设置插件属性
    }

}

在上面的示例中,我们定义了一个MyPlugin类,实现了Interceptor接口,重写了intercept、plugin和setProperties方法。

注解@Intercepts用于标记拦截的对象和方法,@Signature用于标记拦截的方法参数类型。

intercept方法中,我们可以插入自定义的逻辑,例如在执行update方法前后输出日志。

plugin方法中,我们生成了一个代理对象,并将该代理对象返回。

setProperties方法中,我们可以设置插件的属性。

在完成插件的编写后,我们需要在mybatis-config.xml文件中配置插件,

例如:

<plugins>
    <plugin interceptor="com.example.MyPlugin">
        <property name="property1" value="value1"/>
        <property name="property2" value="value2"/>
    </plugin>
</plugins>

在上面的配置中,我们将MyPlugin类作为插件的拦截器,并通过标签设置了插件的属性。

总之,MyBatis的插件机制是一种非常灵活的扩展机制,可以通过自定义的插件实现一些自己的功能,例如日志记录、缓存、分页等。

16、MyBatis中如何处理延迟加载?

MyBatis中的延迟加载可以通过两种方式来实现:基于动态代理的延迟加载和基于MyBatis自带的延迟加载机制

基于动态代理的延迟加载

基于动态代理的延迟加载是MyBatis默认的延迟加载方式。

当查询主对象时,MyBatis只会查询主对象,而不会查询关联对象。

当需要使用关联对象时,MyBatis会通过动态代理的方式生成代理对象,再通过代理对象加载关联对象。

需要使用延迟加载功能时,只需要在Mapper.xml文件中使用或标签指定关联对象即可。

例如:

<!-- 延迟加载一对一关系的关联对象 -->
<association property="author" column="author_id" select="getAuthorById" fetchType="lazy"/>
    
<!-- 延迟加载一对多关系的关联对象 -->
<collection property="posts" column="id" select="getPostsByBlogId" fetchType="lazy"/>

基于MyBatis自带的延迟加载机制

基于MyBatis自带的延迟加载机制是指使用MyBatis提供的专门负责延迟加载的组件进行延迟加载。

该组件需要在MyBatis的配置文件中进行配置。

使用该组件时,需要在查询语句中使用ResultMap来指定关联对象,以便在查询时进行关联对象的加载。

例如:

<!-- 配置MyBatis自带的延迟加载机制 -->
<settings>
   <setting name="lazyLoadingEnabled" value="true"/>
   <setting name="aggressiveLazyLoading" value="false"/>
</settings>
<!-- 延迟加载一对一关系的关联对象 -->
<resultMap id="blogResult" type="Blog">
   <id property="id" column="id"/>
   <result property="title" column="title"/>
   <association property="author" column="author_id" 
      select="getAuthorById" fetchType="lazy"/>
</resultMap>
    
<!-- 延迟加载一对多关系的关联对象 -->
<resultMap id="blogResult" type="Blog">
   <id property="id" column="id"/>
   <result property="title" column="title"/>
   <collection property="posts" column="id" 
      select="getPostsByBlogId" fetchType="lazy"/>
</resultMap>

需要注意的是,基于动态代理的延迟加载方式只支持单个对象的延迟加载,即只能延迟加载一对一关系的关联对象;

而基于MyBatis自带的延迟加载机制则支持一对多关系的关联对象的延迟加载。因此,在使用延迟加载时需要根据具体的场景进行选择。

17、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批处理相同。

18、MyBatis缓存机制

MyBatis的缓存机制是指在执行SQL语句时,将查询结果缓存起来,下次执行相同的SQL语句时,直接从缓存中获取结果,从而提高查询效率。MyBatis的缓存机制分为一级缓存和二级缓存两种。查询顺序为:先查二级缓存,再查一级缓存,最后去数据库

1、一级缓存

一级缓存是指MyBatis的SqlSession级别的缓存,即在同一个SqlSession中执行相同的SQL语句,第二次查询时会直接从缓存中获取结果。

一级缓存的默认开启是通过SqlSessionFactory的默认值配置的,可以通过在mybatis-config.xml文件中的标签中配置开启或关闭,
一级缓存的缓存范围为SqlSession级别,即同一个SqlSession中的所有操作都共享同一个缓存。

一级缓存的缓存失效情况包括以下几种:

(1) SqlSession关闭;
(2) 执行了增删改操作;
(3) 执行了清空缓存操作;
(4) 在SqlSession中显式地调用了clearCache方法。

2、二级缓存

所有的数据都会先放在一级缓存中,只有当会话提交或关闭的时候,才会提交到二级缓存

二级缓存是指MyBatis的Mapper级别的缓存,即在多个SqlSession中执行相同的SQL语句,第二次查询时会直接从缓存中获取结果。

二级缓存的默认开启是通过Mapper的缓存配置实现的,可以通过在Mapper.xml文件中的标签中配置开启或关闭,

例如:

<setting name="cacheEnabled" value="true"/>

二级缓存的缓存范围为Mapper级别,即同一个Mapper的所有SqlSession共享同一个缓存。

二级缓存的缓存失效情况包括以下几种:

(1) 执行了增删改操作;
(2) 执行了清空缓存操作;
(3) 在Mapper.xml文件中的标签中配置了flushInterval属性,表示缓存刷新的时间间隔;
(4) 在Mapper.xml文件中的标签中配置了size属性,表示缓存的最大容量,当缓存的数量超过最大容量时,会触发缓存的清空操作;
(5) 在SqlSession中显式地调用了clearCache方法。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值