关联查询
实际项目中,经常用到关联表的查询,比如一对一,一对多等情况。在Java实体对象中,一对一和一对多可是使用包装对象解决,属性使用List或者Set来实现,在mybatis中一对一和一对多可是使用association或者collection标签来配合实现。
在MyBatis中有两种方式实现关联查询:
- 嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集。首先,然让我们来查看这个元素的属性。所有的你都会看到,它和普通的只由 select 和resultMap 属性的结果映射不同
- 嵌套查询:通过执行另外一个 SQL 映射语句来返回预期的复杂类型
一对一查询
示例:商品表products 和 商品分类category 一个商品对应一种分类,所以products->category是一对一的关系。
嵌套结果实现(resultMap):
通过resultMap将查询结果中商品信息映射到Product对象中,在Product类中添加属性category,将关联结果映射到Product.category属性上。
<!-- 一对一查询 关联查询 使用resultMap映射结果集 -->
<select id="oneToOneTestMap" parameterType="int" resultMap="productInfoMap">
select p.*,c.id
categoryId,c.name categoryName,c.remark categoryRemark
from products p
join category c
on p.categoryId= c.Id
where p.id
=#{value}
</select>
<resultMap id="productInfoMap" type="com.sl.po.Product">
<id column="id" property="Id" />
<result column="name" property="Name" />
<result column="description" property="Description" />
<result column="unitprice" property="UnitPrice" />
<result column="imageUrl" property="ImageUrl" />
<result column="isnew" property="IsNew" />
<result column="citycode" property="cityCode" />
<!-- association:用于映射关联查询单个对象的信息
property:要将关联查询的分类信息映射到属性category上
-->
<association property="category" javaType="com.sl.po.Category">
<id column="categoryId" property="Id" />
<result column="categoryName" property="Name" />
<result column="categoryRemark" property="Remark" />
</association>
</resultMap>
注:也可以使用resultType配置实现,需要定义一个包装类含有product和category两个对象的属性即可。
嵌套查询实现
<!-- 关联嵌套查询 -->
<select id="oneToOneTestAssociationSelect" parameterType="int" resultMap="productInfoMap2">
select p.*
from products p
where p.id =#{value}
</select>
<resultMap id="productInfoMap2" type="com.sl.po.Product" >
<id column="id" property="Id" />
<result column="name" property="Name" />
<result column="description" property="Description" />
<result column="unitprice" property="UnitPrice" />
<result column="imageUrl" property="ImageUrl" />
<result column="isnew" property="IsNew" />
<result column="citycode" property="cityCode" />
<!-- column:传递子查询参数,如果要处理符复合主键,使用column= "{prop1=col1,prop2=col2}" -->
<!-- select:子查询语句id -->
<association property="category" javaType="com.sl.po.Category" column="categoryId" select="selectCategoryInfo">
</association>
</resultMap>
<!-- 子查询 -->
<select id="selectCategoryInfo" parameterType="int" resultType="com.sl.po.Category">
select * from category where id=#{id}
</select>
使用resultType、resultMap和嵌套查询对比分析:
resultType:使用resultType只需要将查询结果集中的列名与定义的pojo属性一一对应即可完成映射,缺点:resultType无法将查询结果映射到包装类的pojo属性中
resultMap:需要额外定义resultMap,在resultMap中将结果集列名与pojo对象属性一一配置。
嵌套查询:需要定义多个查询,上面示例定义两个查询,一个用于查询products表,一个用于查询category表。由于使用的是嵌套查询,当操作大型数据集合和列表时将会带来频繁操作数据库问题。即执行一条sql获取结果集(oneToOneTestAssociationSelect),根据该结果集,循环执行嵌套查询获取具体信息(selectCategoryInfo),与上面的嵌套结果查询相比,这种情况显然有明显不足。
一对多查询
将上面一对一的示例倒过来看,一种类别下有多个商品,所以category->products是一对多的关系
mybatis中可以通过使用resultMap的collection标签将关联查询的多条记录映射到一个list集合属性中。
嵌套结果实现:
<!-- 一对多映射 -->
<select id="oneToManyTest" resultMap="categoryInfo">
select c.id cid,c.`name`
cname,c.remark, p.*
from category c
join products p
on p.categoryId= c.Id
where c.id= #{cid}
</select>
<resultMap type="com.sl.po.Category" id="categoryInfo">
<id column="cid" property="id" />
<result column="cname" property="name" />
<result column="remark" property="remark" />
<!-- collection标签,一对多映射,关联当前分类下产品信息 property映射集合结果,ofType结果集类型 -->
<collection property="productList" ofType="com.sl.po.Product">
<id property="id" column="id" javaType="int" jdbcType="INTEGER" />
<result column="name" property="Name" />
<result column="description" property="Description" />
<result column="unitprice" property="UnitPrice" />
<result column="imageUrl" property="ImageUrl" />
<result column="isnew" property="IsNew" />
<result column="citycode" property="cityCode" />
</collection>
</resultMap>
嵌套查询实现:
<!-- 集合嵌套查询 -->
<select id="oneToManyTestCollectionSelect" resultMap="categoryInfo2">
select *
from category c
where c.id= #{id}
</select>
<resultMap id="categoryInfo2" type="com.sl.po.Category">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="remark" property="remark" />
<!--collection 映射一对多结果集 column:传递嵌套查询参数 select:嵌套查询id-->
<collection property="productList" ofType="com.sl.po.Product" column="id" select="selectProductByCategoryId">
</collection>
</resultMap>
<!-- 嵌套子查询 -->
<select id="selectProductByCategoryId" resultType="com.sl.po.Product">
select *
from products
where categoryId= #{id}
</select>
多对多查询
示例:一个订单包含多个商品,一个商品也可以对应多个订单,这个示例查询稍微扩展一下,增加用户信息,一个用户对应多个订单。
实体对象定义:定义User实体,增加orders属性,用于映射当前用户的订单; 定义Order对象,增加orderItems属性,映射当前订单有哪些内容(产品);定义OrderItem实体,增加product属性,映射具体产品信息。
<!-- 多对多映射 查询用户信息及对应订单信息,订单详情 -->
<!-- select * from orders o join `user` u on o.userId = u.id join orderItem
i on o.Id = i.orderid join products p on i.productid = p.Id -->
<select id="manyToManyTest" resultMap="userAndOrderInfo">
select u.*,
o.id oid,
o.createtime ocreatetime,
o.userid ouserid,
o.amount oamount,
o.remark
oremark,
i.id iid,
i.orderid iorderid,
i.productid iproductid,
i.createtime icreatetime,
i.number inumber,
i.price iprice,
p.id pid,
p.`name` pname,
p.Description pdescription
from orders o
join `user` u on
o.userId = u.id
join orderItem i on o.Id = i.orderid
join products p on
i.productid = p.Id
where u.id=#{id}
</select>
<resultMap type="com.sl.po.User" id="userAndOrderInfo">
<id column="id" property="id" />
<result column="sex" property="sex" />
<result column="birthday" property="birthday" />
<result column="address" property="address" />
<result column="username" property="userName" />
<!-- 映射用户对应的订单信息,一个用户可以有多个订单 -->
<collection property="orders" ofType="com.sl.po.Order">
<id column="oid" property="id" />
<result column="ocreatetime" property="createTime" />
<result column="ouserid" property="userId" />
<result column="oamount" property="amount" />
<result column="oremark" property="remark" />
<!-- 订单对应的商品信息,一个订单可以有多个商品 -->
<collection property="orderItems" ofType="com.sl.po.OrderItem">
<id column="iid" property="id" />
<result column="iorderid" property="orderId" />
<result column="iproductid" property="productId" />
<result column="icreatetime" property="createTime" />
<result column="inumber" property="number" />
<result column="iprice" property="price" />
<!-- 映射商品信息 (OrderItem与商品product 一一对应)-->
<association property="product" javaType="com.sl.po.Product">
<id column="pid" property="Id" />
<result column="pname" property="Name" />
<result column="pdescription" property="Description" />
</association>
</collection>
</collection>
</resultMap>
标签即属性说明
- Association标签: 作用是可以将关联查询信息映射到一个pojo对象中
- collection标签: 作用是可以将关联查询信息映射到一个集合中
- Association和collection标签常用到的属性:
- Property属性: 指定当前association标签内容映射到pojo对象中哪个属性。
- javaType:映射属性的类型
- typeHandler:类型处理器,使用这个属性,你可以覆盖默认的 typeHandler 类型处理器。 这个属性值是类的完全限定名或者是一个类型处理器的实现, 或者是类型别名
- column:sql结果集列名,用在嵌套查询时传递参数,要 处 理 复 合 主 键 , 你 可 以 指 定 多 个 列 名 通 过 column= ” {prop1=col1,prop2=col2} ” 这种语法来传递给嵌套查询语 句。prop1 和 prop2 以参数对象形式来设置给目标嵌套查询语句。
- select:嵌套查询映射语句的ID
- fetchType:可选的。有效值为 lazy和eager。 如果使用了,它将取代全局配置参数lazyLoadingEnabled
- columnPrefix:将含有指定前缀的结果集映射到当前标签下 例如: 将结果集中以“p_”开头的列映射到productInfo属性上
延迟加载
延迟加载其实就是将数据加载时机推迟,比如推迟嵌套查询的执行时机。在Mybatis中经常用到关联查询,但是并不是任何时候都需要立即返回关联查询结果。比如查询订单信息,并不一定需要及时返回订单对应的产品信息,查询商品分类信息并不一定要及时返回该类别下有哪些产品,这种情况一下需要一种机制,当需要查看时,再执行查询,返回需要的结果集,这种需求在Mybatis中可以使用延迟加载机制来实现。延迟加载可以实现先查询主表,按需实时做关联查询,返回关联表结果集,一定程度上提高了效率。
编写lazyLoadMapper.xml映射文件
<?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">
<mapper namespace="com.sl.mapper.LazyLoadMapper">
<!-- 分类信息查询 -->
<select id="lazyLoadTest" resultMap="lazyLoadProductsByCategoryTest">
select * from category where id=#{id}
</select>
<resultMap id="lazyLoadProductsByCategoryTest" type="com.sl.po.Category">
<id column="id" property="Id"></id>
<result column="name" property="Name"></result>
<result column="remark" property="Remark"></result>
<!-- 一个分类对应多个产品,此处使用collection -->
<collection property="productList" ofType="com.sl.po.Product" column="id" select="selectProductsByCategoryId"></collection>
</resultMap>
<!-- 嵌套查询返回商品信息,延迟加载将要执行的sql -->
<select id="selectProductsByCategoryId" resultType="com.sl.po.Product">
select * from products where categoryid=#{id}
</select>
</mapper>
启用延迟加载和按需加载
Mybatis配置文件中通过两个属性lazyLoadingEnabled和aggressiveLazyLoading来控制延迟加载和按需加载。
lazyLoadingEnabled:是否启用延迟加载,mybatis默认为false,不启用延迟加载。lazyLoadingEnabled属性控制全局是否使用延迟加载,特殊关联关系也可以通过嵌套查询中fetchType属性单独配置(fetchType属性值lazy或者eager)。
aggressiveLazyLoading:是否按需加载属性,默认值false,lazyLoadingEnabled属性启用时只要加载对象,就会加载该对象的所有属性;关闭该属性则会按需加载,即使用到某关联属性时,实时执行嵌套查询加载该属性。
SqlMapConfig.xml中修改配置,注册lazyLoadMapper.xml
<settings>
<!-- 启用延迟加载特性,不配置默认关闭该特性-->
<setting name="lazyLoadingEnabled" value="true"></setting>
<!-- 按需加载: false:使用关联属性,及时加载; true,加载对象,则加载所有属性, -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<mappers>
<!-- 注册Mapper.xml文件 -->
<mapper resource="mapper/lazyLoadMapper.xml"></mapper>
</mappers>
mapper接口
package com.sl.mapper;
import java.util.List;
import com.sl.po.Category;
import com.sl.po.Product;
public interface LazyLoadMapper {
Category lazyLoadTest(int id);
//嵌套查询
List<Product> selectProductsByCategoryId(int cId);
}
测试:
当前配置:启用延迟加载lazyLoadingEnabled=true和按需加载aggressiveLazyLoading=false
Step1: 执行Mapper方法lazyLoadTest,实际只执行了select * from category where id=#{id}
Step2: 执行System.out.println(category.getName()),加载属性,由于启用按需加载aggressiveLazyLoading=false,name属性此前已加载好,所以此处无数据库交互
Step3: 执行category.getProductList().size(),加载属性productList,按需加载需要执行延迟加载sql脚本select * from products where categoryid=#{id},与数据库交互
将aggressiveLazyLoading属性设为为true,即:
再一下执行过程:
此时的配置:启用延迟加载lazyLoadingEnabled=true和关闭按需加载aggressiveLazyLoading=true(即加载对象则加载所有属性)
Step1: 执行Mapper方法lazyLoadTest,实际只执行了select * from category where id=#{id}
Step2: 执行System.out.println(category.getName()),访问name属性,由于启用按需加载aggressiveLazyLoading=true,关闭按需加载,则加载category对象时加载该对象的所有属性,执行延迟加载sql脚本select * from products where categoryid=#{id},与数据库交互
Step3: 执行category.getProductList().size(),访问productList属性,该 性step2已加载好,此处无需数据库交互
对比上下两个step2可以发现 aggressiveLazyLoading=true /false的区别。
缓存机制
Mybatis缓存介绍
MyBatis提供一级缓存和二级缓存机制。
一级缓存是Sqlsession级别的缓存,Sqlsession类的实例对象中有一个hashmap用于缓存数据。不同的Sqlsession实例缓存的hashmap数据区域互不影响。Mybatis默认启用一级缓存,在同一个sqlsession中多次执行相同的sql语句,第一次执行后会将数据缓存起来,后面的查询将会从缓存中读取。当一个sqlsession结束后(close),该sqlsession中缓存的数据也将不存在。
二级缓存是Mapper级别的缓存,多个sqlsession实例操作同一个Mapper配置可共享二级缓存。Mybatis默认没有启用二级缓存,需要手动配置开启二级缓存。
一张图看看一集缓存和二级缓存的区别:
一级缓存
一级缓存区域按sqlsession划分,当执行查询时会先从缓存区域查找,如果存在则直接返回数据,否则从数据库查询,并将结果集写入缓存区。 Mybatis一级缓存是在sqlsession内部维护一个hashmap用于存储,缓存key为hashcode+sqlid+sql,value则为查询的结果集。一级缓存在执行sqlsession.commit()后将会被清空。
一级缓存示例:
编写cacheMapper.xml配置文件
<mapper namespace="com.sl.mapper.CacheMapper">
<cache/>
<select id="selectProductById" parameterType="int" resultType="com.sl.po.Product" >
select * from products where id = #{id}
</select>
</mapper>
Mapper接口:
public interface CacheMapper {
Product selectProductById(int id);
@Options(flushCache=FlushCachePolicy.TRUE)
int updateProductById(Product product);
}
测试方法:
public class TestCacheMapperClient {
SqlSessionFactory factory = null;
@Before
public void init() throws IOException {
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
factory = builder.build(inputStream);
}
// 一级缓存
@Test
public void testSelectProductById() {
SqlSession session = factory.openSession();
CacheMapper mapper = session.getMapper(CacheMapper.class);
Product product = mapper.selectProductById(1);
System.out.println(product.getName());
//执行commit 将清空一级缓存
//session.commit();
//再次执行查询 从一级缓存读取
Product product2 = mapper.selectProductById(1);
System.out.println(product.getName());
// 关闭会话
session.close();
}
}
执行第一次执行selectProductById,查询数据库,第二次执行,从缓存中读取
如果在两次查询中间执行commit,即上面的注释掉的session.commit(),则运行结果如下,显然清空了一级缓存,再次执行数据库查询。
二级缓存
二级缓存按照mapper划分,一个mapper有一个自己的二级缓存(按照namespace区分不同缓存区域,如果多个mapper的namespace相同,则公用一个缓存区域),当多个sqlsession类实例加载相同的Mapper文件,执行mapper配置文件中的sql查询时,这些sqlsession可共享一个二级缓存。Mybatis默认没有启用二级缓存,需要自行配置。
二级缓存示例:
1、启用二级缓存:
在mybatis置文件SqlMapConfig.xml中加入一下配置
<setting name="cacheEnabled" value="true"/>
在Mapper.xml配置文件中添加cache标签
<cache /> <!-- 表示此mapper开启二级缓存。-->
还可以配置其他参数,如:
<cache flushInterval="60000" size="512" readOnly="true" eviction="FIFO" type=”xxxxx” />
- flushInterval:刷新时间间隔,单位毫秒,不设置则没有刷新时间间隔,在执行配置了flushCache标签的sql时刷新(清空)
- size:缓存原数个数,默认1024
- readOnly:是否只读,默认false:mybatis将克隆一份数据返回,true:直接返回缓存数据的引用(不安全,程序如果修改,直接改了缓存项)
- eviction:缓存的回收策略(LRU 、FIFO、 SOFT 、WEAK ),默认LRU
- type:指定自定义缓存的全类名(实现Cache接口即可)
2、结果集映射对象实现序列化接口
使用Mybatis二级缓存需要将sql结果集映射的pojo对象实现java.io.Serializable接口,否则将出现序列化错误。
3、编写cacheMapper.xml配置文件
<mapper namespace="com.sl.mapper.CacheMapper">
<cache/>
<select id="selectProductById" parameterType="int" resultType="com.sl.po.Product" ><!-- useCache="false" 禁用二级缓存或者在Mapper接口上通过注解禁用-->
select * from products where id = #{id}
</select>
<!-- update – 映射更新语句 -->
<update id="updateProductById" parameterType="com.sl.po.Product" flushCache="true"> <!-- flushCache="true" 禁用二级缓存或者在Mapper接口上通过注解禁用-->
update products set
Name = #{Name},IsNew=#{IsNew}
where id=#{id}
</update>
</mapper>
4、Mapper.java接口
public interface CacheMapper {
Product selectProductById(int id);
//@Options(flushCache=FlushCachePolicy.TRUE) //清空 二级缓存
int updateProductById(Product product);\
}
5、测试方法:
public class TestCacheMapperClient {
SqlSessionFactory factory = null;
@Before
public void init() throws IOException {
String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
factory = builder.build(inputStream);
}
//二级缓存
@Test
public void testSelectProductById2() throws IOException {
SqlSession session1 = factory.openSession();
CacheMapper mapper1 = session1.getMapper(CacheMapper.class);
Product product = mapper1.selectProductById(1);
System.out.println(product.getName());
/************同一session 共享一级缓存***************/
//CacheMapper mapper2 = session1.getMapper(CacheMapper.class);
//Product product2 = mapper2.selectProductById(1);
//System.out.println(product2.getName());
//执行commit 将清空一级缓存,无法情况二级缓存
session1.commit();
session1.close();
//清空二级缓存
//Mapper接口注解@Options(flushCache=FlushCachePolicy.TRUE) 或者Mapper.xml配置属性 flushCache="true"
SqlSession session4 = factory.openSession();
CacheMapper mapper4 = session4.getMapper(CacheMapper.class);
Product up = new Product();
up.setId(1);
up.setIsNew(true);
up.setName("缓存测试2");
int count = mapper4.updateProductById(up);
session4.commit();
session4.close();
/**********不同session实例 共享二级缓存************/
SqlSession session3 = factory.openSession();
CacheMapper mapper3 = session3.getMapper(CacheMapper.class);
Product product3 = mapper3.selectProductById(1);
System.out.println(product3.getName());
// 关闭会话
session3.close();
}
}
测试结果,上面updateProductById方法在配置sql中清空了二级缓存,所以后面mapper3.selectProductById(1)仍然执行数据库查询。
6、禁用二级缓存
Mybatis还提供属性用于对指定的查询禁用二级缓存,在Mapper.xml配置文件中可是使用useCache=false禁止当前select使用二级缓存,即:
<select id="selectProductById" parameterType="int" resultType="com.sl.po.Product" useCache="false" ><!-- useCache="false" 禁用二级缓存或者在Mapper接口上通过注解禁用-->
select * from products where id = #{id}
</select>
在Mapper.Java接口中可是通过注解来禁用二级缓存,即:
@Options(useCache=false)
Product selectProductById(int id);
7、缓存刷新
当mybatis执行数据更新sql语句后,DB数据与缓存数据可能已经不一致,如果不执行刷新缓存则可能出现脏读的情况,Mybatis同样提供xml配置和注解两种方式来实现缓存刷新。
Xml配置形式:
<update id="updateProductById" parameterType="com.sl.po.Product" flushCache="true"> <!-- flushCache="true" 禁用二级缓存或者在Mapper接口上通过注解禁用-->
update products set
Name = #{Name},IsNew=#{IsNew}
where id=#{id}
</update>
注解形式:
@Options(flushCache=FlushCachePolicy.TRUE) //清空 二级缓存
int updateProductById(Product product);
使用Redis做Mybatis二级缓存
Mybatis默认启用二级缓存是服务器本地缓存,在程序部署到多台服务器时可能出现数据不一致的情况,这种情况下最好能有个集中式缓存来解决此问题。MyBatis的二级缓存允许自定义实现,Mybatis提供二级缓存接口,我们可以通过实现org.apache.ibatis.cache.Cache接口来整合第三方缓存,比如redis、memcache等。
实现案例:
1、引入redis相关的jar包
2、实现 Mybatis二级缓存org.apache.ibatis.cache.Cache接口
package com.sl.redis;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.ibatis.cache.Cache;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class RedisCache implements Cache
{
private Jedis redisClient = createClient();
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private String id;
public RedisCache(final String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
this.id = id;
}
public String getId() {
return this.id;
}
public int getSize() {
return Integer.valueOf(redisClient.dbSize().toString());
}
public void putObject(Object key, Object value) {
redisClient.set(SerializeHelper.serialize(key.toString()), SerializeHelper.serialize(value));
}
public Object getObject(Object key) {
Object value = SerializeHelper.unserialize(redisClient.get(SerializeHelper.serialize(key.toString())));
return value;
}
public Object removeObject(Object key) {
return redisClient.expire(SerializeHelper.serialize(key.toString()), 0);
}
public void clear() {
redisClient.flushDB();
}
public ReadWriteLock getReadWriteLock() {
return readWriteLock;
}
protected static Jedis createClient() {
try {
JedisPool pool = new JedisPool(new JedisPoolConfig(),"localhost");
return pool.getResource();
} catch (Exception e) {
e.printStackTrace();
}
throw new RuntimeException("初始化连接池错误");
}
}
package com.sl.redis;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SerializeHelper {
public static byte[] serialize(Object object) {
ObjectOutputStream oos = null;
ByteArrayOutputStream baos = null;
try {
// 序列化
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(object);
byte[] bytes = baos.toByteArray();
return bytes;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static Object unserialize(byte[] bytes) {
if (bytes == null)
return null;
ByteArrayInputStream bais = null;
try {
// 反序列化
bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
3、修改Mapper.xml配置文件,通过type属型指定自定义二级缓存实现 type=“com.sl.redis.RedisCache”
<mapper namespace="com.sl.mapper.CacheMapper">
<cache type="com.sl.redis.RedisCache"/>
<select id="selectProductById" parameterType="int" resultType="com.sl.po.Product" ><!-- useCache="false" 禁用二级缓存或者在Mapper接口上通过注解禁用-->
select * from products where id = #{id}
</select>
<!-- update – 映射更新语句 -->
<update id="updateProductById" parameterType="com.sl.po.Product" flushCache="true"> <!-- flushCache="true" 禁用二级缓存或者在Mapper接口上通过注解禁用-->
update products set
Name = #{Name},IsNew=#{IsNew}
where id=#{id}
</update>
</mapper>
测试方法同上。
以上通过重写Mybatis二级缓存接口Cache类中的方法,将mybatis中默认的二级缓存空间替换成Redis。mybatis的二级缓存默认存储1024个对象(通过size可配置),且自带的二级缓存是存储在服务器本地内存的,实际开发中往往放弃直接使用默认二级缓存。使用redis 可以将数据存储到专用缓存服务器上,同时redis的高性能也保证了缓存数据的高速读取。