嘘~ 正在从服务器偷取页面 . . .

Mybtis(五)Mapper映射器


Mybatis映射器

Mybatis通过映射器构造的SQL,并且通过配置生成对应的JavaBean返回给调用者,这些配置主要是映射器。映射器的 XML 文件,通俗的说就是Mapper.xml文件就是属于XML映射器,注解的@Select("sql")配置方式则是注解映射器。学习以XML映射器记录为主。

映射器的主要元素:

XML映射文件主要配置:

元素/标签作用说明
select映射查询语句,最常用的复杂的的元素之一可以自定义参数,返回结果集等
insert映射插入语句执行后返回一个整数,代表插入的条数
update映射更新语句执行后返回一个整数,代表更新的条数
delete映射删除语句执行后返回一个整数,代表更新的条数
sql允许定义的可重用SQL语句块如表的列名,一次定义,多个SQL语句都可以使用
resultMap描述从数据库结果集中来加载对象,是最复杂也是最强大的元素提供映射规则
cache指定命名空间的缓存配置。-
cache-ref引用其它命名空间的缓存配置。-

一、SELECT 元素

select元素配置

select 元素允许你配置很多属性来配置每条语句的行为细节。

<select
  id="selectPerson"
  parameterType="int"
  parameterMap="deprecated"
  resultType="hashmap"
  resultMap="personResultMap"
  flushCache="false"
  useCache="true"
  timeout="10"
  fetchSize="256"
  statementType="PREPARED"
  resultSetType="FORWARD_ONLY">
  </select>

SELECT主要属性:

属性说明备注
id和Mapper的命名空间组合成唯一标识符,提供给Mybatis调用如果命名空间和id组合之后存在重复,则抛出异常
parameterType参数类型,可以是基本类型,可以Map,可以是类的全命名或别名,别名必须是内部定义或自定义的如int,Map,JavaBean等复杂的参数类型
resultTypeJavaBean的规范映射;可以是int,double等参数,也可以是类的全命名或符合规范的别名不能和resultMap同时使用,如果返回集合,则值为集合里的类型。
resultMap是映射集的引用,将执行强大的映射功能,可以让我们自定义映射规则可以配置映射级联、类型转换器等
flushCache将值设置为 true 后在调用SQL后,要求Mybatis清空之前的查询本地缓存和二级缓存默认值为false,即不清空缓存
useCache将其设置为 true 后,将会该条语句的结果被二级缓存缓存起来。默认值:对 select 元素为 true。即开启二级缓存
timeout设置等待数据库返回请求结果的超时秒数,超时后抛异常默认值为未设置(unset)(依赖数据库驱动)
fetchSize设置获取记录总条数默认值为未设置(unset)(依赖驱动)
statementType可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement默认值:PREPARED
resultSetType这是和JDBC的resultSet接口对应,值包括FORWARD_ONLY(游标允许向前访问),SCROLL_SENSITIVE(双向滚动,但不及时更新,即数据库里的数据修改之后不再resultSet中反应出来), SCROLL_INSENSITIVE(双向滚动并及时同步数据库的更新以更改resultSet中的数据) 或 DEFAULT(等价于 unset)默认值为 unset (依赖数据库驱动)
databaseId配置了数据库厂商标识(databaseIdProvider)的规则多数据库支持
resultOrdered这个设置仅针对嵌套结果 select 语句:如果为 true,将会假设包含了嵌套结果集或是分组了,当返回一个主结果行时,就不会产生对前面结果集的引用。 这就使得在获取嵌套结果集的时候不至于内存不够用。默认值:false
resultSets这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。很少使用

selec自动映射

在settings元素中配置autoMappingBehavior属性值设置其策略。有三个值:
(1)NONE - 表示取消自动映射
(2)PARTIAL - 简单映射,只会自动映射那些没有定义嵌套结果集映射的结果集。
(3)FULL - 完整映射,会自动映射任意复杂的结果集,不论是否嵌套。

默认值为PARTIAL,默认情况可以实现一般对象的映射,使用FULL嵌套映射,性能会下降。

当autoMappingBehavior设置不为NONE时,Mybatis提供自动映射功能。要求返回SQL列名和JavaBean的属性一致,或者开启采用驼峰命名方式。

如果数据库规范命名,每个单词都使用下划线分割,POJO采用驼峰式命名方法,可以设置mapUnderscoreToCamelCase为true,实现DB到POJO的自动映射。

select多参数传递

(1)使用Map传递参数
映射文件:

<select id="selectByMap" parameterType="map" resultMap="empMap">
select id, name,email from emplee
where name like concat('%',#{name},'%') 
and email like  concat('%',#{email},'%') 
</select>

Mapper接口:

List<Employee> selectByMap(HashMap<String,Object> params);

调用:

Map<String,Obejct> params = new HashMap<String,Obejct>();
params.put("name","张");
params.put("email","@qq.com");
List<Employee>  empList = employeeMapper.selectByMap(params);

使用Map传递优缺点:

优点缺点
可以自定义键,参数个数任意扩展键值使用可能缺少业务关联性,代码可读性相对较差,不看调用地方,不知道Map里会有哪些业务数据

(2)使用注解传递参数
使用参数注解@Param(org.apache.ibatis.annotations.Param)来实现参数传递。

Mapper接口:

List<Employee> selectByAnnotation(@Param("name")String name,@Param("email")String email);

XML映射无须定义参数类型:

<select id="selectByAnnotation" resultMap="empMap">
select id, name,email from emplee
where name like concat('%',#{name},'%') 
and email like  concat('%',#{email},'%') 
</select>

调用:

Sring name = "张";
Sring email= "@qq.com";
List<Employee> empList = employeeMapper.selectByAnnotation(name,email);

参数传递优缺点
|优点|缺点|
|@Param提供的名字知道传递参数的业务数据,可读性较好|参数较多时比较复杂,且不能扩展|

(3)使用JavaBean传递参数

定义个JavaBean,如果只做查询用可以叫xxxVO,xxxQuery之类

package com.ssm.web.demo.entity.emp;

public class Employee{

    private Integer empId;

    private String empName;

    private String email;
    //getter setter ...
}

XML映射无须定义参数类型:

<select id="selectByAnnotation" parameterType="com.ssm.web.demo.entity.emp.Employee"  resultMap="empMap">
select id, name,email from emplee
where name like concat('%',#{empName},'%') 
and email like  concat('%',#{email},'%') 
</select>

Mapper接口:

List<Employee> selectByJavaBean(Employee,emp);

调用同理:

Employee empQuery = new Employee();
empQuery.setEmpName("张");
empQuery.setEmail("@qq.com");
List<Employee> empList = employeeMapper.selectByJavaBean(empQuery);

总结:
(1)使用Map传递参数导致业务可读性较差,参数伸缩性适宜,对叫固定业务的业务可视情况使用。要求扩展性变动的业务视情况使用。
(2)使用@Param注解传递参数,受个数N影响,业务意义明确,可读性较好,参数较少(N<=5)时最佳方式,参数变动时,需修改方法定义。
(3)使用JavaBean方式,参数较多时,优先使用JavaBean方式,业务性明显,伸缩可调。

二、resultMap映射结果集

resultMap和select关联极强。

ResultMap 的设计思想是:对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系。
Mybatis提供了resultType属性指定领域模型,完成自动映射。常见的领域模型如:HashMapJavaBeanPOJO

完整的结果映射:

<resultMap>
    <constructor>
        <idArg/>
        <Arg/>
    </constructor>
    <id/>
    <result/>
    <association/>
    <collection/>
    <discriminator>
        <case/>
    </discriminator>
</resultMap>

constructor

其中constructor元素用于配置构造方法。通常是没有无参构造方法时,对应一个有参数的构造方法。如果配置了构造方法Mybatis就可以使用这个构造方法来构造POJO了。实际中使用较少,因为有些情况下需要使用不可变类,构造方法注入允许你在初始化时为类设置属性的值,而不用暴露出公有方法。

从版本 3.4.3 开始,可以在指定参数名称的前提下,以任意顺序编写 arg 元素。

id和result

id元素:表示哪个列是竹剑,允许多个主键(即联合主键)。

result元素:配置POJO到SQL列名的映射关系。

id和result元素属:

属性描述备注
property映射到列结果的字段或属性。如果 JavaBean/POJO 有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。可以使用点式分隔形式进行复杂属性导航。 比如,访问学生对象(Student)需要访问学生证(SelfCard)的发证日期(issueDate)可以写成:selfCard.issueDate
column数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。
javaType一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。
jdbcTypeJDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。
typeHandler使用自定义类型处理器覆盖默认的类型处理器。这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。用来定制jdbcType 和JavaType相互转化的规则。

支持的 JDBC 类型

MyBatis 通过内置的 jdbcType 枚举类型支持下面的 JDBC 类型。

BITFLOATCHARTIMESTAMPOTHERUNDEFINED
TINYINTREALVARCHARBINARYBLOBNVARCHAR
SMALLINTDOUBLELONGVARCHARVARBINARYCLOBNCHAR
INTEGERNUMERICDATELONGVARBINARYBOOLEANNCLOB
BIGINTDECIMALTIMENULLCURSORARRAY

resultMap结果映射集:

(1)使用Map存储结果集。

<select id="selectEmps" parameterType="com.ssm.web.demo.entity.emp.Employee"  resultType="Map">
select id, name,email from emplee
where name like concat('%',#{empName},'%') 
and email like  concat('%',#{email},'%') 
</select>

一般来说,所有的select语句都可以使用Map,是一种比较通用的方式。

(2)使用POJO存储结果集。

<resultMap id="BaseResultMap" type="com.ssm.web.demo.entity.emp.Employee">
    <id column="emp_id" jdbcType="INTEGER" property="empId" />
    <result column="emp_name" jdbcType="VARCHAR" property="empName" />
    <result column="sex" jdbcType="CHAR" property="sex" />
    <result column="email" jdbcType="VARCHAR" property="email" />
    <result column="dept_id" jdbcType="VARCHAR" property="did" />
</resultMap>

<select id="selectEmp" parameterType="java.lang.Integer"  resultType="com.ssm.web.demo.entity.emp.Employee">
    select emp_id, emp_name,sex,email,dept_id from tbl_emp
    where emp_id=#{empId}
</select>

resultMap的id代表一个resultMap标识,type就是需要映射的POJO。可以使用Mybatis配置中定义别名,或者使用类的全限定名。映射关系中的id就是对象的主键,property对应POJO的属性名称,column对应的是数据库SQL的列名,这样结果就对应起来了。上面的例子列名和column是对应的,当然还可以配置类型转换器typeHandlerjavaTypejdbcType。需要注意:配置了resultMap就不能配置resultType

Mybatis级联

Mybatis级联主要有3种:association(一对一),collection(一对多),discriminator(鉴别器)。

级联需要示例说明,因篇幅较长单作一篇:《Mybtis(五)Mapper级联》

三、INSERT元素

insert元素,Mybatis会在执行插入后返回一个整数,表示操作后插入的记录数。

insert、update、delete元素属性:

属性描述备注
id在命名空间中唯一的标识符,可以被用来引用这条语句。不唯一时Mybatis抛出异常
parameterType将会传入这条语句的参数的类全限定名或别名,使用别名必须是Mybatis内部定义或配置的自定义别名。这个属性是可选的,因为MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。如JavaBean、Map、JavaType等类型传递给SQL
parameterMap用于引用外部 parameterMap 的属性,目前已被废弃。                   
flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:(对 insert、update 和 delete 语句)true。
timeout设置超时参数,是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。默认值是数据库厂商提供的JDBC驱动所设置的秒数。
statementType可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
useGeneratedKeys仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段)。默认值:false。如果要使用需要设置成true
keyProperty仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称。设置哪个列为主键,如果是联合主键可以用逗号隔开
keyColumn仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。如果是联合主键可以用逗号隔开
databaseId如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。多种数据库支持

INSERT回填主键

如果你希望insert语句执行后返回记录的主键:

(1)数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server)

直接设置 useGeneratedKeys="true",然后再把 keyProperty 设置为目标属性就可以了。 如:

<insert id="insertEmployee" parameterType="com.ssm.web.demo.entity.emp.Employee"  useGeneratedKeys="true" keyProperty="empId">
  insert into tbl_emp (emp_name,sex,email,dept_id )
  values (#{empName},#{sex},#{email},#{dept_id})
</insert>

这样传入的employee对象就不需要设置empId属性,Mybatis会用数据库设置进行处理。

如果数据库支持批量插入:

<insert id="insertManyEmployees" useGeneratedKeys="true" keyProperty="empId">
  insert into Author (username, password, email, bio) values
  <foreach item="item" collection="list" separator=",">
    (#{empName},#{sex},#{email},#{dept_id})
  </foreach>
</insert>

(2)数据库不支持自动生成主键列,或者JDBC 驱动不支持生成主键。

对于不支持的情况,MyBatis 支持自定义主键生成

<insert id="insertEmp">
  <selectKey keyProperty="empId" resultType="int" order="BEFORE">
    select if(max(id) is null, 1 , max(id)+2) as  newId a from tbl_emp
  </selectKey>
  insert into tbl_emp
   (emp_Id,emp_name,sex,email,dept_id )
  values
    (#{empId},#{empName},#{sex},#{email},#{dept_id})
</insert>

示例中,首先会运行 selectKey 元素中的语句,并设置 Employee的 empId,然后才会调用插入语句。这样就实现了数据库自动生成主键类似的行为,同时保持了 Java 代码的简洁。 看一看的 MyBatis 处理主键生成的灵活性和宽容度 。

selectKey 元素描述如下:

<selectKey    keyProperty="id" resultType="int" order="BEFORE" statementType="PREPARED">
    ...
</selectKey>

selectKey 属性:

属性描述
keyPropertyselectKey 语句结果应该被设置到的目标属性。如果生成列不止一个,可以用逗号分隔多个属性名称。
keyColumn返回结果集中生成列属性的列名。如果生成列不止一个,可以用逗号分隔多个属性名称。
resultType结果的类型。通常 MyBatis 可以推断出来,但是为了更加准确,写上也不会有什么问题。MyBatis 允许将任何简单类型用作主键的类型,包括字符串。如果生成列不止一个,则可以使用包含期望属性的 Object 或 Map。
order可设置为 BEFOREAFTER。如果设置为BEFORE,那么它首先会生成主键,设置 keyProperty再执行插入语句。如果设置为AFTER,那么先执行插入语句,然后是 selectKey 中的语句 - 这和 Oracle 数据库的行为相似,在插入语句内部可能有嵌入索引调用。
statementType和前面一样,MyBatis 支持 STATEMENTPREPAREDCALLABLE 类型的映射语句,分别代表 Statement, PreparedStatementCallableStatement 类型。

参数使用

如果之前在配置里提到的类型转换器typeHandler

#{sex,javaType=string, jdbcType=NUMERIC,typeHandler=org.apache.ibatis.type.EnumOrdinalTypeHandler}

存储过程支持

存储过程存在3种参数:输入参数(IN)、输出参数(OUT)、输入输出参数(INOUT)。Mybatis的参数规则均提供支持。通过设置mode属性来确定参数类型,mode的值对应也有三种:IN、OUT、INOUT。

mode参数设置OUT或INOUT时,Mybatis会将存储过程返回的结果社会组到指定的参数中。

如果返回的是一个游标(jdbcType=CURSOR)时,还需要设置resultMap,方便Mybatis将存储过程参数映射到对应的类型,这样Mybatis就可以通过设置的resultMap自动设置映射结果。

#{employee, mode=OUT,jdbcType=CURSOR,javaType=ResultSet,resultMap=empResultMap}

javaType可选,因为Mybatis可以自动检测它。

Mybatis还支持一下高级特性,如结构体,当注册参数时需要指定语句类型名称(jdbcTypeName):

#{emp,mode=OUT,jdbcType=STRUCT,jdbcTypeName=MY_TYPE,resultMap=dempResultMap}

在大部分情况下,MyBatis都会自动推断返回数据类型,大部分情况都不需要配置参数类型和结果类型。需要设置的是容易返回为null的字段类型,null值Mybatis无法判断类型。如备注字段

#{mark,jdbcType=VARCHAR}

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

设置参数常用#{empName}在大部分情况Mybatis都会进行预编译处理,然后再赋值。

如果需要传递是SQL语句本身,不是SQL参数,如动态表格,根据条件显示不同的列,传递SQL列名,根据某些列排序等使用场景,可以使用$符号,如传递变量columns="col1,col2,col3"给SQL,组装SQL语句则可以使用:

select ${columns} from t_table

这样columns就不会被Mybatis进行预编译解析,而变为直接替换。只是这样存在SQL注入的风险问题。Mybatis给予足够的灵活性,需要自己保证SQL的正确性和安全性。

四、UPDATE元素

参数见INSERT元素处。示例:

<update id="updateByExample" parameterType="map">
    update tbl_emp
    set emp_id = #{record.empId,jdbcType=INTEGER},
      emp_name = #{record.empName,jdbcType=VARCHAR},
      sex = #{record.sex,jdbcType=CHAR},
      email = #{record.email,jdbcType=VARCHAR}
    where emp_id = #{empId,jdbcType=INTEGER}
  </update>

  <update id="updateByPrimaryKeySelective" parameterType="com.ssm.web.demo.entity.emp.Employee">
    update tbl_emp
    <set>
      <if test="empName != null">
        emp_name = #{empName,jdbcType=VARCHAR},
      </if>
      <if test="sex != null">
        sex = #{sex,jdbcType=CHAR},
      </if>
      <if test="email != null">
        email = #{email,jdbcType=VARCHAR},
      </if>
    </set>
    where emp_id = #{empId,jdbcType=INTEGER}
</update>

五、DELETE元素

参数见INSERT元素处。示例:

<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from tbl_emp
    where emp_id = #{empId,jdbcType=INTEGER}
</delete>

六、SQL元素

SQL元素用来定义一串SQL语句片段,使之可以在其他的语句通过引用来使用它。主要目的是为了SQL代码片段复用。

<sql id="emp_columns">
    emp_id, emp_name, sex, email
</sql>

<select id="selectEmployee" parameterType="int" resultType="hashMap">
  SELECT 
    <include refid="emp_columns" />
  FROM tbl_emp
  WHERE emp_id = #{empId}
</select>

也可以配合指定参数来使用:

<sql id="emp_columns">
    #{prefix}.emp_id, #{prefix}.emp_name, #{prefix}.sex, #{prefix}.email
</sql>

<select id="selectEmployee" parameterType="int" resultType="hashMap">
  SELECT 
    <include refid="emp_columns" >
        <property name="prefix" vaule="e">
    </include>
  FROM tbl_emp e
  WHERE e.emp_id = #{empId}
</select>

还可以给refid参数值由程序引入:

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

七、Cache缓存

缓存是互联网系统常常用到的,特点是讲数据保存在内存中。常见的流行缓存服务器有MongoDB、Redis、Ehcache等。缓存从计算机内存读取数据,无需从磁盘读入,具有快速读取使用的特点,如果缓存命中率高,可以极大提高系统性能。如果缓存命中率很低,缓存就不存在使用的意义了,所以使用缓存的关键是存储内容访问的命中率。

系统缓存

MyBatis系统缓存分:一级缓存和二级缓存。

一级缓存

MyBatis在没有配置的默认情况下,值开启一级缓存(一级缓存值相对于同一个SqlSession而言)。

在参数和SQL完全一样时,使用SqlSession第一次查询后,Mybatis会将其放入缓存中,后续查询时如没有声明需要刷新缓存,且缓存没有超时的情况下,SqlSession都只会取出当前缓存数据,不会发生SQL到数据库执行查询。

需要注意的是不同的 SqlSession都是相互隔离的,如果更换了新的SqlSession,即使是相同的Mapper、参数和方法,还是会发送SQL到数据库执行,返回结果。

二级缓存

为了克服SqlSession相互隔离问题,需要配置二级缓存,使用在SqlSessionFactory层面上给各个SqlSession对象共享数据。SqlSessionFactory曾的二级缓存默认是不开启的,如果开启需要进行配置,并且实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的(即实现Serializable接口),配置比较简单:

(1)第一步,在XML文件中配置即可:

<cache />

这样的配置,许多设置是默认的,其含义为:

(a)映射语句文件中的所有select语句将会被缓存。

(b)映射语句文件中的所有insertupdatedelete语句将会被缓存。

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

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

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

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

(2)第二步,让你POJO对象实现Serializable接口

package com.ssm.web.demo.entity.emp;

public class Employee implements java.io.Serializable {

    private static final long serialVersionUID = 6654960072154305288L;

    private Integer empId;
    private String empName;
    private String sex;
    private String email;
    private int did;
    private Department department ;
    //setter getter ...
}

特别说明

缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef注解指定缓存作用域。

关于 cache元素的属性:

<cache  eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

这个配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

可用的清除策略有:

  • LRU – 最近最少使用:移除最长时间不被使用的对象。
  • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
  • SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
  • WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。

默认的清除策略是 LRU : Least Recently Used(最近最少使用的)

flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。

size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。

readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。速度上慢一些,但是更安全,因此默认值是 false。

特别提示:二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。

自定义缓存

上述Mybatis自带缓存的方式,是在应用部署机器上本地缓存。但是现在分布式、微服务、各类缓存服务器出现,之后可以进行自定义缓存。也可以通过实现自定义的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖系统缓存行为。

比如:Redis缓存,要实现自定义缓存需要实现MyBatis提供的接口org.apache.ibatis.cache.Cache ,缓存接口:

package org.apache.ibatis.cache;
public interface Cache {
    //获取缓存编号
      String getId();
    //获取缓存对象大小
     int getSize();
    //保存Key值缓存对象
      void putObject(Object key, Object value);
    //获取Key值缓存对象
      Object getObject(Object key);
    //判断Key值缓存对象
      boolean hasKey(Object key);
    //删除Key值缓存对象
      Object removeObject(Object key);
    //清空缓存
      void clear();
    //获取读写锁
    ReadWriteLock getReadWriteLock()}

如自定义缓存

package com.ssm.web.example;

import java.util.concurrent.locks.ReadWriteLock;

import org.apache.ibatis.cache.Cache;

public class MyCustomCache implements Cache{
    ....
}

配置缓存:

<cache type="com.ssm.web.example.MyCustomCache">
    <property name="host" value="localhost"/>
    <property name="cacheFile" value="/tmp/my-custom-cache.tmp"/>
</cache>

可以在缓存这添加公有的JavaBean属性,那么在自定义的MyCustomCache类中增加setHost(String host)setCachefile(String file)方法,在MyCustomCache初始化的时候方法就会被调用,这样就可以设置自定义的参数。

从版本 3.4.2 开始,MyBatis 已经支持在所有属性设置完毕之后,调用一个初始化方法。如果想要使用这个特性,请在你的自定义缓存类里实现 org.apache.ibatis.builder.InitializingObject` 接口:

public interface InitializingObject {
  void initialize() throws Exception;
}

注意:上一节中对缓存的配置(如清除策略、可读或可读写等),不能应用于自定义缓存。

缓存的配置和缓存实例会被绑定到 SQL 映射文件的命名空间中。因此,同一命名空间中的所有语句和缓存将通过命名空间绑定在一起。

增删查改的每条语句可以自定义与缓存交互的方式,或将它们完全排除于缓存之外,这可以通过在每条语句上使用两个简单属性来达成。默认情况下,语句会这样来配置:

<select ... flushCache="false" useCache="true"/>
<insert ... flushCache="true"/>
<update ... flushCache="true"/>
<delete ... flushCache="true"/>

鉴于这是默认行为,显然你永远不应该以这样的方式显式配置一条语句。但如果你想改变默认的行为,只需要设置 flushCache 和 useCache 属性。比如,某些情况下你可能希望特定 select 语句的结果排除于缓存之外,或希望一条 select 语句清空缓存。类似地,你可能希望某些 update 语句执行时不要刷新缓存。

cache-ref

对某一命名空间的语句,只会使用该命名空间的缓存进行缓存或刷新。如果想要在多个命名空间中共享相同的缓存配置和实例。要实现这种需求,可以使用cache-ref元素来引用另一个缓存。假如在DepartmentMapper.xml使用EmployeeMapper.xml里的缓存配置,则cache-refnamespace属性指向EmployeeMapper.xml的命名空间即可。

<cache-ref namespace="com.ssm.web.demo.dao.emp.EmployeeMapper"/>

相关文章:

文章名称
《Mybatis(一)主要组件》
《Mybatis(二)配置》
《Mybatis(三)动态SQL》
《Mybtis(四)工作原理》
《Mybtis(五)Mapper映射器》
《Mybtis(六)Mapper级联》


文章作者: Small-Rose /张小菜
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Small-Rose /张小菜 !
评论
  目录