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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM