创立缓存服务
创立缓存服务接口我的项目
- 创立myshop-service-redis-api我的项目,该我的项目只负责定义接口
创立我的项目的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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.oxford</groupId> <artifactId>myshop-dependencies</artifactId> <version>1.0.0-SNAPSHOT</version> <relativePath>../myshop-dependencies/pom.xml</relativePath> </parent> <artifactId>myshop-service-redis-api</artifactId> <packaging>jar</packaging></project>
定义数据Redis接口RedisService:
package com.oxford.myshop.service.redis.apipublic interface RedisService{ void set(String key,Object value); void set(String key,Object value,int seconds); void del(String key); Object get(String key);}
创立缓存服务提供者我的项目
- 创立myshop-service-redis-provider我的项目,该我的项目用作缓存服务提供者
创立我的项目的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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.oxford</groupId> <artifactId>myshop-dependencies</artifactId> <version>1.0.0-SNAPSHOT</version> <relativePath>../myshop-dependencies/pom.xml</relativePath> </parent> <artifactId>myshop-service-redis-api</artifactId> <packaging>jar</packaging> <dependencies> <!-- Spring Boot Starter Settings--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--Common Setting--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <dependency> <groupId>de.javakaffee</groupId> <artifactId>kryo-serializers</artifactId> </dependency> <!--Project Settings--> <dependency> <groupId>com.oxford</groupId> <artifactId>my-shop-commons-dubbo</artifactId> <version>${Project.parent.version}</version> </dependency> <dependency> <groupId>com.oxford</groupId> <artifactId>my-shop-service-redis-api</artifactId> <version>${Project.parent.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>com.oxford.myshop.service.redis.provider.MyshopServiceRedisProviderApplication</mainClass> </configuration> </plugin> </plugins> </build></project>
Redis底层实现的Java的lettuce客户端
创立缓存服务接口实现类RedisServiceImpl
package com.oxford.myshop.service.redis.provider.api.impl;@Service(version="${service.versions.redis.v1}")public class RedisServiceImpl implements RedisService{ @Override public void set(String key,Object value){ redisTemplate.opsForValue().set(key,value); } @Override public void set(String key,Object value,int seconds){ redisTemplate.opsForValue().set(key,value,seconds,TimeUnit.SECONDS); } @Override public void del(String key){ redisTemplate.delete(key); } @Override public Object get(String key){ return redisTemplate.opsForValue().get(key); }}
创立启动类SpringBootApplication
package com.oxford.myshop.service.redis.provider;import com.alibaba.dubbo.container.Main;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.netflix.hystrix.EnableHystrix;import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;@EnableHystrix@EnableHystrixDashboardpublic class MyShopServiceRedisrProviderApplication { public static void main(String[]args) { SpringApplication.run(MyShopServiceRedisProviderApplication.class,args); Main.main(args); }}
创立配置文件application.yml
spring:application: name: myshop-service-redis-providerredis: lettuce: pool: max-active: 8 max-idle: 8 max-wait: -1ms min-idle: 0 sentinel: master: mymaster nodes: 192.168.32.255:26379,192.168.32.255:26380,192.168.32.255:26381server:port: 8503services:version: redis: v1: 1.0.0 user: v1: 1.0.0dubbo:scan: basePackages: com.oxford.myshop.service.redis.provider.api.implapplication: id: com.oxford.myshop.service.redis.provider.api name: com.oxford.myshop.service.redis.provider.api qos-port: 22224 qos-enable: trueprotocal: id: dubbo name: dubbo port: 20883 status: server serialization: kryoregitry: id: zookeeper address: zookeeper://localhost:2181?backup=192.168.32.255:2182,192.168.32.255:2183management:endpoint: dubbo: enabled: true dubbo-shutdown: enabled: true dubbo-configs: enabled: true dubbo-sevicies: enabled: true dubbo-reference: enabled: true dubbo-properties: enabled: truehealth: dubbo: status: defaults: memory extras: load,threadpool
创立缓存服务消费者我的项目
- 在pom文件中引入redis接口依赖
在缓存服务消费者我的项目的ServiceImpl中调用RedisService
@Reference(version="services.versions.redis.v1")private RedisService redisService;
MyBatis Redis二级缓存
MyBatis缓存
一级缓存:
- MyBatis会在示意会话的SqlSession对象中建设一个简略的缓存: 将每次查问到的后果缓存起来,当下次查问的时候,如果判断先前有个齐全一样的查问,会间接从缓存中间接将后果取出,返回给用户,不须要再进行一次数据库查问
一级缓存是SqlSession级别的缓存:
- 在操作数据库时须要结构SqlSession对象
- 对象中有一个(内存区域)数据结构(HashMap)用于存储缓存数据
- 不同的SqlSession之间的缓存数据区域(HashMap)互不影响,
- 一级缓存的作用域是同一个SqlSession
- 在同一个SqlSession中两次执行雷同的SQL语句: 第一次执行结束会将数据库中查问的数据写到缓存(内存),第二次会从缓存中获取数据,将不再从数据库查问,从而进步查问效率
- 当一个SqlSession完结后该SqlSession中的一级缓存就不存在了
- MyBatis默认开启一级缓存
二级缓存:
- 二级缓存是Mapper级别的缓存: 多个SqlSession去操作同一个Mapper的SQL语句,多个SqlSession去操作数据库失去数据会存在二级缓存区域,多个SqlSession能够共用二级缓存,二级缓存是跨SqlSession的
- 二级缓存的作用域是mapper的同一个namespace
- 不同的SqlSession两次执行雷同namespace下的SQL语句且向SQL中传递参数也雷同即最终执行雷同的SQL语句: 第一次执行结束会将数据库中查问的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查问,从而进步查问效率
MyBatis默认没有开启二级缓存,须要在setting全局参数中配置开启二级缓存
配置MyBatis二级缓存
SpringBoot中开启MyBatis二级缓存
在myshop-service-user-provider的配置文件中开启MyBatis二级缓存
spring:application: name: myshop-service-user-providerdatasource: druid: url: jdbc:mysql://localhost:3306/myshop?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: 123456 initial-size: 1 min-idle: 1 main-active: 20 test-on-borrow: true driver-class-name: com.mysql.cj.jdbc.Driverredis: lettuce: pool: max-active: 8 max-idle: 8 max-wait: -1ms min-idle: 0 sentinel: master: mymaster nodes: 192.168.32.255:26379,192.168.32.255:26380,192.168.32.255:26381server:port: 8501# MyBatis Config propertiesmybatis:configuration: cache-enabled: truetype-aliases-package: com.oxford.myshop.commons.domainmapper-location: classpath:mapper/*.xmlservices:version: redis: v1: 1.0.0 user: v1: 1.0.0dubbo:scan: basePackages: com.oxford.myshop.service.user.provider.api.implapplication: id: com.oxford.myshop.service.user.provider.api name: com.oxford.myshop.service.user.provider.api qos-port: 22222 qos-enable: trueprotocal: id: dubbo name: dubbo port: 20001 status: server serialization: kryoregitry: id: zookeeper address: zookeeper://localhost:2181?backup=192.168.32.255:2182,192.168.32.255:2183management:endpoint: dubbo: enabled: true dubbo-shutdown: enabled: true dubbo-configs: enabled: true dubbo-sevicies: enabled: true dubbo-reference: enabled: true dubbo-properties: enabled: truehealth: dubbo: status: defaults: memory extras: load,threadpool
在myshop-commons-mapper的pom.xml中减少redis依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifacted></dependency><dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifacted></dependency>
实体类实现序列化接口并申明序列号
private static final long serialVersionUID = 82897704415244673535L
- 应用GenerateSerialVersionUID插件生成,装置完插件后在实现了序列化接口的类中
应用快捷键Alt+Insert即可呼出生成菜单,即可主动生成序列号
在myshop-commons我的项目中创立ApplicationContextHolder类
package com.oxford.myshop.commons.context;@Componentpublic class ApplicationContextHolder implements ApplicationContextAware,DisposableBean{ private static final Logger logger=LoggerFactory.getLogger(ApplicationContext.class); private static ApplicationContext applicationContext; /** * 获取存储在动态变量中的ApplicationContext */ public static ApplicationContext getApplicationContext(){ assertContextInjected(); return applicationContext; } /** * 从动态变量applicationContext中获取Bean,主动转型成所赋值对象的类型 */ public static <T> T getBean(String name){ assertContextInjected(); return (T) applicationContext.getBean(name); } /** * 从动态变量applicationContext中获取Bean,主动转型成所赋值对象的类型 */ public static <T> T getBean(Class<T> clazz){ assertContextInjected(); return (T) applicationContext.getBean(clazz); } /** * 实现DisposableBean接口,在Context敞开时清理动态变量 */ public void destroy() throws Exception{ logger.debug("革除 SpringContext 中的 ApplicationContext: {}",applicationContext); applicationContext=null; } /** * 实现ApplicationContextAware接口,注入Context到动态变量中 */ public void setApplicationContext(ApplicationContext applicationContext) throws BeanException{ ApplicationContext.applicationContext=applicationContext; } /** * 断言Context曾经注入 */ private static void assertContextInjected(){ Validate.validState(applicationContext !=null,"applicationContext 属性未注入,请在配置文件中配置定义ApplicationContextContext"); }}
在myshop-commons-mapper我的项目中创立一个RedisCache的工具类
package com.oxford.myshop.commons.utils;public class RedisCache implements Cache{ private static final Logger logger=LoggerFactory.getLogger(RedisCache.class); private final ReadWriteLock readWriteLock=new ReentranReadWriteLock(); private final String id; private RedisTemplate redisTemplate; private static final long EXPIRE_TIME_IN_MINUTES=30 // redis过期工夫 public RedisCache(String id){ if(id==null){ throw new IllegalArgumentException("Cache instances require an ID"); } this.id=id; } @Override public String getId(){ return id; } /** * Put query result to redis */ @Override public void putObject(Object key,Object value){ try{ RedisTemplate redisTemplate=getRedisTemplate(); ValueOperations opsForValue=redisTemplate.opsForValue(); opsForValue.set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES); logger.debug("Put query result to redis"); }catch(Throwable t){ logger.error("Redis put failed",t); } } /** * Get cached query result from redis */ @Override public Object getObject(Object key){ try{ RedisTemplate redisTemplate=getRedisTemplate(); ValueOperations opsForValue=redisTemplate.opsForValue(); opsForValue.set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES); logger.debug("Get cache query result from redis"); return opsForValue.get(key); }catch(Throwable t){ logger.error("Redis get failed, fail over to db"); return null; } } /** * Get cached query result from redis */ @Override public Object getObject(Object key){ try{ RedisTemplate redisTemplate=getRedisTemplate(); ValueOperations opsForValue=redisTemplate.opsForValue(); opsForValue.set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES); logger.debug("Get cache query result from redis"); return opsForValue.get(key); }catch(Throwable t){ logger.error("Redis get failed, fail over to db"); return null; } } /** * Remove cached query result from redis */ @Override @SuppressWarnings("unchecked") public Object removeObject(Object key){ try{ RedisTemplate redisTemplate=getRedisTemplate(); redisTemplate.delete(key); logger.debug("Remove cached query result from redis"); }catch(Throwable t){ logger.error("Redis remove failed"); } return null; } /** * Clear this cache instance */ @Override public void clear(){ RedisTemplate redisTemplate=getRedisTemplate(); redisTemplate.execute((RedisCallback)->{ connection.flushDb(); return null; }); logger.debug("Clear all the cached query result from redis"); } @Override public int getSize(){ return 0; } @Override public ReadWriteLock getReadWriteLock(){ return readWriteLock; } private RedisTemplate getRedisTemplate(){ if(redisTemplate==null){ redisTemplate=ApplicationContextHolder.getBean("redisTemplate"); } return redisTemplate; }}
Mapper接口类中标注注解
在Mapper接口类上标注注解,申明应用二级缓存
@CacheNamespace(implementation=RedisCache.class)