本文案例使用 : Spring boot 2.1.3.RELEASE 、 Spring Data Jpa 2.1.3.RELEASE 、 Redis 2.1.3.RELEASE
浅谈 Redis 应用场景:
-
数据高并发的读写
-
海量数据的读写
-
对扩展性要求高的数据
不适场景:Redis 更多理论
-
需要事务支持(非关系型数据库)
-
基于sql结构化查询储存,关系复杂
为什么使用 Redis:
-
减少io的读操作,减轻io的压力
-
关系型数据库的扩展性不强,难以改变表结构
-
nosql数据库没有关联关系,数据结构简单,拓展表比较容易 (nosql 是 Not Only SQL 的简称(非关系型数据库))
-
nosql读取速度快,对较大数据处理快
更多有关redis 理论请移步到 Redis 更多理论
集成 Redis 前提条件:
本地或远程服务器必须安装上 Redis 相关服务。
具体安装步骤推荐几位大牛博客(可自行百度):
https://blog.youkuaiyun.com/u012343297/article/details/78839063
https://www.cnblogs.com/hellogt/p/6954263.html
具体代码:
一、 导入 Jar 包(这里是使用的 gradle 包管理工具):
compile('org.springframework.boot:spring-boot-starter-cache')
compile('org.springframework.boot:spring-boot-starter-data-redis')
二、创建RedisConfig 类,进行相关配置:
package com.hyredis.redisdemo5.RedisConfig;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
import java.time.Duration;
/**
* @Author: HouYi
* @Description: redis 缓存配置类
* @Date: Created in 10:10 2019/2/22 0022
* @Modified By:
*/
@Configuration
@EnableCaching
@Slf4j
public class RedisConfig extends CachingConfigurerSupport {
/***
* 自定义序列化
* @param factory
* @return
*/
@Bean(name="redisTemplate")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
redisTemplate.setKeySerializer(keySerializer());
redisTemplate.setHashKeySerializer(keySerializer());
redisTemplate.setValueSerializer(valueSerializer());
redisTemplate.setHashValueSerializer(valueSerializer());
return redisTemplate;
}
private RedisSerializer<String> keySerializer() {
return new StringRedisSerializer();
}
private RedisSerializer<Object> valueSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
/***
* 缓存管理器
* @param factory
* @return
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
// 配置序列化
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(20));
RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(redisCacheConfiguration)
.build();
return cacheManager;
}
/***
* 自定义缓存key生成策略
* @return
*/
@Bean
@Override
public KeyGenerator keyGenerator(){
return new KeyGenerator(){
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuffer sb = new StringBuffer();
sb.append(target.getClass().getName());
sb.append("#");
sb.append(method.getName());
sb.append("{");
for (Object param : params) {
sb.append(param.toString());
}
sb.append("}");
System.out.println("调用redis生成key:"+sb.toString());
return sb.toString();
}
};
}
}
三、Repository 层:
package com.hyredis.redisdemo5.repostory;
import com.hyredis.redisdemo5.entity.Book;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
/**
* @Author: HouYi
* @Description:
* @Date: Created in 19:47 2019/2/21 0021
* @Modified By:
*/
public interface BookRepostory extends JpaRepository<Book, Long>, JpaSpecificationExecutor<Book> {
}
四、Service层
package com.hyredis.redisdemo5.Service;
import com.hyredis.redisdemo5.entity.Book;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import java.util.List;
/**
* @Author: HouYi
* @Description:
* @Date: Created in 19:56 2019/2/21 0021
* @Modified By:
*/
public interface BookService {
/**
* 查询所有
* @param spec
* @param pageable
* @return
*/
Page<Book> findAll(Specification<Book> spec, Pageable pageable);
/**
* 查询所有(未分页)
* @return
*/
List<Book> findAll();
/**
* 添加
* @param book
*/
void save(Book book);
/***
* 编辑
* @param oldBook
* @param newBook
*/
void update(Book oldBook, Book newBook);
/***
* 删除
* @param books
*/
void delete(Book books);
/***
* 单条查询
* @param id
*/
Book findOne(Long id);
}
package com.hyredis.redisdemo5.Service;
import com.hyredis.redisdemo5.entity.Book;
import com.hyredis.redisdemo5.repostory.BookRepostory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @Author: HouYi
* @Description:
* @Date: Created in 19:56 2019/2/21 0021
* @Modified By:
*
*
* @Cacheable将查询结果缓存到redis中,(key=”#p0”)指定传入的第一个参数作为redis的key。
* @CachePut,指定key,将更新的结果同步到redis中
* @CacheEvict,指定key,删除缓存数据,allEntries=true,方法调用后将立即清除缓存
*/
@Service
@CacheConfig(cacheNames = "bookService")
public class BookServiceImpl implements BookService {
@Autowired
private BookRepostory bookRepostory;
@Override
public Page<Book> findAll(Specification<Book> spec, Pageable pageable) {
return bookRepostory.findAll(spec, pageable);
}
@Override
@Cacheable(value = "bookData")
public List<Book> findAll() {
return bookRepostory.findAll();
}
@Override
public void save(Book book) {
bookRepostory.save(book);
}
@Override
public void update(Book oldBook, Book newBook) {
oldBook.setBookName(newBook.getBookName());
oldBook.setBookPrice(newBook.getBookPrice());
oldBook.setBookDesc(newBook.getBookDesc());
bookRepostory.save(oldBook);
}
@Override
public void delete(Book books) {
bookRepostory.delete(books);
}
@Override
@Cacheable(value = "bookData", key = "#id")
public Book findOne(Long id) {
// TODO 注意这里是 getOne
return bookRepostory.findById(id).orElse(null);
}
}
五、Controller:
package com.hyredis.redisdemo5.controller;
import com.hyredis.redisdemo5.Service.BookService;
import com.hyredis.redisdemo5.entity.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @Author: HouYi
* @Description:
* @Date: Created in 9:27 2019/2/22 0022
* @Modified By:
*/
@RestController
@RequestMapping(value = "book")
public class BookController {
@Autowired
private BookService bookService;
@GetMapping(value = "/getList")
public ResponseEntity getDataList(){
return new ResponseEntity(bookService.findAll() ,HttpStatus.OK);
}
@GetMapping(value = "getOne")
public ResponseEntity findOne(@RequestParam Long id){
return new ResponseEntity(bookService.findOne(id), HttpStatus.OK);
}
@GetMapping
public ResponseEntity findAll(@RequestParam(required = false) String bookName,
@RequestParam(required = false) BigDecimal minBookPrice,
@RequestParam(required = false) BigDecimal maxBookPrice,
@PageableDefault(value = 10, sort = {"id"}, direction = Sort.Direction.DESC)Pageable pageable){
Page<Book> page = bookService.findAll(new Specification<Book>() {
@Override
public Predicate toPredicate(Root<Book> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
List<Predicate> list = new ArrayList<>();
if ((null != bookName) && (!"".equals(bookName))){
list.add(criteriaBuilder.like(root.<String>get("bookName"), "%"+bookName+"%"));
}
if ((null != minBookPrice) && (!"".equals(minBookPrice))){
list.add(criteriaBuilder.greaterThanOrEqualTo(root.<BigDecimal>get("bookPrice"), minBookPrice));
}
if ((null != maxBookPrice) && (!"".equals(maxBookPrice))){
list.add(criteriaBuilder.lessThanOrEqualTo(root.<BigDecimal>get("bookPrice"), maxBookPrice));
}
Predicate[] p = new Predicate[list.size()];
return criteriaBuilder.and(list.toArray(p));
}
}, pageable);
return new ResponseEntity(page, HttpStatus.OK);
}
@PostMapping
public ResponseEntity save(@RequestBody Book book){
bookService.save(book);
return new ResponseEntity(HttpStatus.OK);
}
@PutMapping
public ResponseEntity update(@RequestParam Long thisId,
@RequestBody Book newBook) throws Exception {
bookService.update(getBook(thisId), newBook);
return new ResponseEntity(HttpStatus.OK);
}
@DeleteMapping
public ResponseEntity delete(@RequestBody List<Long> ids) throws Exception {
Set<Book> books = getBook(ids);
if ((null != books) && (!books.isEmpty())){
for (Book book : books) {
bookService.delete(book);
}
}
return new ResponseEntity(HttpStatus.OK);
}
private Book getBook(Long id) throws Exception {
Book book = bookService.findOne(id);
if (null == book){
throw new Exception("操作失败, 单条查询为空!");
}
return book;
}
private Set<Book> getBook(List<Long> ids) throws Exception {
Set<Book> books = new HashSet<>();
if ((null != ids) && (!ids.isEmpty())) {
for (Long id : ids) {
Book book = bookService.findOne(id);
if (null == book) {
throw new Exception("操作失败, 单条查询为空!");
}
books.add(book);
}
}
return books;
}
}
注意下, 我这里 只有 getDataList() 和 findOne() 两个方法做了缓存。 这里之所以贴出所有代码, 为方便查看。
六、当以上代码都编写完成后, 启动项目, 使用 postman(可以使用其它方式调用) 调用接口:
这时查看 控制台 输出日志:
第一次访问查看数据库,并把查询到的接口放到缓存中, 第二次访问该接口时,并没有访问数据库,而是在缓存中直接获取并返回数据。这就说明 数据缓存成功了!!!!
查看缓存数据:
可是缓存的数据在哪可以看到呢? 这里推荐一个 客户端(Redis desktop manager), 因为官网下载需要付费, 这里找了一个保存在网盘, 供道友下载使用:
链接:https://pan.baidu.com/s/12cIigdI1Lbqx1_URmCID7w
提取码:f14b
另附本文Demo 网盘地址:
链接:https://pan.baidu.com/s/13Aj1dSnXNjg_0ZYvIokeUg
提取码:gpdy
再推荐一篇大牛博文(值得一看):
https://blog.youkuaiyun.com/u010588262/article/details/81003493
职场小白,望多多指教!!!!!
原文参考地址:
https://blog.youkuaiyun.com/u011277123/article/details/78692603
https://blog.youkuaiyun.com/u010588262/article/details/81003493