mybatis映射器(四)

4.1 映射器的主要元素

元素名称 描述 备注
select 查询语句,最常用、最复杂的元素之一 可以自定义参数,返回结果集等
insert 插入语句 执行后返回一个整数,代表插入的条数
update 更新语句 执行后返回一个整数,代表更新的条数
delete 删除语句 执行后返回一个整数,代表删除的条数
parameterMap 定义参数映射关系 即将被删除的元素,不建议使用
sql 允许定义一部分的SQL,然后在各个地方引用它 例如,一张表列名,我们可以一次定义,在多个SQL语句中使用
resultMap 用来描述从数据库结果集中来加载对象,它是最复杂、最强大的元素 它将提供映射规则
cache 给定命名空间的缓存配置  
cache-ref 其他命名空间缓存配置的引用  

4.2 select元素

4.2.1 概述

select元素的配置

元素 说明 备注
id 它和Mapper的命名空间组合起来是唯一的,提供给mybatis调用 如果命名空间和id组合起来不唯一,mybatis将抛出异常
parameterType 你可以给出类的全命名,也可以给出类的别名,但使用别名必须是mybatis内部定义或者自定义的 我们可以选择JavaBean、Map等复杂的参数类型传递给SQL
parameterMap 即将废弃的元素,不讨论  
resultType 定义类的全路径名字,在允许自动匹配的情况下,结果集将通过JavaBean的规范映射;或定义为int、double、float等参数;也可以使用别名,但是要符合别名规范,不能和resultMap同时使用 它是我们常用的参数之一,比如我们统计总条数就可以把它的值设置为int
resultMap 它是映射集的引用,将执行强大的映射功能,我们可以使用resultType或者resultMap其中的一个,resultMap可以给予我们自定义映射规则的机会 它是mybatis最复杂的元素,可以配置映射规则、级联、typeHandler等。
flushCatch 它的作用是在调用SQL后,是否要求mybatis清空之前查询的本地缓存和二级缓存 取值为布尔值,true/false,默认值为false
     

4.2.2 简易数据类型的例子

​ 例如,我们需要统计一个姓氏的用户数量。我们应该把姓氏作为参数传递,而将结果设置为整型返回给调用者。

<select id="countFirstName" parameterType="string" resultType="int">
    select count(*) as total from t_user where name like concat (#{firstName},'%')
</select>

我们在接口UserDao中定义方法。

public int countFirstName(String firstName);

​ 操作步骤:

  • id标出了这条SQL

  • parameterType定义参数类型

  • resultType定义返回值类型

    扫描二维码关注公众号,回复: 2333704 查看本文章

4.2.3 自动映射

​ 有这样的一个参数autoMappingBehavior,当它不设置为NONE的时候,mybatis会提供自动映射的功能,只要返回自动映射的功能,只要返回的SQL列名和JavaBean的属性一致,mybatis就会帮助我们回填这些字段而无需任何配置,它可以在很大程度上简化我们的配置工作。

​ 自动映射可以在settings元素中配置autoMappingBehavior属性值来设置其策略。它包含3个值。

  • NONE,取消自动映射。

  • PARTIAL,只会自动映射,没有定义嵌套结果映射集的结果集。

  • FULL,会自动映射任意复杂的结果集(无论是否嵌套)。

    默认为PARTIAL。所以在默认的情况下,它可以做到当前对象的映射,使用FULL是嵌套映射,在性能上会下降。

4.2.4 传递多个参数

4.2.4.1 使用Map传递参数

​ 我们可以使用mybatis提供的Map接口作为参数来实现它。

<select id="findRoleByMap" parameterType="map" resultMap="roleMap">
    select id,role_name,note from t_role
    where role_name like concat('%',#{roleName},'%')
    and note like concat('%',#{note},'%')
</select>

​ 对于RoleDao接口,我们提供一个方法。

public List<Role> findRoleByMap(Map<String,String>params);

4.2.4.2 使用注解方式传递参数

​ 我们需要使用mybatis的参数注解@Param(org.apache.ibatis.annotations.Param)来实现想要的功能。操作方法是,把RoleDao接口修改为下面的形式。

public List<Role> findRoleByAnnotation(@Param("roleName")String rolename,@Param("note")String note);

​ 把映射器的XML修改为无需定义参数类型

<select id="findRoleByAnnotation" resultMap="roleMap">
    select id,role_name,note from t_role
    where role_name like concat('%',#{roleName},'%')
    and note like concat('%',#{note},'%')
</select>

​ 把参数传递给后台的时候,通过@Param提供的名称mybatis就会知道#{roleName}代表rolename参数,参数的可读性大大提高了。但是这会引起另一个麻烦,一条SQL拥有10个参数的查询,如果我们都使用@Param方式,那么参数将十分复杂,可读性依旧不高,不过mybatis为我们提供了JavaBean定义参数的方式来解决这个问题。

4.2.4.3 使用JavaBean传递参数

​ 在参数过多的情况下,mybatis允许组织一个JavaBean,通过简单的setter和getter方法设置参数,这样就可以提高代码可读性。

​ 在xml文件里引入这个JavaBean:

<select id="findRoleByParms" paramterType="com.learn.chapter4.params.RoleParam" resultMap="roleMap">
    select id,role_name,note from t_role
    where role_name like concat('%',#{roleName},'%')
    and note like concat('%',#{note},'%')
</select>

​ 同时我们在RoleDao接口提供一个方法

public List<Role> findRoleByParams(RoleParam params);

4.2.4.4 总结

  • 使用Map传递参数。因为Map导致业务可读性的丧失,从而导致后续扩展和维护的困难,我们应该在实际的应用中果断废弃这样的传递参数的方式。

  • 使用@Param注解传递多个参数,这种方式的使用受到参数个数(n)的影响。当n《=5时,它是最佳的传递参数的方式,它比用JavaBean更好,因为它更加直观;当n>5时,多个参数调用将带来困难。

  • 当参数个数多于5个时,建议使用JavaBean方式。

4.2.5 使用resultMap映射结果集

​ 在映射器中定义resultMap,用resultMap处理更为复杂的映射。

<resultMap id="roleResultMap" type="com.learn.chapter4.pojo.Role">
    <id property="id" column="id"></id>
    <result property="roleName" column="role_name"></result>
    <result property="note" column="note"></result>
</resultMap>
<select paramterType="long" id="getRole" resultMap="roleResultMap">
    select id,role_name,note from t_role where id = #{id}
</select>

​ 解释一下resultMap的配置

  • 定义了一个唯一标识(id)为roleResultMap的resultMap,用type属性去定义它对应的是哪个JavaBean(也可以使用别名)

  • 通过id元素定义resultResultMap,这个对象代表着使用哪个属性作为其主键。result元素定义普通列的映射关系,例如,把SQL结果返回的列role_no和type属性定义JavaBean的属性roleNo等做一一对应。

  • 这样select语句就不再需要使用自动映射的规则,直接使用resultMap属性指定roleResultMap即可,这样mybatis就会使用我们的自定义映射规则。

4.3 insert元素

4.3.1 概述

​ mybatis会在执行插入之后返回一个整数,以表示你进行操作后插入的记录数。

insert元素配置详解

属性名称 描述 备注
id 它和Mapper的命名空间组合起来是唯一的,作为唯一标志提供给Mybatis调用 如不唯一,mybatis将抛出异常
parameterType 可以给出类的全命名,也可以是一个别名,但使用别名必须是mybatis内部定义或者自定义的别名 我们可以选择JavaBean、Map等参数类型传递给SQL
parameterMap    
flushCache 在调用SQL后,是否要求mybatis清空之前查询的本地缓存和二级缓存 取值为布尔值,默认值为false
timeout 设置超时参数,等超时的时候将抛出异常,单位为秒 默认值是数据库厂商提供的JDBC驱动所设置的秒数
statementType 告诉mybatis使用哪个JDBC的statement工作,取值为STATEMENT(statement)、PREPARED(PreparedStatement)和CallableStatement 默认值为PREPARED
keyProperty 表示1️⃣哪个列作为属性的主键。不能和keyProperty同时使用 设置哪个列为主键,如果是联合主键可以用逗号将其隔开
useGeneratedKeys 这会令mybatis使用JDBC的getGeneratedKeys方法来取出由数据库内部生成的主键 取值为布尔值,默认值为false
keyColumn 指明第几列是主键,不能和keyProperty同时使用,只接受整形参数 同keyProperty
databaseId   提供多种数据库的支持
lang 自定义语言,可以使用第三方语言  
     
     
     

4.3.2 主键回填和自定义

​ MySQL里面的主键需要根据一些特殊的规则去生成,在插入后我们往往需要获得这个主键,以便于未来的操作。

​ 使用keyProperty属性指定是哪个是主键字段,同时使用useGeneratedKeys属性告诉mybatis这个主键是否使用数据库内置策略生成。因此建立POJO,它能提供setter和getter方法,这样能使用Mybatis的主键回填功能。

插入后自动返回主键

<insert id="insertRole" parameterType="role"
        useGeneratedKeys="true" keyProperty="id">
        insert into t_role(role_name,note) value (#{roleName},#{note})
</insert>

​ 这样我们传入role对象就无需设置id的值,mybatis会用数据库的设置进行处理。

4.4 update和delete

​ mybatis执行完update和delete元素之后会返回一个整数,标出执行后影响记录条数。

<update parameterType="role" id="updateRole">
update t_role set 
role_name = #{roleName},
    note = #{note}
    where id = #{id}
</update>
​
<delete id = "delete" parameterType="long">
delete from t_role where id = #{id}
</delete>

4.5 参数

​ 通过制定参数的类型去让对应的typeHandler处理它们。

定义参数属性的时候,mybatis不允许换行!

4.5.1 参数配置

​ 参数可以是基本类型也可以是JavaBean,我们可以指定特定的类型,已确定使用哪个typeHandler处理它们。

#{age,javaType=int,jdbcType=NUMERIC}
​
<!-- 可以指定用哪个typeHandler去处理参数 -->
#{age,javaType=int,jdbcType=NUMERIC,typeHandler}
<!-- 可以对一些数值型的参数设置其保存的精度 -->
#{price,javaType=double,jdbcType=NUMREIC,numericScale=2}

4.5.2 存储过程支持

​ 对于存储过程而言,存在三种参数,输入参数(IN),输出参数(OUT),输入输出参数(INOUT)。mybatis的参数规则为其提供了良好的支持。我们通过制定mode属性来确定其是哪一种参数,它的选项有三种:IN-OUT-INOUT。当参数设置为OUT,或者为INOUT的时候,正如你所希望的一样,mybatis会将存储过程返回的结果设置到你制定的参数中。当你返回的是一个游标(也就是我们制定JdbcType=CURSOR)的时候,你还需要去设置resultMap以便mybatis将存储过程的参数映射到对应的类型,这时mybatis就会通过你所设置的resultMap自动为你设置映射结果。

#{role,mode=OUT,jdbcType=CURSOR,javaType=ResultSet,resultMap=roleResultMap}

​ 这里的javaType是可选的,即使你不指定它,mybatis也会自动检测它。

4.5.3 特殊字符串替换和处理(#和$)

select ${columns} from t_tablename

4.6 sql元素

sql元素的使用

<sql id = "role_columns">
    id , role_name , note
</sql>
<select parameterType = "long" id = "getRole" resultMap = "roleMap">
    select <include refid="role_columns"/> from t_role where id = #{id}
</select>
<select parameterType = "map" id = "findRoles">
    select id,role_name,note from t_role
    where role_name like concat('%',#{roleName},'%')
    and note like concat('%',#{note},'%')
</select>

​ 这里我们用sql元素定义了role_columns,它可以很方便地使用include元素的refid属性进行引用,从而达到重用的功能。还可以像下面这样制定参数:

<sql id = "role_columns">
    #{prefix}.role_no,#{prefix}.role_name,#{prefix}.note
</sql>
<select parameterType = "string" id = "getRole" resultMap = "roleResultMap">
    select
        <include refid = "role_columns">
            <property name = "prefix" value = "r"/>
        </include>
    from t_role r where role_no = #{roleNo}
</select>

​ 这样就可以给mybatis加入参数,我们还可以这样给refid一个参数值,由程序制定引入SQL,如:

<sql id = "someinclude">
    select * from<include refid="${tableName}"/>
</sql>

4.7 resultMap结果映射集

​ resultMap是mybatis里面最复杂的元素。它的作用是定义映射规则、级联的更新、定制类型转化器等。resultMap定义的主要是一个结果集的映射关系。mybatis现有的版本只支持resultMap查询,不支持更新或者保存,更不必说级联的更新、删除和修改了。

4.7.1 resultMap元素的构成

<resultMap>
    <constructor>  配置构造方法
        <idArg/>
        <arg/>
    </constructor>
    <id/>
    <result/>
    <association/>
    <collection/>
    <discriminator>
        <case/>
    </discriminator>
</resultMap>

其中constructor元素用于配置构造方法。一个pojo可能不存在没有参数的构造方法,这个时候我们就可以使用constructor进行配置。假设角色类RoleBean不存在没有参数的构造方法,它的构造方法声明为public RoleBean(Integer id,String roleName),那么我们需要配置这个结果集,如下:

<resultMap ......>
    <constructor>
    <idArg column = "id" javaType = "int"/>
    <arg column = "role_name" javaType = "string"/>
    </constructor>
......
</resultMap>

​ 这样mybatis就知道需要用这个构造方法来构造POJO了。

​ id元素是表示哪个列是主键,允许多个主键,多个主键则称为联合主键。result是配置POJO到SQL列名的映射关系。这里的result和id两个元素都有如下所示的属性。

元素名称 说明 备注
property 映射到列结果的字段或属性。如果POJO的属性匹配的是存在的,和给定SQL列名(column元素)相同的,那么mybatis就会映射到POJO上 可以使用导航式的字段,比如访问一个学生对象(Student)需要访问学生证(selfcard)的发证日期(issueDate),那么我们可以写成selfcard.issueDate
column 这里对应的是SQL的列  
javaType 配置Java的类型 可以是特定的类完全限定名或者mybatis上下文的别名
jdbcType 配置数据库类型 这是一个JDBC的类型,mybatis已经为我们做了限定,基本支持所有常用的数据库类型。
typeHandler 类型处理器 允许你用特定的处理器来覆盖mybatis默认的处理器。这就要指定jdbcType和javaType相互转化的规则
     

​ 此外还有association、collection和discrimination这些元素,我们将在级联那里详细讨论它们的运用方法。

4.7.2 使用map存储结果集

​ 一般而言,任何的select语句都可以使用map存储。如下:

<select id = "findColorByNote" parameterType="string" resultType="map">
    select id,color,note from t_color where note like concat('%',#{note},'%')
</select>

​ 但是使用这种方法意味着可读性下降,所以不推荐这种方式。

4.7.3 使用POJO存储结果集

​ 使用POJO是我们常用的方式。一方面我们可以使用自动映射,正如select语句里论述的一样。我们还可以使用select语句的属性resultMap配置映射集合,只是使用前需要配置类的resultMap,如下所示:

<resultMap id="roleResultMap" type="com.learn.chapter4.pojo.Role">
    <id property="id" column="id"/>
    <result property="roleName" column="role_name"/>
    <result property="note" column="note"/>
</resultMap>

​ 映射关系中,id元素表示这个对象的主键,property代表着POJO的属性名称,column表示数据库SQL的列名,于是POJO就和数据库SQL的结果一一对应起开了。接着我们在映射文件中的select元素里面做如下所示的配置,便可以使用了。

<select parameterType = "long" id = "getRole" resultMap = "roleResultMap">
    select id , role_name , note from t_role where id = #{id}
</select>

​ 我们可以发现SQL语句的列名和roleResultMap的column是一一对应的。使用XML配置的结果集,我们还可以配置typeHandler、javaType、jdbcType,但是这条语句配置了resultMap就不能再配置resultType了。

4.7.4 级联

​ 在数据库中包含着一对多、一对一的关系。比方说一个角色可以分配给多个用户,也可以只分配给一个用户。有时候我们希望角色信息和用户信息一起显示出来,这个是很常见的场景,所以会经常遇见这样的SQL,如下:

select r.*,u.* from t_role r inner join t_user_role ur on r.id = ur.id inner join t_user u on ur.user.id = u.id where r.id = #{id}

​ 这里的查询是把角色和用户的信息都查询出来,我们希望的是在角色的信息中多一个属性,即List<UserBean>userList 这样取出Role的同时也可以访问到它下面的用户了。我们把这样的情况叫做级联。

级联分为三种:

  • association—》一对一

  • collection—》一对多

  • discriminator—》鉴别器,它可以根据实际选择采用哪个类作为实例,允许你根据特定的条件去关联不同的结果集。

4.7.4.1 association 一对一级联

<association property="studentSelfcard" column="id" select="com.learn.chapter4.mapper.StudentSelfcardMapper.findStudentSelfcardByStudentId"/>

​ 这就是通过一次关联来处理问题。上面这段代码放在一对一级联的其中一个xml文件中。select的值填写的是查询方法,对应在另一个xml文件中有一个同名的查询方法。

4.7.4.2 collection 一对多级联

4.7.4.3 discrimination鉴别器级联

<discrimainator javaType="int" column="sex">
    <case value="1" resultMap="maleStudentMap" />
    <case value="2" resultMap="femaleStudentMap" />
</discrimainator>  <!-- 写在resultMap中 -->
--------------------------------------------
<resultMap id = "maleStudentMap" type = "com.learn.chapter4.po.MaleStudentBean" extends="studentMap">
    <collection property = "studentHealthMaleList" select = "com.learn.chapter4.mapper.StudentHealthMaleMapper.findStudentHealthMaleByStuId"
                column = "id" />
</resultMap>

​ 首先我们定义了一个discrimination元素,它对应的列(column)是sex,对应的java类型(javaType)为int,所以才有了下面这行代码。

<discrimainator javaType="int" column="sex">

​ 接着,我们配置了case,这是类似switch语句。这样我们就可以在case里面引入resultMap。当sex=1(男性)时,引入的是maleStudentMap;当sex=2(女性)时,引入的是femaleStudentMap,然后我们分别对这两个resultMap进行定义。

​ 这两个resultMap的定义是大同小异,它们都扩展了原有的studentMap,所以有了下面这行代码。

extends="studentMap"

​ 正如类的继承关系一样,resultMap也可以继承,再加入自己的属性。

4.7.4.4 性能分析和N+1问题

​ 级联的优势是能够方便快捷地获取数据。多层级联时,建议超过三层关联时尽量少用级联,因为不仅用处不大,而且会造成复杂度的增加,不利于理解和维护。

​ 级联更严重的问题,假设有表关联到student表里面,那么可以想象,我们还要增加级联关系到这个结果集里,那么级联关系将会异常复杂。如果我们采取类似默认的场景那么有一个关联我们就要多执行一次SQL,正如我们上面的例子一样,每次取一个Student对象,那么它所有的信息都会被取出来,这样会造成SQL执行过多导致性能下降,这就是N+1的问题,为了解决这个问题我们应该考虑采用延迟加载的功能。

4.7.4.5 延迟加载

​ 为了解决N+1问题,mybatis引入了延迟加载的功能,延迟加载功能的意义在于,一开始并不取出级联数据,只有当使用它了才发送SQL去取回数据。

​ 在mybatis的配置中有两个全局的参数lazyLoadingEnabled和aggressiveLazyLoading。

lazyLoadingEnabled的含义是是否开启延迟加载功能。aggressiveLazyLoading的含义是对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将按需加载。

​ aggressiveLazyLoading的默认值为true,也就是使用层级加载的策略。

​ 当一个功能的两个对象经常需要一起用时,我们采用即时加载更好,因为即时加载可以多条SQL一次性发送,性能高。

​ 配置:settings元素里面的lazyLoadingEnable值开启延迟加载,使得关联属性都按需加载,而不自动加载。要知道在默认的情况下它是即时加载的,一旦关联多,那将造成性能问题!为了改变这种情况,我们可以把mybatis文件的内容配置为延迟加载。

<settings>
    <setting name="lazyLoadingEnable" value="true"/>
</settings>

在默认的情况下mybatis是按层级延迟加载的。

及时加载和延迟加载的混合使用

<association property="studentSelfcard" column="id" fetchType="lazy"
             select="com.learn.chapter.mapper.StudentSelfcardMapper.findStudentSelfcardByStudentId"/>
<collection property="studentLectureList" column="id" fetchType="eager"
            select="com.learn.chapter4.mapper.StundentLectureMapper.findStudentLectureByStuId"/>

延迟加载的实现原理是通过动态代理来实现的。在默认的情况下,mybatis3.3或者以上版本时,是采用JAVASSIST的动态代理,低版本用的是CGLIB,当然也可以使用配置修改。动态代理会生成一个动态代理对象,里面保存着sql和参数,一旦我们使用这个代理对象的方法,它会进入到动态代理对象的代理方法里,方法里面会通过发送sql和参数,就可以把对应的结果从数据库里查找回来,这便是实现原理。

4.7.4.6 另一种级联

​ mybatis还提供了另外一种级联方式,这种方式更加简单直接,也没有N+1的问题。简单来说,就是尽量通过左连接(LEFT JOIN)找到其他学生的信息找到想要的信息。mybatis允许我们通过配置来完成这些级联信息。我们将通过studentMap2定义的映射规则,来完成这个功能。

<resultMap id = "studentMap2" type="com.learn.chapter4.po.StudentBean">
    <id property="id" column="id"/>
    <result property="cnname" column="cnname"/>
    <result property="sex" column="sex" jdbcType="INTEGER"
            javaType="com.learn.chapter4.enums.SexEnum"
     typeHandler="com.learn.chapter4.typehandler.SexTypeHandler"/>
     <result property="note" column="snote"/>
    
    <association property="studentSefcard" column="id"
                  javaType="com.learn.chapter4.po.StudentSelfcardBean">
        <result property="id" column="ssid"/>
        <result property="studentId" column="id"/>
        ......
    </association>
    
    <collection property="studentLectureList"
                ofType="com.learn.chapter4.po.StudentLectureBean">
        <result property="id" column="ssid"/>
        <result property="studentId" column="id"/>
        ......
        <association property="lecture" column="lecture_id"
                  javaType="com.learn.chapter4.po.LectureBean">
            ......
            </association>
    </collection>
    
    <discriminator javaType="int" column="sex">
        <case value="1" resultMap="maleStudentMap2"/>
        <case value="2" resultMap="femaleStudentMap2"/>
    </discriminator>   
</resultMap>
​
<resultMap id="maleStudentMap2" type="com.learn.chapter4.po.MaleStudentBean"                   extends="studentMap2">
    <collection property="studentHealthMaleList" 
                ofType="com.learn.chapter4.po.StudentHealthMaleBean">
            <id property="id" column="hid" javaType="int"/>
            <result property="checkDate" column="check_date"/>
    </collection>
</resultMap>
​
<resultMap id="femaleStudentMap2" type="com.learn.chapter4.po.FemaleStudentBean" 
            extends="studentMap2">
    <collection property="studentHealthfemaleList" 
                ofType="com.learn.chapter4.po.StudentHealthFemaleBean">
        <id property="id" column="hid" javaType="int" />
        <result property="checkDate" column="check_date" />
    </collection>
</resultMap>

说明上述代码:

  • 第一个association定义的JavaType属性告诉了mybatis,将使用哪个类去映射这些字段。第二个associationcolumn定义的是lecture_id,说明用SQL中的字段lecture_id去关联结果。

  • ofType属性定义的是collection里面的泛型是什么java类型,mybatis会拿你定义的java类和结果集做映射。

  • discrimination则是根据sex的结果来判断使用哪个类做映射。它决定了使用男生健康表,还是女生健康表。

​ 这里我们看到了一条SQL就能完成映射,但是这条SQL有些复杂。其次我们是否需要在一次查询就导出那么多的数据,这会不会造成资源浪费,同时也给维护带来一定的困难,这些问题都需要各位同学在实际工作中去考量。

4.8 缓存cache

​ 缓存是互联网系统常常用到的,其特点是将数据保存在内存中。目前流行的缓存服务器有MongoDB、Redis、Eccache等。缓存是在计算机内存上保存的数据,在读取的时候无需再从磁盘读入,因此具备快速读取和使用的特点,如果缓存命中率高,那么可以极大地提高系统的性能。如果缓存命中率很低,那么缓存就不存在使用的意义了,所以使用缓存的关键在于存储内容访问的命中率。

4.8.1 系统缓存(一级缓存和二级缓存)

​ mybatis对缓存提供支持,但是在没有配置的默认的情况下,它只开启一级缓存(一级缓存只是相对于同一个sqlsession而言)。

​ 所以在参数和SQL完全一样的情况下,我们使用sqlSession第一次查询后,mybatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没超时的情况下,SqlSession都只会取出当前缓存的数据,而不会在此发送SQL到数据库。

​ 但是如果你使用的是不同的SqlSession对象,因为不同的SqlSession都是相互隔离的,所以用相同的Mapper、参数和方法,它还是会再次发送SQL到数据库去执行,执行后返回结果。

​ 为了克服多个SqlSession之间的相互隔离问题,往往需要配置二级缓存,使得缓存在SqlSessionFactory层面上能够提供给各个SqlSession对象共享。而SqlSessionFactory层面上的二级缓存是不开启的,二级缓存的开启需要进行配置,实现二级缓存的时候,mybatis要求返回的POJO必须是可序列化的,也就是要求实现serializable接口,配置的方法很简单,只需要在映射XML文件配置就可以开启缓存了。

<cache/>

​ 这样的一个语句里面,很多设置是默认的,如果我们只是这样配置,那么就意味着:

  • 映射语句文件中的所有select语句将会被缓存

  • 映射语句文件中的所有insert、update和delete语句会刷新缓存

  • 缓存会使用默认的Least Recently Used(LRU,最近最少使用的)算法来收回。

  • 根据时间表,比如No Flush Interval,(CNFI,没有刷新间隔),缓存不会以任何时间顺序来刷新。

  • 缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用。

  • 缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索是不共享的,而且可以安全的被调用者修改,不干扰其他调用者或线程所做的潜在修改。

    添加了这个配置后,还需要做一件事情,否则就会出现异常。

    就是mybatis要返回的POJO对象要实现Serializable接口,否则它就会抛出异常。

配置缓存

<cache eviction="LRU" flushInterval="100000" size="1024" readOnly="true"/>
  • eviction:代表的是缓存回收策略,目前mybatis提供以下策略。

    • LRU,最近最少使用的,移除最长时间不用的对象。

    • FIFO,先进先出,按对象进入缓存的顺序来移除它们。

    • SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象。

    • WEAK,弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象。这里采用的是LRU,移除最长时间不用的对象。

  • flushInterval:刷新间隔时间,单位为毫秒,这里配置的是100秒刷新,如果你不配置它,那么当SQL被执行的时候才会去刷新缓存。

  • size:引入数目,一个正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。这里配置的是1024个对象。

  • readOnly:只读,意味着缓存数据只能读而不能修改,这样设置的好处是我们可以快速读取缓存,缺点是我们没有办法修改缓存,它的默认值为false,不允许我们修改。

4.8.2 自定义缓存

系统缓存是mybatis应用机器上的本地缓存,但是在大型服务器上,会使用各类不同的缓存服务器,这个时候我们可以定制缓存,比如Redis缓存。我们需要实现mybatis为我们提供的接口org.apache.ibatis.cache.Cache。

缓存接口简介

//获取缓存编号
String getId();
//保存key值缓存对象
void putObject(Object key,Object value);
//通过key获取缓存对象
Object getObject(Object key);
//通过key删除缓存对象
Object removeObject(Object key);
//清空缓存
void clear();
//获取缓存对象大小
int getSize();
//获取缓存的读写锁
ReadWriteLock getReadWriteLock();

猜你喜欢

转载自blog.csdn.net/weixin_41860630/article/details/81170038