• mybatis 与 ehcache 整合[转]


    1.简介 

    MyBatis 是支持普通SQL 查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的JDBC 代码和参数的手工设置以及结果集的检索。
    Ehcache 是现在最流行的纯Java开源缓存框架,配置简单、结构清晰、功能强大,最初知道它,是从Hibernate的缓存开始的。

    2. 准备工作 

    下载mybatis相关包与ehcache相关包
    ehcache-core-2.4.4.jar
    mybatis-ehcache-1.0.0.jar
    slf4j-api-1.6.1.jar
    slf4j-log4j12-1.6.2.jar

    3. 配置步骤:

    用hsqldb作为数据库,使用mybatis自定义缓存。
    数据库建表语句:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    create table category (
        catid varchar(10) not null,
        name varchar(80) null,
        descn varchar(255) null,
        constraint pk_category primary key (catid)
    );
    create table product (
        productid varchar(10) not null,
        category varchar(10) not null,
        name varchar(80) null,
        descn varchar(255) null,
        constraint pk_product primary key (productid),
            constraint fk_product_1 foreign key (category)
            references category (catid)
    );
    create index productCat on product (category);
    create index productName on product (name);
    insert数据的语句:
    1
    2
    3
    4
    INSERT INTO category VALUES ('FISH','Fish','<image src="../images/fish_icon.gif"><font size="5" color="blue"> Fish</font>');
    INSERT INTO category VALUES ('DOGS','Dogs','<image src="../images/dogs_icon.gif"><font size="5" color="blue"> Dogs</font>');
    INSERT INTO product VALUES ('FI-SW-01','FISH','Angelfish','<image src="../images/fish1.gif">Salt Water fish from Australia');
    INSERT INTO product VALUES ('FI-SW-02','FISH','Tiger Shark','<image src="../images/fish4.gif">Salt Water fish from Australia');

    4. cache配置步骤: 

    (1).在classpath下配置ehcache.xml。这个是ehcache的默认配置文件。
    1
    2
    3
    4
    5
    6
    <?xml version="1.0" encoding="UTF-8"?> 
    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:noNamespaceSchemaLocation="../bin/ehcache.xsd"
        <defaultCache overflowToDisk="true" eternal="false" maxElementsInMemory="1"/> 
        <diskStore path="D:/cache" /> 
    </ehcache>
    (2).在需要的Mapper.xml中配置cache。
    上面的配置是全局的cache,在Mapper.xml中可以根据自己的需要,对这个Mapper中进行cache的配置,可以配置某一条sql语句不进行cache。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <cache type="org.mybatis.caches.ehcache.LoggingEhcache"/> //最普通的设置,沿用全局设置
    <cache type="org.mybatis.caches.ehcache.LoggingEhcache" >
        <property name="timeToIdleSeconds" value="3600"/><!--1 hour-->
        <property name="timeToLiveSeconds" value="3600"/><!--1 hour-->
        <property name="maxEntriesLocalHeap" value="1000"/>
        <property name="maxEntriesLocalDisk" value="10000000"/>
        <property name="memoryStoreEvictionPolicy" value="LRU"/>
    <cache>
    <!--
    配置这个mapper使用LRU替换策略。
    (个人比较赞同这种配置,因为每个表的数据都不一样,有一些需要经常更新,有得可能某几个字段需要经常做连接,
    使用一样的cache不太合适)
    -->
    mybatis默认是启用cache的,所以对于某一条不想被cache的sql需要把useCache="false"
    1
    <select id="getCategory" parameterType="string" resultType="Category" useCache="false">

    5. 测试:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    private ApplicationContext application;
    private CategoryMapper categoryMapper;
    private ProductMapper productMapper;
    private String[] categoryId = { "FISH", "DOGS", "REPTILES", "CATS", "BIRDS" };
    @Before
    public void initSpring() {
        application = new FileSystemXmlApplicationContext(
                "resource/applicationContext.xml");
        categoryMapper = application.getBean(CategoryMapper.class);
        productMapper = application.getBean(ProductMapper.class);
    }
    @Test
    public void testSelect() {
        // the first time
        long begin = System.nanoTime();
        categoryMapper.getCategory(categoryId[0]);
        long end = System.nanoTime() - begin;
        print("count :" + end);
        // the second time
        begin = System.nanoTime();
        categoryMapper.getCategory(categoryId[0]);
        end = System.nanoTime() - begin;
        print("count :" + end);
        // the third time
        begin = System.nanoTime();
        categoryMapper.getCategory(categoryId[0]);
        end = System.nanoTime() - begin;
        print("count :" + end);
        //
    }
    @Test
    public void testInsert() {
        // the second time
        long begin = System.nanoTime();
        //
        Product p1 = productMapper.getProduct("FI-SW-01");
        long end = System.nanoTime() - begin;
        print("count :" + end);
        print("Category :"+p1.getCategoryId());
        Map<String, String> parame = new HashMap<String, String>();
        parame.put("categoryId", "DOGS");
        parame.put("productId", "FI-SW-01");
         
        begin = System.nanoTime();
        productMapper.updateProductById(parame);
        end = System.nanoTime() - begin;
        print("count :"+end);
        begin = System.nanoTime();
        Product p2 = productMapper.getProduct("FI-SW-01");
        end = System.nanoTime() - begin;
        print("count :"+end);
        print("Category :"+p2.getCategoryId());
         
    }
    运行的结果
    log4j debug开启:
    16 09:42:16,447 org.apache.ibatis.logging.slf4j.Slf4jImpl: Cache Hit Ratio [org.mybatis.jpetstore.persistence.CategoryMapper]: 0.0
    DEBUG 2013-05-16 09:42:16,501 org.apache.ibatis.logging.slf4j.Slf4jImpl: ==> Executing: SELECT CATID AS categoryId, NAME, DESCN AS description FROM CATEGORY WHERE CATID = ?
    DEBUG 2013-05-16 09:42:16,502 org.apache.ibatis.logging.slf4j.Slf4jImpl: ==> Parameters: FISH(String)
    DEBUG 2013-05-16 09:42:16,542 org.apache.ibatis.logging.slf4j.Slf4jImpl: <== Columns: CATEGORYID, NAME, DESCRIPTION
    DEBUG 2013-05-16 09:42:16,542 org.apache.ibatis.logging.slf4j.Slf4jImpl: <== Row: FISH, Fish, <image src="../images/fish_icon.gif"><font size="5" color="blue"> Fish</font>
    count :204671120
    Cache Hit Ratio [org.mybatis.jpetstore.persistence.CategoryMapper]: 0.5
    count :3737320
    Cache Hit Ratio [org.mybatis.jpetstore.persistence.CategoryMapper]: 0.6666666666666666
    count :2349519
    这里可以很清晰的看到,第一次取数据的时候,mybatis运行了sql语句,并且得到了返回的数据,因为是第一次,所以 Cache Hit Ratio 是0.0,当第二次的时候,因为cache已经存在查询的数据集,因此,mybatis没有发起查询,直接得到了数据。两次的时间差了100倍。
    log4j INFO,关掉debug得到的查询速度快了一点
    count :126226400
    count :1803960
    count :772080
     
    inset:语句的运行。首先查一遍,让cache里面有数据,然后update里面的数据,再取出来。从所耗费的时间来看,update只是在cache进行了,只有等cache过期了,数据才会写入数据库。
    count :121264080
    Category :FISH
    count :4028440
    count :1509640
    Category :DOGS

    6.总结思考 

    查看了一下硬盘cache的文件夹,发现mybatis对于每个Mapper都有自己独立的cache文件,查看mybatis-ehcache的实现:
    private static final CacheManager CACHE_MANAGER = CacheManager.create();
    每一个Mapper都对应的有自己的CacheManager。
    虽然,通过log可以看到cache hit radio 但是,我需要像ehcache整合到spring那样子可以获得每个由mybatis代理运行的cache对象。查看了很久的代码,发现里面的代理对象都是私有的,并不能被用户调用,所以现在还没有找到细粒化操作mybatis cache的方法。
     
     
     
     
     
     
     
     
     
     
     
    <   ehcache       xmlns:xsi   =   "http://www.w3.org/2001/XMLSchema-instance"          xsi:noNamespaceSchemaLocation   =   "ehcache.xsd"   >        
         <   diskStore       path   =   "java.io.tmpdir"   />        
         <   defaultCache        
           maxElementsInMemory   =   "10000"        
           maxElementsOnDisk   =   "0"        
           eternal   =   "true"        
           overflowToDisk   =   "true"        
           diskPersistent   =   "false"        
           timeToIdleSeconds   =   "0"        
           timeToLiveSeconds   =   "0"        
           diskSpoolBufferSizeMB   =   "50"        
           diskExpiryThreadIntervalSeconds   =   "120"        
           memoryStoreEvictionPolicy   =   "LFU"        
           />        
         <   cache       name   =   "myCache"        
           maxElementsInMemory   =   "100"        
           maxElementsOnDisk   =   "0"        
           eternal   =   "false"        
           overflowToDisk   =   "false"        
           diskPersistent   =   "false"        
           timeToIdleSeconds   =   "120"        
           timeToLiveSeconds   =   "120"        
           diskSpoolBufferSizeMB   =   "50"        
           diskExpiryThreadIntervalSeconds   =   "120"        
           memoryStoreEvictionPolicy   =   "FIFO"        
           />        
    </   ehcache   >
        
    diskStore  :指定数据存储位置,可指定磁盘中的文件夹位置
    defaultCache : 默认的管理策略
     
    以下属性是必须的:
    1. name:  Cache的名称,必须是唯一的(ehcache会把这个cache放到HashMap里)。
    2. maxElementsInMemory:   在内存中缓存的element的最大数目。 
    3. maxElementsOnDisk:   在磁盘上缓存的element的最大数目,默认值为0,表示不限制。 
    4. eternal:   设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断。 
    5. overflowToDisk:  如果内存中数据超过内存限制,是否要缓存到磁盘上。
     
    以下属性是可选的:   
    1. timeToIdleSeconds:  对象空闲时间,指对象在多长时间没有被访问就会失效。只对eternal为false的有效。默认值0,表示一直可以访问。
    2. timeToLiveSeconds:  对象存活时间,指对象从创建到失效所需要的时间。只对eternal为false的有效。默认值0,表示一直可以访问。
    3. diskPersistent:  是否在磁盘上持久化。指重启jvm后,数据是否有效。默认为false。
    4. diskExpiryThreadIntervalSeconds:  对象检测线程运行时间间隔。标识对象状态的线程多长时间运行一次。
    5. diskSpoolBufferSizeMB:  DiskStore使用的磁盘大小,默认值30MB。每个cache使用各自的DiskStore。
    6. memoryStoreEvictionPolicy:  如果内存中数据超过内存限制,向磁盘缓存时的策略。默认值LRU,可选FIFO、LFU。
    缓存的3 种清空策略  :
    FIFO ,first in first out (先进先出).

    LFU , Less Frequently Used (最少使用).意思是一直以来最少被使用的。缓存的元素有一个hit 属性,hit 值最小的将会被清出缓存。

    LRU ,Least Recently Used(最近最少使用). (ehcache 默认值).缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
  • 相关阅读:
    Eclipse上改动Jython代码的Comment颜色
    StaggeredGridView+universal-image-loader载入网路图片实现瀑布流
    HDU 1890 Robotic Sort
    overload和override
    FileStream大文件复制
    [Asp.Net]状态管理(Session、Application、Cache)
    c#简单自定义异常处理日志辅助类
    Socket网络编程(3)--两端通信
    [Asp.Net]状态管理(ViewState、Cookie)
    Socket网络编程(2)--服务端实现
  • 原文地址:https://www.cnblogs.com/fx2008/p/4071092.html
Copyright © 2020-2023  润新知