本节主要介绍ehcaches使用,缓存在项目开发中使用较多,比如我们项目中使用操作hbase数据存储集群,每天有上亿数据,如果普通的查询,那就没得玩了,一个查询客户要等几分钟,那这个项目就没戏,所以在操作大数据访问时,一个查询上百万、千万、上亿条数据结果时就比较慢,我们使用缓存将第一次查询的结果缓存起来,当再次同样的查询条件时就会从缓存中取出数据,这样给服务器压力就很小,页面响应速度很快,给用户的体验效果也比较好。下面介绍ehcache具体使用:
创建项目
在pom.xml引入ehcache依赖包
<?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> <groupId>com.example.springboot</groupId> <artifactId>springboot-cache</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springboot-cache</name> <description>Demo project for Spring Boot</description> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.7</java.version> <!--mybatis版本--> <mybatis-spring.version>1.3.2</mybatis-spring.version> <!--mybatis 分页插件版本--> <mybatis-pagehelper.version>4.1.0</mybatis-pagehelper.version> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- mybatis依赖包 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis-spring.version}</version> </dependency> <!-- mysql 驱动包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- cache 依赖包 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <!-- ehcache --> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>在application.properties配置文件配置数据库连接
#服务端口 server.port=8088 #server.context-path=/springboot #mysql数据连接 spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.username=root spring.datasource.password=root #spring.datasource.max-active=20 #spring.datasource.max-idle=8 #spring.datasource.min-idle=8 #spring.datasource.initial-size=20 #mybatis 配置 # 配置映射文件加载 mybatis.mapper-locations=classpath*:mapper/*.xml # 实体类通过别名使用 #mybatis.type-aliases-package=com.example.springboot.mybatis.entity
创建缓存配置
在src/main/resources目录下创建ehcache.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"> <!-- 磁盘缓存位置 --> <diskStore path="java.io.tmpdir/ehcache" /> <!-- 默认缓存 --> <defaultCache maxEntriesLocalHeap="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> <persistence strategy="localTempSwap"/> </defaultCache> <!-- department 缓存 --> <cache name="user" maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="60" timeToLiveSeconds="60" overflowToDisk="false" memoryStoreEvictionPolicy="LRU"/> </ehcache>
创建数据库
-- ---------------------------- -- Table structure for `m_user` -- ---------------------------- DROP TABLE IF EXISTS `t_user`; CREATE TABLE `t_user` ( `id` varchar(50) NOT NULL DEFAULT '0', `username` varchar(50) DEFAULT NULL, `password` varchar(50) DEFAULT NULL, `email` varchar(50) DEFAULT NULL, `useable` int(11) DEFAULT NULL, `addtime` varchar(50) DEFAULT NULL, `logintime` varchar(50) DEFAULT NULL, `loginip` varchar(50) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of m_user -- ---------------------------- INSERT INTO `t_user` VALUES ('1', '1111', '1111', '1111', '11', '1111', '111', '111');
编写user实体类
package com.example.springboot.cache.entity; public class User { private String id; private String username; private String password; private String email; /** * 是否可用(0禁用,1可用) */ private Integer useable; /** * 创建时间 */ private String addtime; /** * 登陆时间 */ private String logintime; /** * 登陆IP */ private String loginip;
编写dao层类
package com.example.springboot.cache.dao; import com.example.springboot.cache.entity.User; import org.apache.ibatis.annotations.Mapper; import java.util.List; /** * 基于接口编程,通过mybatis与spring整合注入,在xml中编写sql语句 */ @Mapper public interface UserMapper { List<User> queryList(); void save(User user); void batchDelete(String[] ids); void update(User user); User getUserById(String id); }
编写mapper.xml配置文件
在src/main/resources/mapper/UserMapper.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.example.springboot.cache.dao.UserMapper"> <select id="queryList" resultType="com.example.springboot.cache.entity.User"> SELECT u.id, u.username, u.password, u.email, u.useable, u.addtime, u.logintime, u.loginip FROM t_user u </select> <select id="queryById" resultType="com.example.springboot.cache.entity.User"> SELECT u.id, u.username, u.password, u.email, u.useable, u.addtime, u.logintime, u.loginip FROM t_user u where u.id = #{id} </select> <insert id="save"> insert into t_user(id,username, password, email, useable, addtime) values(#{id},#{username}, #{password}, #{email}, #{useable}, now()) </insert> <update id="update"> update t_user set password = #{password}, email = #{email}, useable = #{useable} where id = #{id} </update> <delete id="batchDelete"> delete from m_user where id in <foreach collection="array" item="item" open="(" separator="," close=")"> #{item} </foreach> </delete> <!-- <delete id="delUsers"> delete from m_user where id in <foreach collection="list" item="item" open="(" separator="," close=")"> #{item} </foreach> </delete> --> </mapper>
编写service类
package com.example.springboot.cache.service; import com.example.springboot.cache.dao.UserMapper; import com.example.springboot.cache.entity.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import java.util.List; /** * @desc 通过mybatis xml配置文件读取数据 * @Author wangsh * @date 2018/5/6 14:16 * @return */ @Service public class UserService { @Autowired private UserMapper userMapper; @Cacheable public List<User> queryList() { List<User> queryList = userMapper.queryList(); return queryList; } @Cacheable(key = "#id") public User getUserById(String id) { return userMapper.getUserById(id); } @CacheEvict(key = "#ids") public void batchDelete(String[] ids) { userMapper.batchDelete(ids); } // REQUIRED:表示必须又事物管理,如果没有事物,就创建一个事物 @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor = Exception.class) @CachePut(key = "#user.id") public void update(User user) { userMapper.update(user); } // REQUIRED:表示必须又事物管理,如果没有事物,就创建一个事物 @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor = Exception.class) @CachePut(key = "#user.id") public void save(User user) { userMapper.save(user); } }
创建conroller类
package com.example.springboot.cache.web; import com.example.springboot.cache.entity.User; import com.example.springboot.cache.service.UserService; import com.example.springboot.cache.util.UUIDUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * @desc 操作数据库查询示例 * @Author wangsh * @date 2018/5/6 15:01 * @return */ @RestController @RequestMapping("/user") public class UserController { /** * 通过mybatis xml配置文件读取数据 */ @Autowired private UserService userService; @RequestMapping("/getUserById") @ResponseBody public User getUserById(String id) { User User = userService.getUserById(id); return User; } @RequestMapping("/save") @ResponseBody public User save() { User user = new User(); user.setId(UUIDUtil.getUUID()); user.setUsername("lisi"); user.setPassword("lisi"); userService.save(user); return user; } @RequestMapping("/queryList") @ResponseBody public List<User> queryList() { List<User> queryList = userService.queryList(); return queryList; } }
创建启动服务类
package com.example.springboot.cache; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; //开启ehcache缓存 @EnableCaching @SpringBootApplication public class SpringbootCacheApplication { public static void main(String[] args) { SpringApplication.run(SpringbootCacheApplication.class, args); } }
缓存注解详解
参考:https://www.cnblogs.com/m4tech/p/6641110.html
@CacheConfig:主要用于配置该类中会用到的一些共用的缓存配置。在这里@CacheConfig(cacheNames = "users"):配置了该数据访问对象中返回的内容将存储于名为users的缓存对象中,我们也可以不使用该注解,直接通过@Cacheable自己配置缓存集的名字来定义。
@Cacheable:配置了findByName函数的返回值将被加入缓存。同时在查询时,会先从缓存中获取,若不存在才再发起对数据库的访问。该注解主要有下面几个参数:
value、cacheNames:两个等同的参数(cacheNames为Spring 4新增,作为value的别名),用于指定缓存存储的集合名。由于Spring 4中新增了@CacheConfig,因此在Spring 3中原本必须有的value属性,也成为非必需项了
key:缓存对象存储在Map集合中的key值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:@Cacheable(key = "#p0"):使用函数第一个参数作为缓存的key值,更多关于SpEL表达式的详细内容可参考官方文档
condition:缓存对象的条件,非必需,也需使用SpEL表达式,只有满足表达式条件的内容才会被缓存,比如:@Cacheable(key = "#p0", condition = "#p0.length() < 3"),表示只有当第一个参数的长度小于3的时候才会被缓存,若做此配置上面的AAA用户就不会被缓存,读者可自行实验尝试。
unless:另外一个缓存条件参数,非必需,需使用SpEL表达式。它不同于condition参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断的,所以它可以通过对result进行判断。
keyGenerator:用于指定key生成器,非必需。若需要指定一个自定义的key生成器,我们需要去实现org.springframework.cache.interceptor.KeyGenerator接口,并使用该参数来指定。需要注意的是:该参数与key是互斥的
cacheManager:用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用
cacheResolver:用于指定使用那个缓存解析器,非必需。需通过org.springframework.cache.interceptor.CacheResolver接口来实现自己的缓存解析器,并用该参数指定。
除了这里用到的两个注解之外,还有下面几个核心注解:
@CachePut:配置于函数上,能够根据参数定义条件来进行缓存,它与@Cacheable不同的是,它每次都会真是调用函数,所以主要用于数据新增和修改操作上。它的参数与@Cacheable类似,具体功能可参考上面对@Cacheable参数的解析
@CacheEvict:配置于函数上,通常用在删除方法上,用来从缓存中移除相应数据。除了同@Cacheable一样的参数之外,它还有下面两个参数:
allEntries:非必需,默认为false。当为true时,会移除所有数据
beforeInvocation:非必需,默认为false,会在调用方法之后移除数据。当为true时,会在调用方法之前移除数据。
启动服务错误
Exception in thread "main" java.lang.UnsupportedClassVersionError: org/springframework/boot/SpringApplication : Unsupported major.minor version 52.0
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
at com.example.springboot.cache.SpringbootCacheApplication.main(SpringbootCacheApplication.java:11)
Process finished with exit code 1
解决办法:修改jdk版本
我的是jdk1.8编译的,所以启动是将jdk修改为同样版本即可。
启动服务测试
查询测试:http://localhost:8088//user/queryList