Spring Boot + Mybatis + Ehcache 二級緩存實(shí)例

二級緩存是多個SqlSession共享的,其作用域是mapper的同一個namespace,不同的sqlSession兩次執(zhí)行相同namespace下的sql語句且向sql中傳遞參數(shù)也相同即最終執(zhí)行相同的sql語句,第一次執(zhí)行完畢會將數(shù)據(jù)庫中查詢的數(shù)據(jù)寫到緩存(內(nèi)存),第二次會從緩存中獲取數(shù)據(jù)將不再從數(shù)據(jù)庫查詢,從而提高查詢效率。Mybatis默認(rèn)沒有開啟二級緩存需要在setting全局參數(shù)中配置開啟二級緩存。

下面是使用Ehcache來作為Mybatis二級緩存的實(shí)例:

application.properties開啟mybatis中二級緩存

==mybatis.configuration.cache-enabled=true==

默認(rèn)二級緩存是開啟的

server.port=80
# 數(shù)據(jù)源配置
spring.datasource.url=jdbc:mysql://localhost:3306/ssb_test
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
#連接池配置
#spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource

#mybatis
#entity掃描的包名
mybatis.type-aliases-package=com.xiaolyuh.domain.model
#Mapper.xml所在的位置
mybatis.mapper-locations=classpath*:/mybaits/*Mapper.xml
#開啟MyBatis的二級緩存
mybatis.configuration.cache-enabled=true

#pagehelper
pagehelper.helperDialect=mysql
pagehelper.reasonable=true
pagehelper.supportMethodsArguments=true
pagehelper.params=count=countSql

#日志配置
logging.level.com.xiaolyuh=debug
logging.level.org.springframework.web=debug
logging.level.org.springframework.transaction=debug
logging.level.org.mybatis=debug

debug=false

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <artifactId>spring-boot-student-mybatis-ehcache</artifactId>
    <packaging>jar</packaging>

    <name>spring-boot-student-mybatis-ehcache</name>
    <description>Demo Mybatis ehcache for Spring Boot</description>

    <parent>
        <groupId>com.xiaolyuh</groupId>
        <artifactId>spring-boot-student</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath />
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.0</version>
        </dependency>
        <!--pagehelper -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.1.1</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.31</version>
        </dependency>

        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.0.0</version>
        </dependency>
    </dependencies>

</project>

Ehcache配置

在src\main\resources目錄下創(chuàng)建ehcache.xml文件,內(nèi)容如下:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache>

    <!--
        http://www.cnblogs.com/lzy1991/p/5335249.html
        http://www.cnblogs.com/little-fly/p/6251439.html
        緩存配置
           diskStore:指定數(shù)據(jù)在磁盤中的存儲位置。
           name:緩存名稱。
           defaultCache:當(dāng)借助CacheManager.add("demoCache")創(chuàng)建Cache時,EhCache便會采用<defalutCache/>指定的的管理策略,以下屬性是必須的:
           maxElementsInMemory:緩存最大個數(shù)。
           eternal:對象是否永久有效,一但設(shè)置了,timeout將不起作用。
           timeToIdleSeconds:設(shè)置對象在失效前的允許閑置時間(單位:秒)。僅當(dāng)eternal=false對象不是永久有效時使用,可選屬性,默認(rèn)值是0,也就是可閑置時間無窮大。
           timeToLiveSeconds:設(shè)置對象在失效前允許存活時間(單位:秒)。最大時間介于創(chuàng)建時間和失效時間之間。僅當(dāng)eternal=false對象不是永久有效時使用,默認(rèn)是0.,也就是對象存活時間無窮大。
           overflowToDisk:當(dāng)內(nèi)存中對象數(shù)量達(dá)到maxElementsInMemory時,Ehcache將會對象寫到磁盤中。
           diskSpoolBufferSizeMB:這個參數(shù)設(shè)置DiskStore(磁盤緩存)的緩存區(qū)大小。默認(rèn)是30MB。每個Cache都應(yīng)該有自己的一個緩沖區(qū)。
           maxElementsOnDisk:硬盤最大緩存?zhèn)€數(shù)。
           diskPersistent:是否緩存虛擬機(jī)重啟期數(shù)據(jù) Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
           diskExpiryThreadIntervalSeconds:磁盤失效線程運(yùn)行時間間隔,默認(rèn)是120秒。
           memoryStoreEvictionPolicy:當(dāng)達(dá)到maxElementsInMemory限制時,Ehcache將會根據(jù)指定的策略去清理內(nèi)存。默認(rèn)策略是LRU(最近最少使用)。你可以設(shè)置為FIFO(先進(jìn)先出)或是LFU(較少使用)。
           clearOnFlush:內(nèi)存數(shù)量最大時是否清除。
    -->
    <diskStore path="e:\ehcache" />
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true"
            maxElementsOnDisk="10000000"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"
    />
</ehcache>

Mapper文件

<cache /> 添加這個節(jié)點(diǎn)會默認(rèn)將該namespace下所有的的select語句緩存。如果不需要進(jìn)行緩存,需要設(shè)置useCache="false"。

 <select id="findByPage" resultMap="BaseResultMap" useCache="false">

完整的Mapper文件,如下:

<?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.xiaolyuh.domain.mapper.PersonMapper">
    <!--mybatis ehcache緩存配置 -->
    <!-- 以下兩個<cache>標(biāo)簽二選一,第一個可以輸出日志,第二個不輸出日志 -->
    <!--<cache type="org.mybatis.caches.ehcache.LoggingEhcache" />
    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>-->

    <!--根據(jù)需求調(diào)整緩存參數(shù):-->
    <cache type="org.mybatis.caches.ehcache.EhcacheCache">
        <property name="timeToIdleSeconds" value="3600"/>
        <property name="timeToLiveSeconds" value="3600"/>
        <!-- 同ehcache參數(shù)maxElementsInMemory -->
        <property name="maxEntriesLocalHeap" value="1000"/>
        <!-- 同ehcache參數(shù)maxElementsOnDisk -->
        <property name="maxEntriesLocalDisk" value="10000000"/>
        <property name="memoryStoreEvictionPolicy" value="LRU"/>
    </cache>

    <resultMap id="BaseResultMap" type="com.xiaolyuh.domain.model.Person">
        <!--
          WARNING - @mbggenerated
          This element is automatically generated by MyBatis Generator, do not modify.
        -->
        <id column="id" property="id" jdbcType="BIGINT"/>
        <result column="name" property="name" jdbcType="VARCHAR"/>
        <result column="age" property="age" jdbcType="INTEGER"/>
        <result column="address" property="address" jdbcType="VARCHAR"/>
    </resultMap>
    <sql id="Base_Column_List">
        <!--
          WARNING - @mbggenerated
          This element is automatically generated by MyBatis Generator, do not modify.
        -->
        id, name, age, address
    </sql>
    <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long">
        <!--
          WARNING - @mbggenerated
          This element is automatically generated by MyBatis Generator, do not modify.
        -->
        select
        <include refid="Base_Column_List"/>
        from person
        where id = #{id,jdbcType=BIGINT}
    </select>
    <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
        <!--
          WARNING - @mbggenerated
          This element is automatically generated by MyBatis Generator, do not modify.
        -->
        delete from person
        where id = #{id,jdbcType=BIGINT}
    </delete>
    <insert id="insert" parameterType="com.xiaolyuh.domain.model.Person">
        <!--
          WARNING - @mbggenerated
          This element is automatically generated by MyBatis Generator, do not modify.
        -->
        <selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER">
            SELECT LAST_INSERT_ID()
        </selectKey>
        insert into person (name, age, address
        )
        values (#{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER}, #{address,jdbcType=VARCHAR}
        )
    </insert>
    <insert id="insertSelective" parameterType="com.xiaolyuh.domain.model.Person">
        <!--
          WARNING - @mbggenerated
          This element is automatically generated by MyBatis Generator, do not modify.
        -->
        <selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER">
            SELECT LAST_INSERT_ID()
        </selectKey>
        insert into person
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="name != null">
                name,
            </if>
            <if test="age != null">
                age,
            </if>
            <if test="address != null">
                address,
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="name != null">
                #{name,jdbcType=VARCHAR},
            </if>
            <if test="age != null">
                #{age,jdbcType=INTEGER},
            </if>
            <if test="address != null">
                #{address,jdbcType=VARCHAR},
            </if>
        </trim>
    </insert>
    <update id="updateByPrimaryKeySelective" parameterType="com.xiaolyuh.domain.model.Person">
        <!--
          WARNING - @mbggenerated
          This element is automatically generated by MyBatis Generator, do not modify.
        -->
        update person
        <set>
            <if test="name != null">
                name = #{name,jdbcType=VARCHAR},
            </if>
            <if test="age != null">
                age = #{age,jdbcType=INTEGER},
            </if>
            <if test="address != null">
                address = #{address,jdbcType=VARCHAR},
            </if>
        </set>
        where id = #{id,jdbcType=BIGINT}
    </update>
    <update id="updateByPrimaryKey" parameterType="com.xiaolyuh.domain.model.Person">
        <!--
          WARNING - @mbggenerated
          This element is automatically generated by MyBatis Generator, do not modify.
        -->
        update person
        set name = #{name,jdbcType=VARCHAR},
        age = #{age,jdbcType=INTEGER},
        address = #{address,jdbcType=VARCHAR}
        where id = #{id,jdbcType=BIGINT}
    </update>

    <!-- 對這個語句useCache="true"默認(rèn)是true,可以不寫 -->
    <select id="findAll" resultMap="BaseResultMap" useCache="true">
        select
        <include refid="Base_Column_List"/>
        from person
    </select>

    <!-- 對這個語句禁用二級緩存 -->
    <select id="findByPage" resultMap="BaseResultMap" useCache="false">
        select
        <include refid="Base_Column_List"/>
        from person
    </select>
</mapper>

實(shí)體類

package com.xiaolyuh.domain.model;

import java.io.Serializable;

public class Person implements Serializable {

    private Long id;

    /**
     * 名稱
     */
    private String name;

    /**
     * 年齡
     */
    private Integer age;

    /**
     * 地址
     */
    private String address;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

Mapper-類

package com.xiaolyuh.domain.mapper;

import com.github.pagehelper.Page;
import com.xiaolyuh.domain.model.Person;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper//聲明成mybatis Dao層的Bean,也可以在配置類上使用@MapperScan("com.xiaolyuh.domain.mapper")注解聲明
public interface PersonMapper {

    int deleteByPrimaryKey(Long id);

    int insert(Person record);

    int insertSelective(Person record);

    Person selectByPrimaryKey(Long id);

    int updateByPrimaryKeySelective(Person record);

    int updateByPrimaryKey(Person record);

    /**
     * 獲取所有數(shù)據(jù)
     * @return
     */
    List<Person> findAll();

    /**
     * 分頁查詢數(shù)據(jù)
     * @return
     */
    Page<Person> findByPage();
}

Severice接口類

package com.xiaolyuh.service;

import com.github.pagehelper.Page;
import com.xiaolyuh.domain.model.Person;

import java.util.List;

/**
 * Created by yuhao.wang on 2017/6/19.
 */
public interface PersonService {

    List<Person> findAll();

    /**
     * 分頁查詢
     * @param pageNo 頁號
     * @param pageSize 每頁顯示記錄數(shù)
     * @return
     */
    Page<Person> findByPage(int pageNo, int pageSize);

    void insert(Person person);
}

Severice實(shí)現(xiàn)類

package com.xiaolyuh.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.xiaolyuh.domain.mapper.PersonMapper;
import com.xiaolyuh.domain.model.Person;
import com.xiaolyuh.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * Created by yuhao.wang on 2017/6/19.
 */
@Service
@Transactional(readOnly = true)
public class PersonServiceImpl implements PersonService {

    @Autowired
    private PersonMapper personMapper;

    @Override
    public List<Person> findAll() {
        return personMapper.findAll();
    }

    @Override
    public Page<Person> findByPage(int pageNo, int pageSize) {
        PageHelper.startPage(pageNo, pageSize);
        return personMapper.findByPage();
    }

    @Override
    @Transactional
    public void insert(Person person) {
        personMapper.insert(person);
    }

}

測試類

package com.xiaolyuh;

import com.github.pagehelper.Page;
import com.xiaolyuh.domain.model.Person;
import com.xiaolyuh.service.PersonService;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonMapperTests {

    private Logger logger = LoggerFactory.getLogger(PersonMapperTests.class);

    @Autowired
    private PersonService personService;

    @Before
    public void testInsert() {
        Person person = new Person();
        person.setName("測試");
        person.setAddress("address");
        person.setAge(10);
        personService.insert(person);

        Assert.assertNotNull(person.getId());
        logger.debug(JSON.toJSONString(person));
    }

    @Test
    public void testFindAll() {
        List<Person> persons = personService.findAll();

        Assert.assertNotNull(persons);
        logger.debug(JSON.toJSONString(persons));
    }

    @Test
    public void testFindByPage() {
        Page<Person> persons = personService.findByPage(1, 2);

        Assert.assertNotNull(persons);
        logger.debug(persons.toString());
        logger.debug(JSON.toJSONString(persons));
    }

    @Test
    public void testCacheByPage() {
        long begin = System.currentTimeMillis();
        List<Person> persons = personService.findAll();
        long ing = System.currentTimeMillis();
        personService.findAll();
        long end = System.currentTimeMillis();
        logger.debug("第一次請求時間:" + (ing - begin) + "ms");
        logger.debug("第二次請求時間:" + (end - ing) + "ms");

        Assert.assertNotNull(persons);
        logger.debug(persons.toString());
        logger.debug(JSON.toJSONString(persons));
    }

}

執(zhí)行測試類,打印日志信息

 Cache Hit Ratio [com.xiaolyuh.domain.mapper.PersonMapper]: 0.5
2017-06-28 10:15:32.214 DEBUG 10472 --- [           main] org.mybatis.spring.SqlSessionUtils       : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3f6c2763]
2017-06-28 10:15:32.214 DEBUG 10472 --- [           main] org.mybatis.spring.SqlSessionUtils       : Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3f6c2763]
2017-06-28 10:15:32.214 DEBUG 10472 --- [           main] org.mybatis.spring.SqlSessionUtils       : Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3f6c2763]
2017-06-28 10:15:32.214 DEBUG 10472 --- [           main] org.mybatis.spring.SqlSessionUtils       : Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3f6c2763]
2017-06-28 10:15:32.223 DEBUG 10472 --- [           main] com.xiaolyuh.PersonMapperTests           : 第一次請求時間:26ms
2017-06-28 10:15:32.223 DEBUG 10472 --- [           main] com.xiaolyuh.PersonMapperTests           : 第二次請求時間:11ms

源碼

https://github.com/wyh-spring-ecosystem-student/spring-boot-student/tree/releases

spring-boot-student-mybatis-ehcache工程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容