天天看点

Spring boot 2.x + Spring data jpa 2.x + Redis 2.x 数据缓存

本文案例使用 :  Spring boot 2.1.3.RELEASE  、 Spring  Data  Jpa  2.1.3.RELEASE 、 Redis  2.1.3.RELEASE

浅谈 Redis 应用场景:

  1. 数据高并发的读写
  2. 海量数据的读写
  3. 对扩展性要求高的数据

不适场景:Redis 更多理论

  1. 需要事务支持(非关系型数据库)
  2. 基于sql结构化查询储存,关系复杂

 为什么使用 Redis:

  1. 解决应用服务器的cpu和内存压力
  2. 减少io的读操作,减轻io的压力
  3. 关系型数据库的扩展性不强,难以改变表结构 
  4. nosql数据库没有关联关系,数据结构简单,拓展表比较容易   (nosql 是 Not Only SQL 的简称(非关系型数据库))
  5. nosql读取速度快,对较大数据处理快

                                          更多有关redis 理论请移步到  Redis 更多理论

集成 Redis 前提条件:

      本地或远程服务器必须安装上 Redis 相关服务。   

      具体安装步骤推荐几位大牛博客(可自行百度):

                 https://blog.csdn.net/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(可以使用其它方式调用) 调用接口:

Spring boot 2.x + Spring data jpa 2.x + Redis 2.x 数据缓存

这时查看 控制台 输出日志:

Spring boot 2.x + Spring data jpa 2.x + Redis 2.x 数据缓存

第一次访问查看数据库,并把查询到的接口放到缓存中, 第二次访问该接口时,并没有访问数据库,而是在缓存中直接获取并返回数据。这就说明 数据缓存成功了!!!!

       查看缓存数据:

Spring boot 2.x + Spring data jpa 2.x + Redis 2.x 数据缓存

 可是缓存的数据在哪可以看到呢?  这里推荐一个 客户端(Redis desktop manager), 因为官网下载需要付费, 这里找了一个保存在网盘, 供道友下载使用: 

    链接:https://pan.baidu.com/s/12cIigdI1Lbqx1_URmCID7w 

    提取码:f14b 

另附本文Demo 网盘地址:

    链接:https://pan.baidu.com/s/13Aj1dSnXNjg_0ZYvIokeUg 

    提取码:gpdy 

再推荐一篇大牛博文(值得一看):

        https://blog.csdn.net/u010588262/article/details/81003493

                  职场小白,望多多指教!!!!! 

原文参考地址:

          https://blog.csdn.net/u011277123/article/details/78692603

          https://blog.csdn.net/u010588262/article/details/81003493

继续阅读