二级缓存是多个SqlSession共享的,其做用域是mapper的同一个namespace,不一样的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将再也不从数据库查询,从而提升查询效率。Mybatis默认没有开启二级缓存须要在setting全局参数中配置开启二级缓存。html
下面是使用Ehcache来做为Mybatis二级缓存的实例:java
==mybatis.configuration.cache-enabled=true==mysql
默认二级缓存是开启的git
server.port=80 # 数据源配置 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
<?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>
在src\main\resources目录下建立ehcache.xml文件,内容以下:github
<?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:指定数据在磁盘中的存储位置。 name:缓存名称。 defaultCache:当借助CacheManager.add("demoCache")建立Cache时,EhCache便会采用<defalutCache/>指定的的管理策略,如下属性是必须的: maxElementsInMemory:缓存最大个数。 eternal:对象是否永久有效,一但设置了,timeout将不起做用。 timeToIdleSeconds:设置对象在失效前的容许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。 timeToLiveSeconds:设置对象在失效前容许存活时间(单位:秒)。最大时间介于建立时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。 overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。 diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每一个Cache都应该有本身的一个缓冲区。 maxElementsOnDisk:硬盘最大缓存个数。 diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false. diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。 memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你能够设置为FIFO(先进先出)或是LFU(较少使用)。 clearOnFlush:内存数量最大时是否清除。 --> <diskStore path="e:\ehcache" /> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" maxElementsOnDisk="10000000" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" /> </ehcache>
<cache /> 添加这个节点会默认将该namespace下全部的的select语句缓存。若是不须要进行缓存,须要设置useCache="false"。web
<select id="findByPage" resultMap="BaseResultMap" useCache="false">
完整的Mapper文件,以下:spring
<?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>标签二选一,第一个能够输出日志,第二个不输出日志 --> <!--<cache type="org.mybatis.caches.ehcache.LoggingEhcache" /> <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>--> <!--根据需求调整缓存参数:--> <cache type="org.mybatis.caches.ehcache.EhcacheCache"> <property name="timeToIdleSeconds" value="3600"/> <property name="timeToLiveSeconds" value="3600"/> <!-- 同ehcache参数maxElementsInMemory --> <property name="maxEntriesLocalHeap" value="1000"/> <!-- 同ehcache参数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"默认是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>
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; } }
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); /** * 获取全部数据 * @return */ List<Person> findAll(); /** * 分页查询数据 * @return */ Page<Person> findByPage(); }
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 每页显示记录数 * @return */ Page<Person> findByPage(int pageNo, int pageSize); void insert(Person person); }
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)); } }
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/releasessql
spring-boot-student-mybatis-ehcache工程数据库