SSM之Mybatis(三)复杂查询、缓存

关联查询

实际项目中,经常用到关联表的查询,比如一对一,一对多等情况。在Java实体对象中,一对一和一对多可是使用包装对象解决,属性使用List或者Set来实现,在mybatis中一对一和一对多可是使用association或者collection标签来配合实现。

在MyBatis中有两种方式实现关联查询:

  1. 嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集。首先,然让我们来查看这个元素的属性。所有的你都会看到,它和普通的只由 select 和resultMap 属性的结果映射不同
  2. 嵌套查询:通过执行另外一个 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的高性能也保证了缓存数据的高速读取。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值