您现在的位置是:网站首页 > 代码编程 > JAVA开发JAVA开发
【原】mybatis整合ehcache配置二级缓存
不忘初心 2019-05-01 围观() 评论() 点赞() 【JAVA开发】
简介:Mybatis提供了二级缓存机制,这个缓存是mapper级别的,可以克服一级缓存的不足,可以整合ehcache实现它的二级缓存。
Mybatis默认开启了一级缓存,这个缓存是sqlsession级别的,在同一个sqlsession中执行相同的sql,第一次会发送sql去数据库查询,后面执行时就会触发缓存,而在不同的sqlsession中,就算是相同的sql也依旧会每次都发送sql去数据库查询。
不过他还提供了二级缓存,这个缓存是mapper级别的,可以克服一级缓存的不足,只要是相同mapper中的查询方法,第一次会发送sql,后面都会触发缓存,但是mybatis默认是不开启二级缓存的,需要在配置中手动开启(这里是总开关,具体使用的时候还需要在mapper.xml中配置cache)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--在application.properties中配置也可以,但是这里配置文件就不能使用了-->
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
<!--开启日志打印-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
</configuration>
mybatis并没有写死二级缓存的实现,而是提供了一个Cache接口来写入缓存、获取缓存、删除缓存
/**
* Copyright 2009-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.cache;
import java.util.concurrent.locks.ReadWriteLock;
/**
* SPI for cache providers.
*
* One instance of cache will be created for each namespace.
*
* The cache implementation must have a constructor that receives the cache id as an String parameter.
*
* MyBatis will pass the namespace as id to the constructor.
*
* <pre>
* public MyCache(final String id) {
* if (id == null) {
* throw new IllegalArgumentException("Cache instances require an ID");
* }
* this.id = id;
* initialize();
* }
* </pre>
*
* @author Clinton Begin
*/
public interface Cache {
/**
* @return The identifier of this cache
*/
String getId();
/**
* @param key Can be any object but usually it is a {@link CacheKey}
* @param value The result of a select.
*/
void putObject(Object key, Object value);
/**
* @param key The key
* @return The object stored in the cache.
*/
Object getObject(Object key);
/**
* As of 3.3.0 this method is only called during a rollback
* for any previous value that was missing in the cache.
* This lets any blocking cache to release the lock that
* may have previously put on the key.
* A blocking cache puts a lock when a value is null
* and releases it when the value is back again.
* This way other threads will wait for the value to be
* available instead of hitting the database.
*
*
* @param key The key
* @return Not used
*/
Object removeObject(Object key);
/**
* Clears this cache instance
*/
void clear();
/**
* Optional. This method is not called by the core.
*
* @return The number of elements stored in the cache (not its capacity).
*/
int getSize();
/**
* Optional. As of 3.2.6 this method is no longer called by the core.
*
* Any locking needed by the cache must be provided internally by the cache provider.
*
* @return A ReadWriteLock
*/
ReadWriteLock getReadWriteLock();
}
只提供了接口,具体实现可以让开发者自定义,非常灵活,现在市面上有很多种缓存实现,比如说:redis、ehcache等,甚至可以直接用一个hashmap来实现,这都是允许的,今天来看一下官方提供的ehcache版本,关于ehcache,在这里就不多说了,大家可以直接将它理解成一个小型的内存缓存。
引入相关的jar包
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.11</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.0</version>
</dependency>
创建ehcache.xml,配置默认缓存
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd">
<!-- ehcache官网配置示例:http://www.ehcache.org/ehcache.xml -->
<!-- 缓存的bean,必须实现Serializable序列化 -->
<!--
一级缓存失效的四种情况:
1.SqlSession不同,Cache会失效
SqlSession ss2=ssf.openSession();
EmployeeMapper mapper2=ss2.getMapper(EmployeeMapper.class);
Employee emp2=mapper2.getEmployeeById(38);
System.out.println(emp2);
2.sqlSession相同,查询条件相同,但两次查询之间执行了增删改操作
mapper.addEmployeeBysql(new Employee(null,"yiwen","yiwen@qq.com","1"));
3.SqlSession相同,查询条件不同
Employee emp3=mapper.getEmployeeById(41);
System.out.println(emp3);
4.sqlSession相同,查询条件相同,手动清除了缓存clearCache()
ss.clearCache();
-->
<!-- 缓存文件存放的磁盘路径,如果没有自定义,会使用系统默认的磁盘路径 -->
<!-- <diskStore path="F:/cache"/> -->
<!-- <diskStore path="/usr/local/ehcache"/>-->
<!-- 自定义缓存属性 -->
<defaultCache maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="true"/>
<!-- 上面配置中,缓存最大存活60s,最少存活30s -->
<!-- 如果创建之后无任何访问,30s之后就会被销毁;如果一直有访问,那么一直持续到60s才会被销毁 -->
<!--
name: Cache的唯一标识
maxElementsInMemory: 缓存中允许创建的最大对象数
maxElementsOnDisk: 磁盘中最大缓存对象数,若是0表示无穷大
eternal: Element是否永久有效,一但设置了,timeout将不起作用,对象永不过期。
timeToIdleSeconds: 缓存数据的钝化时间,也就是在一个元素消亡之前,两次访问时间的最大时间间隔值,这只能在元素不是永久驻留时有效,如果该值是 0 就意味着元素可以停顿无穷长的时间。
timeToLiveSeconds: 缓存数据的生存时间,也就是一个元素从构建到消亡的最大时间间隔值,这只能在元素不是永久驻留时有效,如果该值是0就意味着元素可以停顿无穷长的时间。
overflowToDisk: 内存不足时,是否启用磁盘缓存。
diskPersistent: 是否缓存虚拟机重启期数据
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒
diskSpoolBufferSizeMB: 这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区
memoryStoreEvictionPolicy: 缓存满了之后的淘汰算法。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)
-->
</ehcache>
注释写的比较多,在文章中就不再重复了,大家可以直接看注释,上面是默认的ehcache配置,如果在mapper.xml中没有重新配置cache,那么最终生效的缓存配置就是这一份。
配置文件默认名字是ehcache.xml,如果有特殊的名字,可以在框架配置中指定路径。
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.wolffy.reader.mapper.ApplyMapper">
<!--开启二级缓存-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
<resultMap id="applyMap" type="com.wolffy.reader.entity.Apply">
......
</resultMap>
</mapper>
在使用的mapper中,必须使用<ehcache/>标签来开启缓存,type参数用来指定缓存类型,如果是自定义缓存,那这里就是自己定义的缓存,这里是ehcache,所以是EhcacheCache,它也实现了Cache接口
/*
* Copyright 2010 The MyBatis Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mybatis.caches.ehcache;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;
/**
* Cache adapter for Ehcache.
*
* @version $Id: EhcacheCache.java 3454 2010-12-29 20:35:44Z simone.tripodi $
*/
public final class EhcacheCache implements Cache {
/**
* The cache manager reference.
*/
private static final CacheManager CACHE_MANAGER = createCacheManager();
/**
* Looks for "/ehcache.xml" classpath resource and builds the relative
* {@code CacheManager}; if it's no found or it is impossible to load it,
* returns the default manager.
*
* @return the application cache manager.
*/
private static CacheManager createCacheManager() {
CacheManager cacheManager;
InputStream input = EhcacheCache.class.getResourceAsStream("/ehcache.xml");
if (input != null) {
try {
cacheManager = CacheManager.create(input);
} catch (Throwable t) {
cacheManager = CacheManager.create();
} finally {
try {
input.close();
} catch (IOException e) {
}
}
} else {
cacheManager = CacheManager.create();
}
return cacheManager;
}
/**
* The {@code ReadWriteLock}.
*/
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
/**
* The cache id.
*/
private final String id;
/**
*
*
* @param id
*/
public EhcacheCache(final String id) {
if (id == null) {
throw new IllegalArgumentException("Cache instances require an ID");
}
this.id = id;
if (!CACHE_MANAGER.cacheExists(this.id)) {
CACHE_MANAGER.addCache(this.id);
}
}
/**
* {@inheritDoc}
*/
public void clear() {
this.getCache().removeAll();
}
/**
* {@inheritDoc}
*/
public String getId() {
return this.id;
}
/**
* {@inheritDoc}
*/
public Object getObject(Object key) {
try {
Element cachedElement = this.getCache().get(key.hashCode());
if (cachedElement == null) {
return null;
}
return cachedElement.getObjectValue();
} catch (Throwable t) {
throw new CacheException(t);
}
}
/**
* {@inheritDoc}
*/
public ReadWriteLock getReadWriteLock() {
return this.readWriteLock;
}
/**
* {@inheritDoc}
*/
public int getSize() {
try {
return this.getCache().getSize();
} catch (Throwable t) {
throw new CacheException(t);
}
}
/**
* {@inheritDoc}
*/
public void putObject(Object key, Object value) {
try {
this.getCache().put(new Element(key.hashCode(), value));
} catch (Throwable t) {
throw new CacheException(t);
}
}
/**
* {@inheritDoc}
*/
public Object removeObject(Object key) {
try {
Object obj = this.getObject(key);
this.getCache().remove(key.hashCode());
return obj;
} catch (Throwable t) {
throw new CacheException(t);
}
}
/**
* Returns the ehcache manager for this cache.
*
* @return the ehcache manager for this cache.
*/
private Ehcache getCache() {
return CACHE_MANAGER.getCache(this.id);
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Cache)) {
return false;
}
Cache otherCache = (Cache) obj;
return this.id.equals(otherCache.getId());
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return this.id.hashCode();
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "EHCache {"
+ this.id
+ "}";
}
}
序列化javabean
package com.wolffy.reader.entity;
import java.io.Serializable;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Apply implements Serializable {
}
这一步非常重要,如果没有序列化,是缓存不了查询出来的java对象的,直接会抛一个异常:
java.io.NotSerializableException: com.wolffy.reader.entity.Apply
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at java.util.ArrayList.writeObject(ArrayList.java:762)
at sun.reflect.GeneratedMethodAccessor234.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.__invoke(DelegatingMethodAccessorImpl.java:43)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:45009)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:45012)
at java.lang.reflect.Method.invoke(Method.java:498)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1028)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:441)
at net.sf.ehcache.Element.writeObject(Element.java:835)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.__invoke(DelegatingMethodAccessorImpl.java:43)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:45009)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:45012)
at java.lang.reflect.Method.invoke(Method.java:498)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1028)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at net.sf.ehcache.util.MemoryEfficientByteArrayOutputStream.serialize(MemoryEfficientByteArrayOutputStream.java:97)
at net.sf.ehcache.store.disk.DiskStorageFactory.serializeElement(DiskStorageFactory.java:405)
at net.sf.ehcache.store.disk.DiskStorageFactory.write(DiskStorageFactory.java:384)
at net.sf.ehcache.store.disk.DiskStorageFactory$DiskWriteTask.call(DiskStorageFactory.java:485)
at net.sf.ehcache.store.disk.DiskStorageFactory$PersistentDiskWriteTask.call(DiskStorageFactory.java:1088)
at net.sf.ehcache.store.disk.DiskStorageFactory$PersistentDiskWriteTask.call(DiskStorageFactory.java:1072)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
配置完毕,我们来看一下效果,可以看到查询的效率提升了很多
而且在指定的缓存目录/usr/local/ehcache下,也生成了很多缓存文件
看完文章,有任何疑问,请加入群聊一起交流!!!
很赞哦! ()
相关文章
- mybatis中foreach的变量名和if的变量名相同
- springmvc + spring + mybatis整合搭建图文教程
- mybatis-generator代码自动生成插件的使用图文教程
- Mybatis调用MySQL存储过程图文教程
- 使用mybatis-generator自动生成mapper失败
- IntelliJ IDEA去掉mybatis映射mapper文件的背景色
- mybatis的if判断条件将字符串解析成了数字
- SSM框架基础整合搭建图文教程
- springboot项目提示“Failed to determine a suitable driver class”
- ehcache警告“diskStorePath is already used by an existing CacheManager”
标签云
猜你喜欢
- IntelliJ IDEA 2019.2已经可以利用补丁永久破解激活了
- IntelliJ IDEA 2019.3利用补丁永久破解激活教程
- IntelliJ IDEA高版本最灵活的永久破解激活方法(含插件激活,时长你说了算)
- Jetbrains全家桶基于ja-netfilter的最新破解激活详细图文教程
- IntelliJ IDEA 2022.1永久破解激活教程(亲测可用,持续更新)
- 分享几个正版 IntelliJ IDEA 激活码(破解码、注册码),亲测可用,持续更新
- ja-netfilter到底需不需要mymap,2021.3.2版本激活失效?
- 如何激活idea2022.1及以上版本中的插件(亲测可用)
- 【史上最全】IntelliJ IDEA最新2022.1版本安装和激活视频教学(含插件)
- IntelliJ IDEA 2022.2 版本最新2099年永久激活方法,亲测可用,也可以开启新UI了。
站点信息
- 网站程序:spring + freemarker
- 主题模板:《今夕何夕》
- 文章统计:篇文章
- 标签管理:标签云
- 微信公众号:扫描二维码,关注我们