电脑里文件无故丢失 | 电脑里文件
590 2023-04-02 23:52:51
前面学习整合了mongodb和 Thymeleaf,这里来整合redis和mongodb。学习参考:http://gitbook.cn/gitchat/column/5acda6f6d7966c5ae1086f2b/topic/5acdaa62d7966c5ae108708a
这里需要用到mongodb,和redis,这两个都可以使用docker来启动redis和mongodb的容器,设置好用户名和密码启动容器就可以建立连接了,具体的操作略掉了,网上资料很多。mongodb容器可以采用之前博客建立的mongodb容器
1,创建一个springboot2的项目
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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.jack</groupId><artifactId>webflux_redis</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><name>webflux_redis</name><description>Demo project for Spring Boot</description><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.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-redis-reactive</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency><!-- Spring Boot 响应式 MongoDB 依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb-reactive</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>io.projectreactor</groupId><artifactId>reactor-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>
application.properties的配置如下:
## Redis 配置## Redis服务器地址spring.redis.host=xx.xx.xx.xx## Redis服务器连接端口spring.redis.port=6379## Redis服务器连接密码(默认为空)spring.redis.password=xxxxxxxx# 连接超时时间(毫秒),这里注意的是连接超时时间不能太少或者为 0,不然会引起异常 RedisCommandTimeoutException: Command timed outspring.redis.timeout=5000
#mongodb的配置spring.data.mongodb.host=192.168.0.104spring.data.mongodb.database=adminspring.data.mongodb.port=27017spring.data.mongodb.username=adminspring.data.mongodb.password=admin
2,实体类代码如下:
package com.jack.webflux_redis.domain;import lombok.Data;import org.springframework.data.annotation.Id;import java.io.Serializable;/** * create by jack 2018/5/13 * City 必须实现序列化,因为需要将对象序列化后存储到 Redis。如果没实现 Serializable, * 会引出异常:java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable * payload but received an object of type。 如果不是用默认的序列化,需要自定义序列化实现,只要实现 RedisSerializer 接口去实现即可, 然后在使用 RedisTemplate.setValueSerializer 方法去设置你实现的序列化实现,支持 JSON、XML 等。 */@Datapublic class City implements Serializable { private static final long serialVersionUID = -1L; /** * 城市编号 * @Id 注解标记对应库表的主键或者唯一标识符。因为这个是我们的 DO,数据访问对象一一映射到数据存储。 */ @Id private Long id; /** * 省份编号 */ private Long provinceId; /** * 城市名称 */ private String cityName; /** * 描述 */ private String description;}
3,redis的测试代码如下:
redis的测试controller如下,此代码不具有reactive特性:
package com.jack.webflux_redis.controller;import com.jack.webflux_redis.domain.City;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.ValueOperations;import org.springframework.web.bind.annotation.*;import reactor.core.publisher.Mono;import java.util.concurrent.TimeUnit;/** * create by jack 2018/5/13 * * 使用 @Autowired 注入 RedisTemplate 对象,这个对象和 Spring 的 JdbcTemplate 功能十分相似,RedisTemplate 封装了 RedisConnection,具有连接管理、序列化和各个操作等,还有针对 String 的支持对象 StringRedisTemplate。 删除 Redis 某对象,直接通过 key 值调用 delete(key)。 Redis 操作视图接口类用的是 ValueOperations,对应的是 Redis String/Value 操作,get 是获取数据;set 是插入数据,可以设置失效时间,这里设置的失效时间是 60 s。 还有其他的操作视图,ListOperations、SetOperations、ZSetOperations 和 HashOperations。 */@RestController@RequestMapping(value = "/city")public class CityWebFluxController { /** * RedisTemplate 实现操作 Redis,但操作是同步的,不是 Reactive 的 */ @Autowired private RedisTemplate redisTemplate; @GetMapping(value = "/{id}") public Mono<City> findCityById(@PathVariable("id") Long id) { String key = "city_" + id; ValueOperations<String, City> operations = redisTemplate.opsForValue(); boolean hasKey = redisTemplate.hasKey(key); City city = operations.get(key); if (!hasKey) { return Mono.create(monoSink -> monoSink.success(null)); } return Mono.create(monoSink -> monoSink.success(city)); } @PostMapping() public Mono<City> saveCity(@RequestBody City city) { String key = "city_" + city.getId(); ValueOperations<String, City> operations = redisTemplate.opsForValue(); operations.set(key, city, 60, TimeUnit.SECONDS); return Mono.create(monoSink -> monoSink.success(city)); } @DeleteMapping(value = "/{id}") public Mono<Long> deleteCity(@PathVariable("id") Long id) { String key = "city_" + id; boolean hasKey = redisTemplate.hasKey(key); if (hasKey) { redisTemplate.delete(key); } return Mono.create(monoSink -> monoSink.success(id)); }}运行程序打开postman,添加一个城市信息,
通过redis获取一个城市代码如下:
4,添加一个reactive的redis操作
上面的redis操作不具有reactive的特性,下面是具有reactive特性的redis操作,代码如下:
package com.jack.webflux_redis.controller;import com.jack.webflux_redis.domain.City;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.ReactiveRedisTemplate;import org.springframework.data.redis.core.ReactiveValueOperations;import org.springframework.web.bind.annotation.*;import reactor.core.publisher.Mono;/** * create by jack 2018/5/13 */@RestController@RequestMapping(value = "/city2")public class CityWebFluxReactiveController { /** * * 持 Reactive 的操作类为 ReactiveRedisTemplate * @Autowired 注入 ReactiveRedisTemplate 对象。 ReactiveValueOperations 是 String(或 value)的操作视图, 操作视图还有 ReactiveHashOperations、ReactiveListOperations、ReactiveSetOperations 和 ReactiveZSetOperations 等。 不一样的是,操作视图 set 方法是操作 City 对象,但可以 get 回 Mono 或者 Flux 对象 */ @Autowired private ReactiveRedisTemplate reactiveRedisTemplate; @GetMapping(value = "/{id}") public Mono<City> findCityById(@PathVariable("id") Long id) { String key = "city_" + id; ReactiveValueOperations<String, City> operations = reactiveRedisTemplate.opsForValue(); Mono<City> city = operations.get(key); return city; } @PostMapping public Mono<City> saveCity(@RequestBody City city) { String key = "city_" + city.getId(); ReactiveValueOperations<String, City> operations = reactiveRedisTemplate.opsForValue(); return operations.getAndSet(key, city); } @DeleteMapping(value = "/{id}") public Mono<Long> deleteCity(@PathVariable("id") Long id) { String key = "city_" + id; return reactiveRedisTemplate.delete(key); }}
测试结果和上面的redis测试结果一样,只是具有reactive的特性,是异步非阻塞的。
5,整合redis和mongodb
dao层的代码如下:
package com.jack.webflux_redis.dao;import com.jack.webflux_redis.domain.City;import org.springframework.data.mongodb.repository.ReactiveMongoRepository;import org.springframework.stereotype.Repository;/** * create by jack 2018/5/12 */@Repositorypublic interface CityRepository extends ReactiveMongoRepository<City, Long> {}
新增一个handler,代码如下:
package com.jack.webflux_redis.handler;import com.jack.webflux_redis.dao.CityRepository;import com.jack.webflux_redis.domain.City;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.ValueOperations;import org.springframework.stereotype.Component;import reactor.core.publisher.Flux;import reactor.core.publisher.Mono;/** * create by jack 2018/5/13 */@Componentpublic class CityHandler { private static final Logger LOGGER = LoggerFactory.getLogger(CityHandler.class); @Autowired private RedisTemplate redisTemplate; private final CityRepository cityRepository; @Autowired public CityHandler(CityRepository cityRepository) { this.cityRepository = cityRepository; } public Mono<City> save(City city) { return cityRepository.save(city); } /** * 如果缓存存在,从缓存中获取城市信息; 如果缓存不存在,从 DB 中获取城市信息,然后插入缓存 * @param id * @return */ public Mono<City> findCityById(Long id) { // 从缓存中获取城市信息 String key = "city_" + id; ValueOperations<String, City> operations = redisTemplate.opsForValue(); // 缓存存在 boolean hasKey = redisTemplate.hasKey(key); if (hasKey) { City city = operations.get(key); LOGGER.info("CityHandler.findCityById() : 从缓存中获取了城市 >> " + city.toString()); return Mono.create(cityMonoSink -> cityMonoSink.success(city)); } // 从 MongoDB 中获取城市信息 Mono<City> cityMono = cityRepository.findById(id); if (cityMono == null) return cityMono; // 插入缓存 cityMono.subscribe(cityObj -> { operations.set(key, cityObj); LOGGER.info("CityHandler.findCityById() : 城市插入缓存 >> " + cityObj.toString()); }); return cityMono; } public Flux<City> findAllCity() { return cityRepository.findAll().cache(); } public Mono<City> modifyCity(City city) { Mono<City> cityMono = cityRepository.save(city); // 缓存存在,删除缓存 String key = "city_" + city.getId(); boolean hasKey = redisTemplate.hasKey(key); if (hasKey) { redisTemplate.delete(key); LOGGER.info("CityHandler.modifyCity() : 从缓存中删除城市 ID >> " + city.getId()); } return cityMono; } /** * 如果缓存存在,删除; 如果缓存不存在,不操作 * @param id * @return */ public Mono<Long> deleteCity(Long id) { cityRepository.deleteById(id); // 缓存存在,删除缓存 String key = "city_" + id; boolean hasKey = redisTemplate.hasKey(key); if (hasKey) { redisTemplate.delete(key); LOGGER.info("CityHandler.deleteCity() : 从缓存中删除城市 ID >> " + id); } return Mono.create(cityMonoSink -> cityMonoSink.success(id)); }}
新增一个controller,代码如下:
package com.jack.webflux_redis.controller;import com.jack.webflux_redis.domain.City;import com.jack.webflux_redis.handler.CityHandler;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;import reactor.core.publisher.Flux;import reactor.core.publisher.Mono;/** * create by jack 2018/5/13 */@RestController@RequestMapping(value = "/city3")public class CityWebFluxMongodbController { @Autowired private CityHandler cityHandler; @GetMapping(value = "/{id}") public Mono<City> findCityById(@PathVariable("id") Long id) { return cityHandler.findCityById(id); } @GetMapping() public Flux<City> findAllCity() { return cityHandler.findAllCity(); } @PostMapping() public Mono<City> saveCity(@RequestBody City city) { return cityHandler.save(city); } @PutMapping() public Mono<City> modifyCity(@RequestBody City city) { return cityHandler.modifyCity(city); } @DeleteMapping(value = "/{id}") public Mono<Long> deleteCity(@PathVariable("id") Long id) { return cityHandler.deleteCity(id); }}运行测试,http://127.0.0.1:8080/city3
新增一个城市
查询一个城市:http://127.0.0.1:8080/city3/3
后台输出
2018-05-13 10:52:40.283 INFO 8444 --- [ntLoopGroup-4-3] c.j.webflux_redis.handler.CityHandler : CityHandler.findCityById() : 城市插入缓存 >> City(id=3, provinceId=6410, cityName=海南, description=海南岛是一个历史不长、但风景优美的沿海岛屿,为中国第二大岛)
进行第二次查询,直接从缓存获取数据:
2018-05-13 10:54:30.253 INFO 8444 --- [ctor-http-nio-2] c.j.webflux_redis.handler.CityHandler : CityHandler.findCityById() : 从缓存中获取了城市 >> City(id=3, provinceId=6410, cityName=海南, description=海南岛是一个历史不长、但风景优美的沿海岛屿,为中国第二大岛)源码地址: https://github.com/wj903829182/springcloud5/tree/master/webflux_redis
总结:上面演示了webflux整合redis和mongodb的案例,代码都十分的简单和之前的webflux的CRUD差不多,只是采用了数据存储的方式,引入了nosql数据库mongodb和内存数据库redis。