1 Springboot中使用redis,自动缓存、更新、删除

时间:2023-01-15 03:47:37

第一篇记录一下在springboot中,redis的基础用法,自动缓存新增的数据,自动修改及删除。

在本机安装好mysql和redis。新建一个springboot的web项目,在新建项目时勾选redis,mysql。

pom文件如下:

<?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.tianyalei</groupId>
<artifactId>demo0</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>demo0</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.18</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>


</project>

数据库连接池用的druid,里面使用了spring-boot-starter-data-redis,有这一个依赖就够了,系统就能识别并应用redis了。dao工具用的jpa,默认集成了hibernate。

下面配置一下application.yml。如下:

spring:
jpa:
database: mysql
show-sql: true
hibernate:
ddl-auto: update
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/tx2
username: root
password:

配置一下show-sql为true,目的是看看查表时的缓存效果。至于redis的ip,端口什么的都不用配,系统有个默认值,等会看看就知道了。 

创建个java bean。

package com.tianyalei.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.io.Serializable;

/**
* Created by wuwf on 17/4/21.
*/
@Entity
public class Post implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;

private String content;

private Integer weight;

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

public Integer getWeight() {
return weight;
}

public void setWeight(Integer weight) {
this.weight = weight;
}
}

创建个repository

package com.tianyalei.repository;

import com.tianyalei.domain.Post;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.transaction.annotation.Transactional;

/**
* Created by wuwf on 17/4/20.
*/
@CacheConfig(cacheNames = "post")
public interface PostRepository extends PagingAndSortingRepository<Post, Integer> {
@Cacheable(key = "#p0")
Post findById(int id);

/**
* 新增或修改时
*/
@CachePut(key = "#p0.id")
@Override
Post save(Post post);

@Transactional
@Modifying
int deleteById(int id);
}
这个里面有个CacheConfig,配置了cacheNames。

我在findById方法时加了个@Cacheable(key= "#p0"),#p0代表第一个参数,也就是id。这句话加上之后,当你在调用findById时,就会先从redis的post缓存对象里去查询key等于传过来的id的值。如果没有,就去查表。

在save方法上加了个CachePut,代表往缓存里添加值,key为参数post的id属性,这样当我们save一个Post对象时,redis就会新增一个以id为key的Post对象;如果是update操作,那同样,redis会覆盖id相同的Post对象的值,也完成一次更新。更多标签,请看http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html#cache-spel-context

这样,在对post的新增和修改时都会自动缓存到redis里。

下面来验证一下。

加个service

package com.tianyalei.service;

import com.tianyalei.domain.Post;
import com.tianyalei.repository.PostRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


/**
* Created by wuwf on 17/4/20.
*/
@Service
public class PostService {
@Autowired
private PostRepository postRepository;

public Post findById(int id) {
return postRepository.findById(id);
}

public Post save(Post post) {
return postRepository.save(post);
}

public int delete(int id) {
return postRepository.deleteById(id);
}
}
来个controller

package com.tianyalei.controller;

import com.tianyalei.domain.Post;
import com.tianyalei.service.PostService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


/**
* Created by wuwf on 17/4/20.
*/
@RestController
public class PostController {
@Autowired
private PostService postService;

@RequestMapping("/query/{id}")
public Object query(@PathVariable int id) {
return postService.findById(id);
}

@RequestMapping("/save")
public Object save(@ModelAttribute Post post) {
return postService.save(post);
}

@RequestMapping("/delete/{id}")
public Object delete(@PathVariable int id) {
return postService.delete(id);
}

@RequestMapping("/queryPage")
public Object query(String name, int pageNum, int count) {
//根据weight倒序分页查询
// Pageable pageable = new PageRequest(pageNum, count, Sort.Direction.DESC, "weight");
// return userRepository.findByName(name, pageable);
return null;
}
}
然后启动Application,在启动之前需要加上@EnableCaching注解,缓存才能正常工作。

package com.tianyalei;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching
public class Demo0Application {

public static void main(String[] args) {
SpringApplication.run(Demo0Application.class, args);
}
}
启动后访问 http://localhost:8080/save?content=1&weight=1

这样就添加了一条记录,控制台有一个insert语句。
1 Springboot中使用redis,自动缓存、更新、删除

然后访问查询,http://localhost:8080/query/1

会发现查询到了id为1的这条记录,并且控制台没有走select查询语句,也就是根本没访问数据库,直接从redis缓存拿的值。

下面做一个更新操作,看看是否会同步到redis里。http://localhost:8080/save?content=1&weight=2&id=1

把weight改为2,访问地址看看结果。

控制台打印了两条语句

Hibernate: select post0_.id as id1_0_0_, post0_.content as content2_0_0_, post0_.weight as weight3_0_0_ from post post0_ where post0_.id=?
Hibernate: update post set content=?, weight=? where id=?

说明数据已经被更新了。然后再查询http://localhost:8080/query/1

发现查到的数据已经改变,并且控制台没有走select语句,说明在update时,redis已经更新了。

下面做删除操作,可以直接在数据库里删这条记录,或者通过浏览器访问来删除。http://localhost:8080/delete/1

控制台走了删除delete语句。再访问查询地址。发现依旧能查到这条记录,也就是db的删除成功了,但redis并没有删除。

那么怎么在db删除时,也删除redis的相关记录呢?

用CacheEvict

@CacheConfig(cacheNames = "post")
public interface PostRepository extends PagingAndSortingRepository<Post, Integer> {
@Cacheable(key = "#p0")
Post findById(int id);

/**
* 新增或修改时
*/
@CachePut(key = "#p0.id")
@Override
Post save(Post post);

@Transactional
@Modifying
@CacheEvict(key = "#p0")
int deleteById(int id);
}
加上这个标签后,再走deleteById方法时,就会删除掉key为id的redis记录了。可以重启试试,访问http://localhost:8080/delete/1

然后再查询就会发现id为1的值已经查不到了。

这样我们就完成了一个最简单的整合redis的demo。包括了对单个对象的增删改查的缓存。

那么下面来讲几个疑问:

1.为什么不用配置redis的地址,port,密码什么的?

上面的那些默认的对redis的操作,来源于Springboot里整合的RedisTemplate,template里会默认使用一个JedisConnectionFactory来做默认的连接属性配置。

1 Springboot中使用redis,自动缓存、更新、删除


这里面已经对jedis的连接地址和jedisPool做了初始化操作了,都是默认值。系统就会使用这些默认值来操作redis。

后面我们会对Connection进行自定义,设置value的序列化方式,还有修改连接地址,那时就会使用自定义的配置了。

2.能否用上面的方法来存储集合?譬如所有的Post集合,当新增时集合也随之改变?

不行的,假如给List<Post> findAll做了个缓存,那下次查询时确实不用查表了,但是当你新增、修改、删除任何一个对象时,这个缓存的集合都是不变的。

除非你在所有的能修改对象的地方,都加上CacheEvict,key为集合的key,这样任何修改,都是删除整个集合对象的缓存,下次再查时才能缓存起来。而大部分时候,集合对象都是在不停变化的,除了一些不变的如城市列表之类的,其他的都不适合用这种缓存方式。会导致频繁创建大对象,而且大部分时候也不需要查整个集合,而是分页。

3.怎么用redis来做集合查询,分页查询,甚至于条件分页查询?

这个也是问题2的延续,后面一篇会讲。