zoukankan      html  css  js  c++  java
  • mybatis存取blob对象+@Cacheable实现数据缓存

    参考文档:

    http://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/

    需求场景:

      当前业务通过第三方接口查询一个业务数据,该数据更新频率略低(约2小时),但前端查询的频率不可控。所以,需要实现一个带有数据缓存功能的查询接口。

    设计方案:

       实时数据由第三方接口获取,ehcache作为一级缓存(数据有效时间60秒),mysql作为二级缓存和数据持久层(数据有效时间2小时)。查询优先级:ehcache>mysql>第三方。其中mysql的数据过期与更新需要自行实现管理。

    常规的增删改查在此不表,主要说下之前很少用到的blob对象和@Cacheable。从第三方获取到的对象由于字段较多且结构复杂,所以不想在数据库建表然后再做VO-TABLE的映射。选择偷懒的方式使用blob直接存储整个对象。

    mysql中blob字段对应java中byte[],所以对象在存取的时候要手动序列化(反序列化)的过程,这个借用ObjectInputStream、ByteArrayInputStream这些对象即可完成。

    VO对象:

    public class BusinessInfoVo
    {
        private int id;
        
        private String nu;
        
        private byte[] data;
        
        private Date updatetime;
        
        //...
    }

    表结构:

    CREATE TABLE `shop_expressinfo` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `nu` varchar(255) DEFAULT NULL,
      `data` blob,
      `updatetime` datetime DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4;

    mapper.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.lichmama.demo.dao.mapper.ExpressMapper">
        
        <resultMap type="BusinessInfoVo" id="businessInfoMap">
            <result property="id" column="id" />
            <result property="nu" column="nu" />
            <result property="data" column="data" jdbcType="BLOB" />
            <result property="updatetime" column="updatetime" />
        </resultMap>
        
        <select id="select" resultMap="businessInfoMap" parameterType="BusinessInfoVo">
            select * from shop_expressinfo where nu = #{nu}
        </select>
        
        <insert id="insert" parameterType="BusinessInfoVo">
            insert into shop_expressinfo(nu, data, updatetime) values(#{nu}, #{data, jdbcType=BLOB}, now())
        </insert>
        
        <update id="update" parameterType="BusinessInfoVo">
            update shop_expressinfo set data = #{data, jdbcType=BLOB}, updatetime = now() where nu = #{nu}
        </update>
        
        <delete id="delete" parameterType="java.lang.String">
            delete from shop_expressinfo where nu = #{nu}
        </delete>
        
    </mapper>

    对象序列化过程:

        /**
         * 将对象转化为字节数组
         * @author lichmama
         * @param object
         * @return
         * @throws IOException
         */
        private byte[] objectToBytes(Object object) throws IOException
        {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(object);
            byte[] bytes = baos.toByteArray();
            baos.close();
            oos.close();
            return bytes;
        }
        
        /**
         * 将字节数组转化为对象
         * @author lichmama
         * @param bytes
         * @return
         * @throws IOException
         * @throws ClassNotFoundException
         */
        @SuppressWarnings("unchecked")
        private <T> T bytesToObject(byte[] bytes) throws IOException, ClassNotFoundException
        {
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bais);
            Object object = ois.readObject();
            bais.close();
            ois.close();
            return (T) object;
        }

    在业务方法加@Cacheable注解,用于缓存数据到内存中,加速查询。这里我使用ehcache做缓存容器,下面贴出配置。

    ehcache.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache updateCheck="false" dynamicConfig="false">
    
        <!-- * maxElementsInMemory - 内存中最大缓存对象数 * eternal - 缓存元素是否永久有效,若配置为true,则其他的缓存生命周期timeout设置均无效 
            * timeToIdleSeconds - 设置Element在失效前的允许闲置时间。仅当element不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。 
            * timeToLiveSeconds - 设置Element在失效前允许存活时间。最大时间介于创建时间和失效时间之间。仅当element不是永久有效时使用,默认是0.,也就是element存活时间无穷大。 
            * overflowToDisk - 配置此属性,当内存中Element数量达到maxElementsInMemory时,Ehcache将会Element写到磁盘中。 
            * maxElementsOnDisk - 磁盘中最大缓存对象数,若是0表示无穷大。 * diskExpiryThreadIntervalSeconds 
            - 磁盘失效线程运行时间间隔,默认是120秒。 * memoryStoreEvictionPolicy - 当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。 -->
    
        <!-- 默认配置 -->
        <defaultCache maxElementsInMemory="5000" eternal="false"
            timeToIdleSeconds="120" timeToLiveSeconds="120"
            memoryStoreEvictionPolicy="LRU" overflowToDisk="false" />
            
        <cache
            name="businessInfoCache"
            maxElementsInMemory="10000"
            maxElementsOnDisk="100000"
            eternal="false"
            overflowToDisk="false"
            timeToIdleSeconds="60"
            timeToLiveSeconds="60"
            memoryStoreEvictionPolicy="LRU" />
    </ehcache>

    关于timeToIdleSeconds和timeToLiveSeconds的解释参看:http://blog.csdn.net/vtopqx/article/details/8522333

    对应的spring配置:

        <!-- ehcache -->
        <cache:annotation-driven cache-manager="ehcacheManager"/>
        
        <!-- cacheManager工厂类,指定ehcache.xml的位置 -->
        <bean id="ehcacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
            <property name="configLocation" value="/WEB-INF/conf/ehcache.xml" />
        </bean>
        
        <!-- 声明cacheManager -->
        <bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
            <property name="cacheManager" ref="ehcacheManagerFactory" />
        </bean>

    在业务方法中使用如下(在首次查询后,spring会缓存数据并保持60秒):

        /**
         * 获取订单的物流跟踪信息
         * @author lichmama
         * @param nu
         * @return
         */
        @Cacheable(value="businessInfoCache", key="#root.methodName" + "." + "#nu")
        public ExpressInfo queryBusinessInfo(String nu)
        {
            //...
        }

    *关于@Cacheable中key的说明:

      key属性是用来指定Spring缓存方法的返回结果时对应的key的。该属性支持SpringEL表达式。当我们没有指定该属性时,Spring将使用默认策略生成key。我们这里先来看看自定义策略,至于默认策略会在后文单独介绍。

           自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。除了上述使用方法参数作为key之外,Spring还为我们提供了一个root对象可以用来生成key。通过该root对象我们可以获取到以下信息。

    参考文档:

    http://blog.csdn.net/fireofjava/article/details/48913335
    http://blog.csdn.net/wjacketcn/article/details/50945887
    http://docs.spring.io/spring/docs/3.2.18.RELEASE/spring-framework-reference/htmlsingle/#cache-annotations-cacheable
  • 相关阅读:
    面向对象、面向接口、面向方法编程的区别?
    面向接口、对象、方面编程区别 -- 精简版
    面向接口编程详解(一)——思想基础
    吴裕雄--天生自然数据结构:静态链表及其创建
    吴裕雄--天生自然数据结构:单链表的基本操作
    吴裕雄--天生自然数据结构:单链表,链式存储结构
    吴裕雄--天生自然数据结构:顺序表的基本操作
    吴裕雄--天生自然Python Matplotlib库学习笔记:matplotlib绘图(2)
    吴裕雄--天生自然Python Matplotlib库学习笔记:matplotlib绘图(1)
    吴裕雄--天生自然Numpy库学习笔记:NumPy Matplotlib
  • 原文地址:https://www.cnblogs.com/lichmama/p/5834049.html
Copyright © 2011-2022 走看看