SpringBoot系列教材 (二十四)- 其他 - springboot jpa redis 整合在一起做一套带分页的 增删改查 crud

步骤1:Redis
步骤2:运行 Redis 服务器
步骤3:基于前面的知识点
步骤4:先运行,看到效果,再学习
步骤5:模仿和排错
步骤6:改动
步骤7:pom.xml
步骤8:application.properties
步骤9:Application
步骤10:RedisConfig.java
步骤11:Page4Navigator
步骤12:CategoryService
步骤13:CategoryServiceImpl
步骤14:CategoryController

步骤 1 : Redis

Redis 是一套 key-value 高性能数据库。
关于 Redis 如何安装,运用有专门的教程,在这里就不展开了。
Redis 教程

步骤 2 : 运行 Redis 服务器

按照 Redis 如何运行 中的做法,把 Redis 服务器先跑起来,这个不跑起来,本教程就没法撸了。

运行 Redis 服务器

步骤 3 : 基于前面的知识点

本教程在前面的知识点:SPRINGBOOT使用JPA实现完整的CRUD和分页 的基础上进行扩展。 所以没有撸前面的同学,请先撸了,不然这里也撸不出来,因为数据库怎么建立都是在前面写的,你没做,后面也没法做的。

步骤 4 : 先运行,看到效果,再学习

老规矩,先下载下载区(点击进入)的可运行项目,配置运行起来,确认可用之后,再学习做了哪些步骤以达到这样的效果。
启动后,访问如下地址

http://127.0.0.1:8080/listCategory


可以看到如图所示的 CRUD 和分页的效果。
虽然,在使用效果上,看起来和 SPRINGBOOT使用JPA实现完整的CRUD和分页 没有任何分别,但是在后台,已经把数据都撸到 Redis 里面缓存起来了。

先运行,看到效果,再学习

步骤 5 : 模仿和排错

在确保可运行项目能够正确无误地运行之后,再严格照着教程的步骤,对代码模仿一遍。
模仿过程难免代码有出入,导致无法得到期望的运行结果,此时此刻通过比较正确答案 ( 可运行项目 ) 和自己的代码,来定位问题所在。
采用这种方式,学习有效果,排错有效率,可以较为明显地提升学习速度,跨过学习路上的各个槛。

推荐使用diffmerge软件,进行文件夹比较。把你自己做的项目文件夹,和我的可运行项目文件夹进行比较。
这个软件很牛逼的,可以知道文件夹里哪两个文件不对,并且很明显地标记出来
这里提供了绿色安装和使用教程:diffmerge 下载和使用教程

步骤 6 : 改动

接下来,就一条条地说,在原来项目的基础上做了什么改动,以达到这样的效果

步骤 7 : pom.xml

增加对 Redis 支持的包

<?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.how2java</groupId>

  <artifactId>springboot</artifactId>

  <version>0.0.1-SNAPSHOT</version>

  <name>springboot</name>

  <description>springboot</description>

  <packaging>war</packaging>

   

    <parent>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-parent</artifactId>

        <version>1.5.9.RELEASE</version>

    </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-tomcat</artifactId>

             

        </dependency>

        <dependency>

              <groupId>junit</groupId>

              <artifactId>junit</artifactId>

              <version>3.8.1</version>

              <scope>test</scope>

        </dependency>

        <!-- servlet依赖. -->

        <dependency>

              <groupId>javax.servlet</groupId>

              <artifactId>javax.servlet-api</artifactId>

               

        </dependency>

              <dependency>

                     <groupId>javax.servlet</groupId>

                     <artifactId>jstl</artifactId>

              </dependency>

        <!-- tomcat的支持.-->

        <dependency>

               <groupId>org.apache.tomcat.embed</groupId>

               <artifactId>tomcat-embed-jasper</artifactId>

                

        </dependency>    

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-devtools</artifactId>

            <optional>true</optional<!-- 这个需要为 true 热部署才有效 -->

        </dependency>

         

        <!-- mysql-->

        <dependency>

            <groupId>mysql</groupId>

            <artifactId>mysql-connector-java</artifactId>

            <version>5.1.21</version>

        </dependency>

        <!-- jpa-->

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-data-jpa</artifactId>

        </dependency>

        <!-- redis -->       

        <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>       

        <dependency>

          <groupId>junit</groupId>

          <artifactId>junit</artifactId>

          <version> 4.12</version>

        </dependency>       

    </dependencies>

    <properties>

        <java.version>1.8</java.version>

    </properties>

    <build>

        <plugins>

            <plugin>

                <groupId>org.springframework.boot</groupId>

                <artifactId>spring-boot-maven-plugin</artifactId>

            </plugin>

        </plugins>

    </build>

</project>

步骤 8 : application.properties

增加redis相关配置
同时让hibernate的sql语句显示出来,这样才知道到底是通过 Redis 取到的数据,还是依然是从数据库取到的数据

spring.jpa.show-sql=true

spring.mvc.view.prefix=/WEB-INF/jsp/

spring.mvc.view.suffix=.jsp

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/how2java?characterEncoding=UTF-8

spring.datasource.username=root

spring.datasource.password=admin

spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.jpa.properties.hibernate.hbm2ddl.auto=update

###########################redis#########################

#Redis数据库索引(默认为0)

spring.redis.database=0

#Redis服务器地址

spring.redis.host=127.0.0.1

#Redis服务器连接端口

spring.redis.port=6379

#Redis服务器连接密码(默认为空)

spring.redis.password=

#连接池最大连接数(使用负值表示没有限制)

spring.redis.pool.max-active=10

#连接池最大阻塞等待时间(使用负值表示没有限制)

spring.redis.pool.max-wait=-1

#连接池中的最大空闲连接

spring.redis.pool.max-idle=8

#连接池中的最小空闲连接

spring.redis.pool.min-idle=0

#连接超时时间(毫秒)

spring.redis.timeout=0

spring.jpa.show-sql=true

步骤 9 : Application

增加注解,以开启缓存

@EnableCaching

package com.how2java.springboot;

  

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cache.annotation.EnableCaching;

  

@SpringBootApplication

@EnableCaching

public class Application {

  

    public static void main(String[] args) {

        SpringApplication.run(Application.class, args);

    }

  

}

步骤 10 : RedisConfig.java

Redis 缓存配置类。
这个配置,一个作用: 让保存到 Redis 里的 key 和 value 都转换为可读的 json 格式。 否则会是二进制格式,通过RedisClient 工具也无法识别。

package com.how2java.springboot.config;

import org.springframework.cache.CacheManager;

import org.springframework.cache.annotation.CachingConfigurerSupport;

import org.springframework.cache.annotation.EnableCaching;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.data.redis.cache.RedisCacheManager;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

import org.springframework.data.redis.serializer.RedisSerializer;

import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.fasterxml.jackson.annotation.JsonAutoDetect;

import com.fasterxml.jackson.annotation.PropertyAccessor;

import com.fasterxml.jackson.databind.ObjectMapper;

@Configuration

@EnableCaching

//Redis 缓存配置类

public class RedisConfig extends CachingConfigurerSupport {

    @Bean

    public CacheManager cacheManager(RedisTemplate<?,?> redisTemplate) {

        RedisSerializer stringSerializer = new StringRedisSerializer();

        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        ObjectMapper om = new ObjectMapper();

        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.PUBLIC_ONLY);

        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        jackson2JsonRedisSerializer.setObjectMapper(om);

        redisTemplate.setKeySerializer(stringSerializer);

        redisTemplate.setHashKeySerializer(stringSerializer);  

         

        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);         

        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

        CacheManager cacheManager = new RedisCacheManager(redisTemplate);

        return cacheManager;

  

    }

}

步骤 11 : Page4Navigator

创建一个工具类 Page4Navigator 用以替换 原本分页查询要返回的 org.springframework.data.domain.Page 类。 原因是 Page 类对json 还原不支持,在放如 Redis 之后,再拿出来,就会报错失败。
使用 Page4Navigator 对其包裹,就解决了这个问题了。

package com.how2java.springboot.util;

import java.util.List;

import org.springframework.data.domain.Page;

public class Page4Navigator<T> {

    Page<T> page4jpa;

    int navigatePages;

     

    int totalPages;

    int number;

     

    long totalElements;

     

    int size;

    int numberOfElements;

    List<T> content;

    boolean isHasContent;

    boolean first;

    boolean last;

     

    boolean isHasNext;

    boolean isHasPrevious;

     

    int[] navigatepageNums;

     

    public Page4Navigator() {

        //这个空的分页是为了 Redis 从 json格式转换为 Page4Navigator 对象而专门提供的

    }

     

    public Page4Navigator(Page<T> page4jpa,int navigatePages) {

        this.page4jpa = page4jpa;

        this.navigatePages = navigatePages;

         

        totalPages = page4jpa.getTotalPages();

         

        number  = page4jpa.getNumber() ;

         

        totalElements = page4jpa.getTotalElements();

         

        size = page4jpa.getSize();

         

        numberOfElements = page4jpa.getNumberOfElements();

         

        content = page4jpa.getContent();

         

        isHasContent = page4jpa.hasContent();

                 

        first = page4jpa.isFirst();

         

        last = page4jpa.isLast();

         

        isHasNext = page4jpa.hasNext();

         

        isHasPrevious  = page4jpa.hasPrevious();       

         

        calcNavigatepageNums();

         

    }

    private void calcNavigatepageNums() {

        int navigatepageNums[];

        int totalPages = getTotalPages();

        int num = getNumber();

        //当总页数小于或等于导航页码数时

        if (totalPages <= navigatePages) {

            navigatepageNums = new int[totalPages];

            for (int i = 0; i < totalPages; i++) {

                navigatepageNums[i] = i + 1;

            }

        else //当总页数大于导航页码数时

            navigatepageNums = new int[navigatePages];

            int startNum = num - navigatePages / 2;

            int endNum = num + navigatePages / 2;

            if (startNum < 1) {

                startNum = 1;

                //(最前navigatePages页

                for (int i = 0; i < navigatePages; i++) {

                    navigatepageNums[i] = startNum++;

                }

            else if (endNum > totalPages) {

                endNum = totalPages;

                //最后navigatePages页

                for (int i = navigatePages - 1; i >= 0; i--) {

                    navigatepageNums[i] = endNum--;

                }

            else {

                //所有中间页

                for (int i = 0; i < navigatePages; i++) {

                    navigatepageNums[i] = startNum++;

                }

            }

        }  

        this.navigatepageNums = navigatepageNums;

    }

    public int getNavigatePages() {

        return navigatePages;

    }

    public void setNavigatePages(int navigatePages) {

        this.navigatePages = navigatePages;

    }

    public int getTotalPages() {

        return totalPages;

    }

    public void setTotalPages(int totalPages) {

        this.totalPages = totalPages;

    }

    public int getNumber() {

        return number;

    }

    public void setNumber(int number) {

        this.number = number;

    }

    public long getTotalElements() {

        return totalElements;

    }

    public void setTotalElements(long totalElements) {

        this.totalElements = totalElements;

    }

    public int getSize() {

        return size;

    }

    public void setSize(int size) {

        this.size = size;

    }

    public int getNumberOfElements() {

        return numberOfElements;

    }

    public void setNumberOfElements(int numberOfElements) {

        this.numberOfElements = numberOfElements;

    }

    public List<T> getContent() {

        return content;

    }

    public void setContent(List<T> content) {

        this.content = content;

    }

    public boolean isHasContent() {

        return isHasContent;

    }

    public void setHasContent(boolean isHasContent) {

        this.isHasContent = isHasContent;

    }

    public boolean isFirst() {

        return first;

    }

    public void setFirst(boolean first) {

        this.first = first;

    }

    public boolean isLast() {

        return last;

    }

    public void setLast(boolean last) {

        this.last = last;

    }

    public boolean isHasNext() {

        return isHasNext;

    }

    public void setHasNext(boolean isHasNext) {

        this.isHasNext = isHasNext;

    }

    public boolean isHasPrevious() {

        return isHasPrevious;

    }

    public void setHasPrevious(boolean isHasPrevious) {

        this.isHasPrevious = isHasPrevious;

    }

    public int[] getNavigatepageNums() {

        return navigatepageNums;

    }

    public void setNavigatepageNums(int[] navigatepageNums) {

        this.navigatepageNums = navigatepageNums;

    }

}

步骤 12 : CategoryService

增加 Service接口。 注意: list 返回的是 Page4Navigator 而不是 Page 类型。

package com.how2java.springboot.service;

import org.springframework.data.domain.Pageable;

import com.how2java.springboot.pojo.Category;

import com.how2java.springboot.util.Page4Navigator;

public interface CategoryService {

    public Page4Navigator<Category> list(Pageable pageable);

     

    public void save(Category category);

     

    public void delete(int id);

     

    public Category get(int id);

}

步骤 13 : CategoryServiceImpl

实现类CategoryServiceImp 做了一下工作:
1. 实现 CategoryService 接口,提供 crud

2. 在相应方法实现的时候,都是通过调用 dao 实现的

3. CacheConfig,表示 分类数据在 redis 中都放在 category 这个分组里。

@CacheConfig(cacheNames="category")



4. list方法讲解:
先说注解

@Cacheable(key="'category '+#p0.offset + '-' + #p0.pageSize ")


假如是第一页,即offset=0,pageSize=5,那么会创建一个 key: "category 0-5"
首先根据这个key 到 redis中查询数据。 第一次是不会有数据的,那么就会从数据库中取到这5条数据,然后以这个 key: "category 0-5" 保存到 redis 数据库中。
下一次再次访问的时候,根据这个key,就可以从 redis 里取到数据了。

5. get 方法讲解
先说注解:

@Cacheable(key="'category '+ #p0")


假如是获取id=71的数据,那么
就会以 key= "category 71" 到reids中去获取,如果没有就会从数据库中拿到,然后再以 key= "category 71" 这个值存放到 redis 当中。
下一次再次访问的时候,根据这个key,就可以从 redis 里取到数据了。

6. add 方法讲解
先说注解:

    @CacheEvict(allEntries=true)

//  @CachePut(key="'category '+ #p0")


可以看到,本来有个 CachePut,但是被注释掉了。 按理说,本来是应该用这个的。 这样会到在,在增加数据之后,就会在Redis 中以 key= "category 71" 缓存一条数据。 但是为什么被注释掉不用呢?
因为加入这样做了,那么 list 对应的数据,在缓存在对应的数据,并没有发生变化呀? 因为 list 对应的数据是这样的 key: "category 0-5"。 如果用这种方式,就会导致数据不同步,即,虽然增加了,并且也增加到缓存中了,但是因为 key 不一样,通过查询拿到的数据,是不会包含新的这一条的
所以,才会使用CacheEvict 这个注解,这个注解就表示清除掉缓存。 allEntries= true 是表示清除掉 category 分组 下所有的keys. 注意看截图,里面有一个 category~keys ,里面就表明了都有哪些 keys,都会被清除掉。
假如这个时候,还有一个分组 cacheNames="product", 那么它下面对应的缓存,都是不会被影响到的。 这样就保证了,只清楚当前分组下的缓存,而不是清除 redis 所有的数据了。

7. delete 方法讲解
先说注解:

    @CacheEvict(allEntries=true)

//  @CacheEvict(key="'category '+ #p0")


这个道理和 add 是一样的,仅仅删除 key= "category 71" ,没有什么意义, key: "category 0-5" 里面的数据没有影响呀。 所以还是通过 CacheEvict删除掉所有的缓存就好了。

CategoryServiceImpl

package com.how2java.springboot.service.impl;

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.stereotype.Service;

import com.how2java.springboot.dao.CategoryDAO;

import com.how2java.springboot.pojo.Category;

import com.how2java.springboot.service.CategoryService;

import com.how2java.springboot.util.Page4Navigator;

@Service

@CacheConfig(cacheNames="category")

public class CategoryServiceImpl implements CategoryService{

    @Autowired CategoryDAO categoryDAO;

     

    @Override

    @Cacheable(key="'category '+#p0.offset + '-' + #p0.pageSize ")

    public Page4Navigator<Category> list(Pageable pageable) {

        Page<Category> pageFromJPA=  categoryDAO.findAll(pageable);

        Page4Navigator<Category> page = new Page4Navigator<>(pageFromJPA,5);

        return page;

    }

    @Override

    @Cacheable(key="'category '+ #p0")

    public Category get(int id) {

        Category c =categoryDAO.findOne(id);

        return c;

    }  

     

    @Override

    @CacheEvict(allEntries=true)

//  @CachePut(key="'category '+ #p0")

    public void save(Category category) {

        // TODO Auto-generated method stub

        categoryDAO.save(category);

    }

     

    @Override

    @CacheEvict(allEntries=true)

//  @CacheEvict(key="'category '+ #p0")

    public void delete(int id) {

        // TODO Auto-generated method stub

        categoryDAO.delete(id);

    }

}

步骤 14 : CategoryController

由原来直接从 dao 获取,变为从 Service 获取了

package com.how2java.springboot.web;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.data.domain.PageRequest;

import org.springframework.data.domain.Pageable;

import org.springframework.data.domain.Sort;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;

import com.how2java.springboot.pojo.Category;

import com.how2java.springboot.service.CategoryService;

import com.how2java.springboot.util.Page4Navigator;

  

@Controller

public class CategoryController {

    @Autowired CategoryService categoryService;

     

    @RequestMapping("/listCategory")

     

    public String listCategory(Model m,@RequestParam(value = "start", defaultValue = "0"int start,@RequestParam(value = "size", defaultValue = "5"int size) throws Exception {

        start = start<0?0:start;

        Sort sort = new Sort(Sort.Direction.DESC, "id");

        Pageable pageable = new PageRequest(start, size, sort);

        Page4Navigator<Category> page =categoryService.list(pageable);

        m.addAttribute("page", page);

        return "listCategory";

    }

    @RequestMapping("/addCategory")

    public String addCategory(Category c) throws Exception {

        categoryService.save(c);

        return "redirect:listCategory";

    }

    @RequestMapping("/deleteCategory")

    public String deleteCategory(Category c) throws Exception {

        categoryService.delete(c.getId());

        return "redirect:listCategory";

    }

    @RequestMapping("/updateCategory")

    public String updateCategory(Category c) throws Exception {

        categoryService.save(c);

        return "redirect:listCategory";

    }

    @RequestMapping("/editCategory")

    public String ediitCategory(int id,Model m) throws Exception {

        Category c= categoryService.get(id);

        m.addAttribute("c", c);

        return "editCategory";

    }

}


更多内容,点击了解: https://how2j.cn/k/springboot/springboot-redis/1789.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值