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等复杂的参数类型 |
resultType | JavaBean的规范映射;可以是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属性指定领域模型,完成自动映射。常见的领域模型如:HashMap
,JavaBean
,POJO
。
完整的结果映射:
<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 来保证行为与期望的相一致。 |
jdbcType | JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 | 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。 |
typeHandler | 使用自定义类型处理器覆盖默认的类型处理器。 | 这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。用来定制jdbcType 和JavaType相互转化的规则。 |
支持的 JDBC 类型
MyBatis 通过内置的 jdbcType 枚举类型支持下面的 JDBC 类型。
BIT | FLOAT | CHAR | TIMESTAMP | OTHER | UNDEFINED |
---|---|---|---|---|---|
TINYINT | REAL | VARCHAR | BINARY | BLOB | NVARCHAR |
SMALLINT | DOUBLE | LONGVARCHAR | VARBINARY | CLOB | NCHAR |
INTEGER | NUMERIC | DATE | LONGVARBINARY | BOOLEAN | NCLOB |
BIGINT | DECIMAL | TIME | NULL | CURSOR | ARRAY |
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
是对应的,当然还可以配置类型转换器typeHandler
、javaType
、jdbcType
。需要注意:配置了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 属性:
属性 | 描述 |
---|---|
keyProperty | selectKey 语句结果应该被设置到的目标属性。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
keyColumn | 返回结果集中生成列属性的列名。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
resultType | 结果的类型。通常 MyBatis 可以推断出来,但是为了更加准确,写上也不会有什么问题。MyBatis 允许将任何简单类型用作主键的类型,包括字符串。如果生成列不止一个,则可以使用包含期望属性的 Object 或 Map。 |
order | 可设置为 BEFORE 或 AFTER 。如果设置为BEFORE ,那么它首先会生成主键,设置 keyProperty 再执行插入语句。如果设置为AFTER ,那么先执行插入语句,然后是 selectKey 中的语句 - 这和 Oracle 数据库的行为相似,在插入语句内部可能有嵌入索引调用。 |
statementType | 和前面一样,MyBatis 支持 STATEMENT ,PREPARED 和 CALLABLE 类型的映射语句,分别代表 Statement , PreparedStatement 和 CallableStatement 类型。 |
参数使用
如果之前在配置里提到的类型转换器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)映射语句文件中的所有insert
、update
、delete
语句将会被缓存。
(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-ref
的namespace
属性指向EmployeeMapper.xml
的命名空间即可。
<cache-ref namespace="com.ssm.web.demo.dao.emp.EmployeeMapper"/>
相关文章:
文章名称 |
---|
《Mybatis(一)主要组件》 |
《Mybatis(二)配置》 |
《Mybatis(三)动态SQL》 |
《Mybtis(四)工作原理》 |
《Mybtis(五)Mapper映射器》 |
《Mybtis(六)Mapper级联》 |