本章知识点:Spring Data JPA,Spring Boot 使用Mysql,Spring MVC,EHCache,Spring Cache等(其中@Cacheable请看上一节的理论知识),具体分如下几个步骤:
步骤 描述 1 新建Maven Java Project 2 在pom.xml中加入依赖包 3 编写Spring Boot启动类; 4 配置application.properties; 5 编写缓存配置类以及ehcache.xml配置文件 6 编写DemoInfo实体类进行测试; 7 编写持久类DemoInfoRepository; 8 编写处理类DemoInfoService; 9 编写DemoInfoController测试类;运行测试接下来是具体实现
1)新建Maven Java Project
新建一个工程名为spring-boot-ehcache的maven java project。
(2)在pom.xml中加入依赖包
在pom.xml文件中加入相应的依赖包,Spring Boot父节点依赖包;spring boot web支持;缓存依赖spring-context-support;集成ehcache需要的依赖;JPA操作数据库;mysql 数据库驱动,具体pom.xml文件:
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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 < 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.kfit</ groupId > < artifactId >spring-boot-ehcache</ artifactId > < version >0.0.1-SNAPSHOT</ version > < packaging >jar</ packaging > < name >spring-boot-ehcache</ name > < url >http://maven.apache.org</ url > < properties > < project.build.sourceEncoding >UTF-8</ project.build.sourceEncoding > <!-- 配置JDK编译版本. --> < java.version >1.8</ java.version > </ properties > <!-- spring boot 父节点依赖, 引入这个之后相关的引入就不需要添加version配置, spring boot会自动选择最合适的版本进行添加。 --> < parent > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-parent</ artifactId > < version >1.3.3.RELEASE</ version > </ parent > < dependencies > <!-- 单元测试. --> < dependency > < groupId >junit</ groupId > < artifactId >junit</ artifactId > < scope >test</ scope > </ dependency > <!-- spring boot web支持:mvc,aop... --> < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-web</ artifactId > </ dependency > <!-- 包含支持UI模版(Velocity,FreeMarker,JasperReports), 邮件服务, 脚本服务(JRuby), 缓存Cache(EHCache), 任务计划Scheduling(uartz)。 --> < dependency > < groupId >org.springframework</ groupId > < artifactId >spring-context-support</ artifactId > </ dependency > <!-- 集成ehcache需要的依赖--> < dependency > < groupId >net.sf.ehcache</ groupId > < artifactId >ehcache</ artifactId > </ dependency > <!-- JPA操作数据库. --> < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-data-jpa</ artifactId > </ dependency > <!-- mysql 数据库驱动. --> < dependency > < groupId >mysql</ groupId > < artifactId >mysql-connector-java</ artifactId > </ dependency > <!-- Spring boot单元测试. --> < dependency > < groupId >org.springframework.boot</ groupId > < artifactId >spring-boot-starter-test</ artifactId > < scope >test</ scope > </ dependency > </ dependencies > </ project >3)编写Spring Boot启动类(com.kfit.App.java);
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 package com.kfit; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * * * @SpringBootApplication申明让spring boot自动给程序进行必要的配置, * @SpringBootApplication 等待于: @Configuration @EnableAutoConfiguration @ComponentScan * * * @version v.0.1 */ @SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App. class , args); } }(4)配置application.properties;
在application.properties中主要配置数据库连接和JPA的基本配置,具体如下:
Src/main/resouces/application.properties:
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 ######################################################## ###datasource ,mysql数据库连接配置 ######################################################## spring.datasource.url = jdbc:mysql: //localhost:3306/test spring.datasource.username = root spring.datasource.password = root spring.datasource.driverClassName = com.mysql.jdbc.Driver spring.datasource.max-active= 20 spring.datasource.max-idle= 8 spring.datasource.min-idle= 8 spring.datasource.initial-size= 10 ######################################################## ### Java Persistence Api ,JPA自动建表操作配置 ######################################################## # Specify the DBMS spring.jpa.database = MYSQL # Show or not log for each sql query spring.jpa.show-sql = true # Hibernate ddl auto (create, create-drop, update) spring.jpa.hibernate.ddl-auto = update # Naming strategy spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy # stripped before adding them to the entity manager) spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect5)编写缓存配置类以及ehcache.xml配置文件:
这个类主要是注册缓存管理对象EhCacheCacheManager、缓存工厂对象EhCacheManagerFactoryBean,具体代码如下:
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 package com.kfit.config; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.ehcache.EhCacheCacheManager; import org.springframework.cache.ehcache.EhCacheManagerFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; /** * 缓存配置. * * @version v.0.1 */ @Configuration @EnableCaching //标注启动缓存. public class CacheConfiguration { /** * ehcache 主要的管理器 * @param bean * @return */ @Bean public EhCacheCacheManager ehCacheCacheManager(EhCacheManagerFactoryBean bean){ System.out.println( "CacheConfiguration.ehCacheCacheManager()" ); return new EhCacheCacheManager(bean.getObject()); } /* * 据shared与否的设置, * Spring分别通过CacheManager.create() * 或new CacheManager()方式来创建一个ehcache基地. * * 也说是说通过这个来设置cache的基地是这里的Spring独用,还是跟别的(如hibernate的Ehcache共享) * */ @Bean public EhCacheManagerFactoryBean ehCacheManagerFactoryBean(){ System.out.println( "CacheConfiguration.ehCacheManagerFactoryBean()" ); EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean (); cacheManagerFactoryBean.setConfigLocation ( new ClassPathResource( "conf/ehcache.xml" )); cacheManagerFactoryBean.setShared( true ); return cacheManagerFactoryBean; } }在src/main/resouces/conf下编写ehcache.xml配置文件,当然这个文件你可以放在其它目录下:
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 55 56 57 <? xml version = "1.0" encoding = "UTF-8" ?> < ehcache xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation = "http://ehcache.org/ehcache.xsd" updateCheck = "false" > <!-- diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下: user.home – 用户主目录 user.dir – 用户当前工作目录 java.io.tmpdir – 默认临时文件路径 --> < diskStore path = "java.io.tmpdir/Tmp_EhCache" /> <!-- defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。 --> <!-- name:缓存名称。 maxElementsInMemory:缓存最大数目 maxElementsOnDisk:硬盘最大缓存个数。 eternal:对象是否永久有效,一但设置了,timeout将不起作用。 overflowToDisk:是否保存到磁盘,当系统当机时 timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。 timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。 diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false. diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。 diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。 memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。 clearOnFlush:内存数量最大时是否清除。 memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。 FIFO,first in first out,这个是大家最熟的,先进先出。 LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。 LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。 --> < defaultCache eternal = "false" maxElementsInMemory = "1000" overflowToDisk = "false" diskPersistent = "false" timeToIdleSeconds = "0" timeToLiveSeconds = "600" memoryStoreEvictionPolicy = "LRU" /> < cache name = "demo" eternal = "false" maxElementsInMemory = "100" overflowToDisk = "false" diskPersistent = "false" timeToIdleSeconds = "0" timeToLiveSeconds = "300" memoryStoreEvictionPolicy = "LRU" /> </ ehcache >(6)编写DemoInfo实体类进行测试;
在com.kfit.bean下编写DemoInfo实体类进行缓存测试:
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 package com.kfit.bean; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; /** * 测试实体类. * @version v.0.1 */ @Entity public class DemoInfo { @Id @GeneratedValue private longid; //主键. private String name; //名称; private String pwd; //密码; private intstate; public long getId() { returnid; } public void setId(longid) { this .id = id; } public String getName() { returnname; } publicvoid setName(String name) { this .name = name; } public String getPwd() { returnpwd; } public void setPwd(String pwd) { this .pwd = pwd; } public int getState() { returnstate; } public void setState(intstate) { this .state = state; } @Override public String toString() { return "DemoInfo [id=" + id + ", name=" + name + ", pwd=" + pwd + ", state=" + state + "]" ; } }(7)编写持久类DemoInfoRepository;
编写持久类DemoInfoRepository:
com.kfit.repository.DemoInfoRepository:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.kfit.repository; import org.springframework.data.repository.CrudRepository; import com.kfit.bean.DemoInfo; /** * 操作数据库. * * @version v.0.1 */ public interface DemoInfoRepository extends CrudRepository<DemoInfo,Long>{ }(8)编写处理类DemoInfoService;
编写增删改查的方法,在这几个方法中都使用注解缓存,进行缓存的创建以及删除,修改等操作:
com.kfit.service.DemoInfoService:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.kfit.service; import com.kfit.bean.DemoInfo; import javassist.NotFoundException; public interface DemoInfoService { void delete(Long id); DemoInfo update(DemoInfo updated) throws NotFoundException; DemoInfo findById(Long id); DemoInfo save(DemoInfo demoInfo); }com.kfit.service.impl.DemoInfoServiceImpl:
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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 package com.kfit.service.impl; import javax.annotation.Resource; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import com.kfit.bean.DemoInfo; import com.kfit.repository.DemoInfoRepository; import com.kfit.service.DemoInfoService; import javassist.NotFoundException; @Service public class DemoInfoServiceImpl implements DemoInfoService { //这里的单引号不能少,否则会报错,被识别是一个对象; public static final String CACHE_KEY = "'demoInfo'" ; @Resource private DemoInfoRepository demoInfoRepository; /** * value属性表示使用哪个缓存策略,缓存策略在ehcache.xml */ public static final String DEMO_CACHE_NAME = "demo" ; /** * 保存数据. * @param demoInfo */ @CacheEvict (value=DEMO_CACHE_NAME,key=CACHE_KEY) @Override public DemoInfo save(DemoInfo demoInfo){ return demoInfoRepository.save(demoInfo); } /** * 查询数据. * @param id * @return */ @Cacheable (value=DEMO_CACHE_NAME,key= "'demoInfo_'+#id" ) @Override public DemoInfo findById(Long id){ System.err.println( "没有走缓存!" +id); return demoInfoRepository.findOne(id); } /** * http://www.mincoder.com/article/2096.shtml: * * 修改数据. * * 在支持Spring Cache的环境下,对于使用@Cacheable标注的方法,Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中。@CachePut也可以声明一个方法支持缓存功能。与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。 @CachePut也可以标注在类上和方法上。使用@CachePut时我们可以指定的属性跟@Cacheable是一样的。 * * @param updated * @return * * @throws NotFoundException */ @CachePut (value = DEMO_CACHE_NAME,key = "'demoInfo_'+#updated.getId()" ) //@CacheEvict(value = DEMO_CACHE_NAME,key = "'demoInfo_'+#updated.getId()")//这是清除缓存. @Override public DemoInfo update(DemoInfo updated) throws NotFoundException{ DemoInfo demoInfo = demoInfoRepository.findOne(updated.getId()); if (demoInfo == null ){ thrownew NotFoundException( "No find" ); } demoInfo.setName(updated.getName()); demoInfo.setPwd(updated.getPwd()); return demoInfo; } /** * 删除数据. * @param id */ @CacheEvict (value = DEMO_CACHE_NAME,key = "'demoInfo_'+#id" ) //这是清除缓存. @Override public void delete(Long id){ demoInfoRepository.delete(id); } }(9)编写DemoInfoController测试类;
编写一个rest进行测试:
com.kfit.controller.DemoInfoController:
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 55 56 57 58 59 60 61 62 63 package com.kfit.controller; import javax.annotation.Resource; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.kfit.bean.DemoInfo; import com.kfit.service.DemoInfoService; import javassist.NotFoundException; @RestController public class DemoInfoController { @Resource private DemoInfoService demoInfoService; @RequestMapping ( "/test" ) public String test(){ //存入两条数据. DemoInfo demoInfo = new DemoInfo(); demoInfo.setName( "张三" ); demoInfo.setPwd( "123456" ); DemoInfo demoInfo2 = demoInfoService.save(demoInfo); //不走缓存. System.out.println(demoInfoService.findById(demoInfo2.getId())); //走缓存. System.out.println(demoInfoService.findById(demoInfo2.getId())); demoInfo = new DemoInfo(); demoInfo.setName( "李四" ); demoInfo.setPwd( "123456" ); DemoInfo demoInfo3 = demoInfoService.save(demoInfo); //不走缓存. System.out.println(demoInfoService.findById(demoInfo3.getId())); //走缓存. System.out.println(demoInfoService.findById(demoInfo3.getId())); System.out.println( "============修改数据=====================" ); //修改数据. DemoInfo updated = new DemoInfo(); updated.setName( "李四-updated" ); updated.setPwd( "123456" ); updated.setId(demoInfo3.getId()); try { System.out.println(demoInfoService.update(updated)); } catch (NotFoundException e) { e.printStackTrace(); } //不走缓存. System.out.println(demoInfoService.findById(updated.getId())); return "ok" ; } }(10)运行测试;
运行App.java进行测试,访问:http://127.0.0.1:8080/test 进行测试,主要是观察控制台的打印信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Hibernate: insert into demo_info (name, pwd, state) values (?, ?, ?) 没有走缓存! 52 DemoInfo [id= 52 , name=张三, pwd= 123456 , state= 0 ] DemoInfo [id= 52 , name=张三, pwd= 123456 , state= 0 ] Hibernate: insert into demo_info (name, pwd, state) values (?, ?, ?) 没有走缓存! 53 DemoInfo [id= 53 , name=李四, pwd= 123456 , state= 0 ] DemoInfo [id= 53 , name=李四, pwd= 123456 , state= 0 ] ============修改数据===================== DemoInfo [id= 53 , name=李四-updated, pwd= 123456 , state= 0 ] DemoInfo [id= 53 , name=李四-updated, pwd= 123456 , state= 0 ] C:\Users\ADMINI~ 1 .ANG\AppData\Local\Temp\ Hibernate: insert into demo_info (name, pwd, state) values (?, ?, ?) 没有走缓存! 54 DemoInfo [id= 54 , name=张三, pwd= 123456 , state= 0 ] DemoInfo [id= 54 , name=张三, pwd= 123456 , state= 0 ] Hibernate: insert into demo_info (name, pwd, state) values (?, ?, ?) 没有走缓存! 55 DemoInfo [id= 55 , name=李四, pwd= 123456 , state= 0 ] DemoInfo [id= 55 , name=李四, pwd= 123456 , state= 0 ] ============修改数据===================== DemoInfo [id= 55 , name=李四-updated, pwd= 123456 , state= 0 ] DemoInfo [id= 55 , name=李四-updated, pwd= 123456 , state= 0 ]