WebFlux04 SpringBootWebFlux集成MongoDB之Windows版本、WebFlux实现CRUD、WebFlux实现JPA、参数校验
1 下载并安装MongoDB
1.1 MongoDB官网
1.2 下载
solutions -> download center
1.3 安装
双击进入安装即可
1.3.1 安装时常见bug01
1.3.2 bug01解决办法
1.4 启动mongodb
技巧01:需要在同安装目录同一级别创建一个data目录来存放数据
技巧02:将下下面的命令存储成一个 bat 文件,下次启动时双击即可
- C:\tool\mongoDB\bin\mongod --dbpath C:\tool\data --smallfiles
1.5 mongodb正常启动后的控制台信息
1.6 启动MongoDB客户端
双击mongoDB安装目录下 -> bin -> mongo.exe
1.6.1 常用命令
show databases -> 显示数据库
use 数据库名称 -> 更换当前数据库
show tables -> 查看当前数据库中的数据表
db.表名.find() -> 查看某个表中的所有数据
db.表名.find().pretty() -> 查看某个表中的所有数据并进行格式化输出
2 SpringBootWebFlux集成MongoDB
2.1 创建一个项目
引入相关依赖:webflux、mongodb、devtool、lombok
- <?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>cn.xiangxu</groupId>
- <artifactId>webflux_demo</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <packaging>jar</packaging>
- <name>webflux_demo</name>
- <description>Demo project for Spring Boot</description>
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>2.0.3.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-webflux</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-devtools</artifactId>
- <!--<scope>runtime</scope>-->
- <optional>true</optional>
- </dependency>
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- <optional>true</optional>
- </dependency>
- <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-mongodb-reactive -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
- </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>
- <dependency>
- <groupId>org.junit.jupiter</groupId>
- <artifactId>junit-jupiter-api</artifactId>
- </dependency>
- </dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- <configuration>
- <fork>true</fork>
- </configuration>
- </plugin>
- </plugins>
- </build>
- </project>
pom.xml
2.2 启动类
在启动类上添加 @EnableReactiveMongoRepositories 注解来开启mongodb相关的配置
- package cn.xiangxu.webflux_demo;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories;
- @SpringBootApplication
- @EnableReactiveMongoRepositories
- public class WebfluxDemoApplication {
- public static void main(String[] args) {
- SpringApplication.run(WebfluxDemoApplication.class, args);
- }
- }
2.3 实体类
@Document(collection = "user") 目的时定义在mongodb中的表名,相当于JPA中的@Table注解
技巧01:在mongodb中的主键一般都是String类型的
- package cn.xiangxu.webflux_demo.domain;
- import lombok.Data;
- import org.springframework.data.annotation.Id;
- import org.springframework.data.mongodb.core.mapping.Document;
- /**
- * @author 王杨帅
- * @create 2018-06-27 8:42
- * @desc
- **/
- @Document(collection = "user")
- @Data
- public class User {
- @Id
- private String id;
- private String name;
- private int age;
- }
User.java
2.4 持久层
只需要继承 ReactiveMongoRepository 接口就行啦,和JPA差不多
- package cn.xiangxu.webflux_demo.repository;
- import cn.xiangxu.webflux_demo.domain.User;
- import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
- import org.springframework.stereotype.Repository;
- /**
- * @author 王杨帅
- * @create 2018-06-27 8:44
- * @desc
- **/
- @Repository
- public interface UserRepository extends ReactiveMongoRepository<User, String> {
- }
UserRepository.java
2.5 控制层
技巧01:有两种返回方式,一种是把所有数据一次性返回,另一种是像流一样的进行返回
- package cn.xiangxu.webflux_demo.web;
- import cn.xiangxu.webflux_demo.domain.User;
- import cn.xiangxu.webflux_demo.repository.UserRepository;
- import org.springframework.http.MediaType;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
- import reactor.core.publisher.Flux;
- /**
- * @author 王杨帅
- * @create 2018-06-27 8:45
- * @desc
- **/
- @RestController
- @RequestMapping(value = "/user")
- public class UserController {
- private final UserRepository userRepository;
- /**
- * 利用构造器注入持久层对象
- * @param userRepository
- */
- public UserController(UserRepository userRepository) {
- this.userRepository = userRepository;
- }
- /**
- * 一次性返回
- * @return
- */
- @GetMapping(value = "/")
- public Flux<User> getAll() {
- return userRepository.findAll();
- }
- /**
- * 流式返回
- * @return
- */
- @GetMapping(value = "/stream/all", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
- public Flux<User> streamGetAll() {
- return userRepository.findAll();
- }
- }
UserController.java
2.6 配置文件
前提:安装好mongodb并启动
- spring.data.mongodb.uri=mongodb://localhost:27017/webflux
2.7 启动并测试
技巧01:利用postman进行测试
3 WebFlux实现CRUD
准备:SpringBootWebFlux项目搭建以及SpringBootWebFlux集成MongoDB请参见上面的
3.1 新增
ReactiveCrudRepository 接口中的 save 方法可以实现更新和新增操作
技巧01:利用save方法进行更新操作时,如果接收到的ID在mongodb中没有对应的记录就会执行新增操作,而且新增数据的ID就是传过来的ID信息
技巧02:利用save方法进行新增操作时,不需要前端传ID信息,mongodb会自动根据实体类生成ID信息;如果传了ID信息就会变成更新操作了
- <S extends T> Mono<S> save(S var1);
- /**
- * 新增用户
- * @param user
- * @return
- */
- @PostMapping
- public Mono<User> createUser(@RequestBody User user
- ) {
- return userRepository.save(user);
- /**
- * Note
- * 1 save 可以修改和新增,如果有ID就是修改【该ID存在,如果该ID不存在直接新增】,没有就是新增
- */
- }
3.2 删除
需求:根据前端传过来的ID信息删除用户信息,如果删除成功就返回200状态码,删除失败就返回404状态码
坑01:ReactiveCrudRepository接口提供的一系列delete方法都没有返回自,所以不确定是否已经删除成功
- Mono<Void> deleteById(ID var1);
- Mono<Void> deleteById(Publisher<ID> var1);
- Mono<Void> delete(T var1);
- Mono<Void> deleteAll(Iterable<? extends T> var1);
- Mono<Void> deleteAll(Publisher<? extends T> var1);
- Mono<Void> deleteAll();
解坑01:根据前端I传过来的ID查询信息 -> 查到就进行删除操作 -> 返回200状态码
-> 查不到就直接返回404状态码
技巧01:map和flatMap的使用时机
当要操作数据并返回Mono时使用flatMap; 如果不操作数据,仅仅转换数据时使用Map
技巧02:如果一个stream没有返回值但方法有要求有返回值时,可以利用then来返回数据
- /**
- * 删除用户
- * 需求:删除成功后返回200状态码,删除失败就返回404状态码
- * @param id
- * @return
- */
- @DeleteMapping(value = "/{id}")
- public Mono<ResponseEntity<Void>> deleteUser(@PathVariable("id") String id) {
- // userRepository.deleteById(id); // deleteById 方法没有返回值,我们无法知道是否删除成功
- return userRepository.findById(id)
- .flatMap( // 根据ID找到数据进行删除操作,并返回200状态码
- user -> userRepository.delete(user)
- .then(Mono.just(new ResponseEntity<Void>(HttpStatus.OK)))
- )
- .defaultIfEmpty(new ResponseEntity<Void>(HttpStatus.NOT_FOUND)); // 如果没有查找到数据就返回404状态码
- /**
- * Note
- * 1 map和flatMap的使用时机
- * 当要操作数据并返回Mono时使用flatMap
- * 如果不操作数据,仅仅转换数据时使用Map
- * 2 如果一个stream没有返回值但方法有要求有返回值时,可以利用then来返回数据
- */
- }
3.3 更新
需求:根据前端传过来的ID和更新数据类进行更新操作;更新成功后返回200状态码和更新后的数据,更新失败后就返回404状态码
坑01:直接利用save方法进行更新操作时容易产生歧义,因为save方法可以进行更新和删除操作;当接收到对象没有id信息时就进行新增操作,如果有ID信息而且数据库有该ID对应的数据时就进行更新操作,如果有ID信息但是数据库中没有改ID对应的数据时也会进行新增操作
解坑01: 根据前端传过来的ID查询数据 -> 查到数据就进行更新操作 -> 返回200状态码和更新过后的数据
-> 没查到数据就返回404状态码
- /**
- * 修改数据
- * 修改成功返回200和修改成功后的数据,不存在时返回404
- * @param id 要修改的用户ID
- * @param user 修改数据
- * @return
- */
- @PutMapping(value = "/{id}")
- public Mono<ResponseEntity<User>> updateUser(
- @PathVariable("id") String id,
- @RequestBody User user
- ) {
- return userRepository.findById(id)
- .flatMap( // 操作数据
- u -> {
- u.setAge(user.getAge());
- u.setName(user.getName());
- return userRepository.save(u);
- }
- )
- .map(u -> new ResponseEntity<User>(u, HttpStatus.OK)) // 转化数据
- .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
- }
3.4 查询
需求:根据前端传过来的ID查询数据,如果查到就直接返回200状态码和查到的数据,如果不存在该ID对应的数据就直接返回404状态码
- /**
- * 根据ID查找用户
- * 存在时返回200和查到的数据,不存在时就返回404
- * @param id 用户ID
- * @return
- */
- @GetMapping(value = "/{id}")
- public Mono<ResponseEntity<User>> getById(@PathVariable("id") String id) {
- return userRepository.findById(id)
- .map(u -> new ResponseEntity<User>(u, HttpStatus.OK)) // 转化数据
- .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
- }
·3.5 代码汇总
- package cn.xiangxu.webflux_demo.web;
- import cn.xiangxu.webflux_demo.domain.User;
- import cn.xiangxu.webflux_demo.repository.UserRepository;
- import org.springframework.http.HttpStatus;
- import org.springframework.http.MediaType;
- import org.springframework.http.ResponseEntity;
- import org.springframework.web.bind.annotation.*;
- import reactor.core.publisher.Flux;
- import reactor.core.publisher.Mono;
- /**
- * @author 王杨帅
- * @create 2018-06-27 8:45
- * @desc
- **/
- @RestController
- @RequestMapping(value = "/user")
- public class UserController {
- private final UserRepository userRepository;
- /**
- * 利用构造器注入持久层对象
- * @param userRepository
- */
- public UserController(UserRepository userRepository) {
- this.userRepository = userRepository;
- }
- /**
- * 以数组形式一次性返回
- * @return
- */
- @GetMapping(value = "/")
- public Flux<User> getAll() {
- return userRepository.findAll();
- }
- /**
- * 以SSE形式流式返回
- * @return
- */
- @GetMapping(value = "/stream/all", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
- public Flux<User> streamGetAll() {
- return userRepository.findAll();
- }
- /**
- * 新增用户
- * @param user
- * @return
- */
- @PostMapping
- public Mono<User> createUser(@RequestBody User user
- ) {
- return userRepository.save(user);
- /**
- * Note
- * 1 save 可以修改和新增,如果有ID就是修改【该ID存在,如果该ID不存在直接新增】,没有就是新增
- */
- }
- /**
- * 删除用户
- * 需求:删除成功后返回200状态码,删除失败就返回404状态码
- * @param id
- * @return
- */
- @DeleteMapping(value = "/{id}")
- public Mono<ResponseEntity<Void>> deleteUser(@PathVariable("id") String id) {
- // userRepository.deleteById(id); // deleteById 方法没有返回值,我们无法知道是否删除成功
- return userRepository.findById(id)
- .flatMap( // 根据ID找到数据进行删除操作,并返回200状态码
- user -> userRepository.delete(user)
- .then(Mono.just(new ResponseEntity<Void>(HttpStatus.OK)))
- )
- .defaultIfEmpty(new ResponseEntity<Void>(HttpStatus.NOT_FOUND)); // 如果没有查找到数据就返回404状态码
- /**
- * Note
- * 1 map和flatMap的使用时机
- * 当要操作数据并返回Mono时使用flatMap
- * 如果不操作数据,仅仅转换数据时使用Map
- * 2 如果一个stream没有返回值但方法有要求有返回值时,可以利用then来返回数据
- */
- }
- /**
- * 修改数据
- * 修改成功返回200和修改成功后的数据,不存在时返回404
- * @param id 要修改的用户ID
- * @param user 修改数据
- * @return
- */
- @PutMapping(value = "/{id}")
- public Mono<ResponseEntity<User>> updateUser(
- @PathVariable("id") String id,
- @RequestBody User user
- ) {
- return userRepository.findById(id)
- .flatMap( // 操作数据
- u -> {
- u.setAge(user.getAge());
- u.setName(user.getName());
- return userRepository.save(u);
- }
- )
- .map(u -> new ResponseEntity<User>(u, HttpStatus.OK)) // 转化数据
- .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
- }
- /**
- * 根据ID查找用户
- * 存在时返回200和查到的数据,不存在时就返回404
- * @param id 用户ID
- * @return
- */
- @GetMapping(value = "/{id}")
- public Mono<ResponseEntity<User>> getById(@PathVariable("id") String id) {
- return userRepository.findById(id)
- .map(u -> new ResponseEntity<User>(u, HttpStatus.OK)) // 转化数据
- .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
- }
- /**
- * 根据年龄段查询:数组形式返回
- * @param start 最小年龄
- * @param end 最大年龄
- * @return
- */
- @GetMapping(value = "/age/{start}/{end}")
- public Flux<User> findByAge(
- @PathVariable("start") Integer start,
- @PathVariable("end") Integer end
- ) {
- return userRepository.findByAgeBetween(start, end);
- }
- /**
- * 根据年龄段查询:流式返回
- * @param start
- * @param end
- * @return
- */
- @GetMapping(value = "/stream/age/{start}/{end}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
- public Flux<User> streamFindByAge(
- @PathVariable("start") Integer start,
- @PathVariable("end") Integer end
- ) {
- return userRepository.findByAgeBetween(start, end);
- }
- /**
- * 查询年龄在20-30的用户
- * @return
- */
- @GetMapping(value = "/age/oldUser")
- public Flux<User> oldUser() {
- return userRepository.oldUser();
- }
- /**
- * 查询年龄在20-30的用户
- * @return
- */
- @GetMapping(value = "/stream/age/oldUser", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
- public Flux<User> streamFindByAge() {
- return userRepository.oldUser();
- }
- }
4 WebFlux实现JPA
ReactiveCrudRepository接口除了提供简单的CRUD操作外,还可以进行自定义数据操作方法,但是自定义方法的方法名有一定的要求;如果定义的方法名不满足 SpringData JPA 也可以利用 @Query 注解使用原生的注解进行实现
4.1 符合JPA规范的写法
4.1.1 持久层
- /**
- * 根据年龄段查询用户【PS: 不包括端点值】
- * @param start
- * @param end
- * @return
- */
- Flux<User> findByAgeBetween(Integer start, Integer end);
- package cn.xiangxu.webflux_demo.repository;
- import cn.xiangxu.webflux_demo.domain.User;
- import org.springframework.beans.factory.annotation.Qualifier;
- import org.springframework.data.mongodb.repository.Query;
- import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
- import org.springframework.stereotype.Repository;
- import reactor.core.publisher.Flux;
- /**
- * @author 王杨帅
- * @create 2018-06-27 8:44
- * @desc
- **/
- @Repository
- public interface UserRepository extends ReactiveMongoRepository<User, String> {
- /**
- * 根据年龄段查询用户【PS: 不包括端点值】
- * @param start
- * @param end
- * @return
- */
- Flux<User> findByAgeBetween(Integer start, Integer end);
- /**
- * 查询年龄在20-30的用户【PS: 包括端点值】
- * 利用MongoDB的 SQL 语句实现
- * @return
- */
- @Query("{'age':{'$gte':20, '$lte':30}}")
- Flux<User> oldUser();
- }
4.1.2 控制层
- /**
- * 根据年龄段查询:数组形式返回
- * @param start 最小年龄
- * @param end 最大年龄
- * @return
- */
- @GetMapping(value = "/age/{start}/{end}")
- public Flux<User> findByAge(
- @PathVariable("start") Integer start,
- @PathVariable("end") Integer end
- ) {
- return userRepository.findByAgeBetween(start, end);
- }
- /**
- * 根据年龄段查询:流式返回
- * @param start
- * @param end
- * @return
- */
- @GetMapping(value = "/stream/age/{start}/{end}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
- public Flux<User> streamFindByAge(
- @PathVariable("start") Integer start,
- @PathVariable("end") Integer end
- ) {
- return userRepository.findByAgeBetween(start, end);
- }
4.2 @Query的写法
4.2.1 持久层
- /**
- * 查询年龄在20-30的用户【PS: 包括端点值】
- * 利用MongoDB的 SQL 语句实现
- * @return
- */
- @Query("{'age':{'$gte':20, '$lte':30}}")
- Flux<User> oldUser();
- package cn.xiangxu.webflux_demo.repository;
- import cn.xiangxu.webflux_demo.domain.User;
- import org.springframework.beans.factory.annotation.Qualifier;
- import org.springframework.data.mongodb.repository.Query;
- import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
- import org.springframework.stereotype.Repository;
- import reactor.core.publisher.Flux;
- /**
- * @author 王杨帅
- * @create 2018-06-27 8:44
- * @desc
- **/
- @Repository
- public interface UserRepository extends ReactiveMongoRepository<User, String> {
- /**
- * 根据年龄段查询用户【PS: 不包括端点值】
- * @param start
- * @param end
- * @return
- */
- Flux<User> findByAgeBetween(Integer start, Integer end);
- /**
- * 查询年龄在20-30的用户【PS: 包括端点值】
- * 利用MongoDB的 SQL 语句实现
- * @return
- */
- @Query("{'age':{'$gte':20, '$lte':30}}")
- Flux<User> oldUser();
- }
4.2.2 控制层
- /**
- * 查询年龄在20-30的用户
- * @return
- */
- @GetMapping(value = "/age/oldUser")
- public Flux<User> oldUser() {
- return userRepository.oldUser();
- }
- /**
- * 查询年龄在20-30的用户
- * @return
- */
- @GetMapping(value = "/stream/age/oldUser", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
- public Flux<User> streamFindByAge() {
- return userRepository.oldUser();
- }
4.3 代码汇总
4.3.1 持久层
- package cn.xiangxu.webflux_demo.repository;
- import cn.xiangxu.webflux_demo.domain.User;
- import org.springframework.beans.factory.annotation.Qualifier;
- import org.springframework.data.mongodb.repository.Query;
- import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
- import org.springframework.stereotype.Repository;
- import reactor.core.publisher.Flux;
- /**
- * @author 王杨帅
- * @create 2018-06-27 8:44
- * @desc
- **/
- @Repository
- public interface UserRepository extends ReactiveMongoRepository<User, String> {
- /**
- * 根据年龄段查询用户【PS: 不包括端点值】
- * @param start
- * @param end
- * @return
- */
- Flux<User> findByAgeBetween(Integer start, Integer end);
- /**
- * 查询年龄在20-30的用户【PS: 包括端点值】
- * 利用MongoDB的 SQL 语句实现
- * @return
- */
- @Query("{'age':{'$gte':20, '$lte':30}}")
- Flux<User> oldUser();
- }
4.3.2 控制层
- package cn.xiangxu.webflux_demo.web;
- import cn.xiangxu.webflux_demo.domain.User;
- import cn.xiangxu.webflux_demo.repository.UserRepository;
- import org.springframework.http.HttpStatus;
- import org.springframework.http.MediaType;
- import org.springframework.http.ResponseEntity;
- import org.springframework.web.bind.annotation.*;
- import reactor.core.publisher.Flux;
- import reactor.core.publisher.Mono;
- /**
- * @author 王杨帅
- * @create 2018-06-27 8:45
- * @desc
- **/
- @RestController
- @RequestMapping(value = "/user")
- public class UserController {
- private final UserRepository userRepository;
- /**
- * 利用构造器注入持久层对象
- * @param userRepository
- */
- public UserController(UserRepository userRepository) {
- this.userRepository = userRepository;
- }
- /**
- * 以数组形式一次性返回
- * @return
- */
- @GetMapping(value = "/")
- public Flux<User> getAll() {
- return userRepository.findAll();
- }
- /**
- * 以SSE形式流式返回
- * @return
- */
- @GetMapping(value = "/stream/all", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
- public Flux<User> streamGetAll() {
- return userRepository.findAll();
- }
- /**
- * 新增用户
- * @param user
- * @return
- */
- @PostMapping
- public Mono<User> createUser(@RequestBody User user
- ) {
- return userRepository.save(user);
- /**
- * Note
- * 1 save 可以修改和新增,如果有ID就是修改【该ID存在,如果该ID不存在直接新增】,没有就是新增
- */
- }
- /**
- * 删除用户
- * 需求:删除成功后返回200状态码,删除失败就返回404状态码
- * @param id
- * @return
- */
- @DeleteMapping(value = "/{id}")
- public Mono<ResponseEntity<Void>> deleteUser(@PathVariable("id") String id) {
- // userRepository.deleteById(id); // deleteById 方法没有返回值,我们无法知道是否删除成功
- return userRepository.findById(id)
- .flatMap( // 根据ID找到数据进行删除操作,并返回200状态码
- user -> userRepository.delete(user)
- .then(Mono.just(new ResponseEntity<Void>(HttpStatus.OK)))
- )
- .defaultIfEmpty(new ResponseEntity<Void>(HttpStatus.NOT_FOUND)); // 如果没有查找到数据就返回404状态码
- /**
- * Note
- * 1 map和flatMap的使用时机
- * 当要操作数据并返回Mono时使用flatMap
- * 如果不操作数据,仅仅转换数据时使用Map
- * 2 如果一个stream没有返回值但方法有要求有返回值时,可以利用then来返回数据
- */
- }
- /**
- * 修改数据
- * 修改成功返回200和修改成功后的数据,不存在时返回404
- * @param id 要修改的用户ID
- * @param user 修改数据
- * @return
- */
- @PutMapping(value = "/{id}")
- public Mono<ResponseEntity<User>> updateUser(
- @PathVariable("id") String id,
- @RequestBody User user
- ) {
- return userRepository.findById(id)
- .flatMap( // 操作数据
- u -> {
- u.setAge(user.getAge());
- u.setName(user.getName());
- return userRepository.save(u);
- }
- )
- .map(u -> new ResponseEntity<User>(u, HttpStatus.OK)) // 转化数据
- .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
- }
- /**
- * 根据ID查找用户
- * 存在时返回200和查到的数据,不存在时就返回404
- * @param id 用户ID
- * @return
- */
- @GetMapping(value = "/{id}")
- public Mono<ResponseEntity<User>> getById(@PathVariable("id") String id) {
- return userRepository.findById(id)
- .map(u -> new ResponseEntity<User>(u, HttpStatus.OK)) // 转化数据
- .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
- }
- /**
- * 根据年龄段查询:数组形式返回
- * @param start 最小年龄
- * @param end 最大年龄
- * @return
- */
- @GetMapping(value = "/age/{start}/{end}")
- public Flux<User> findByAge(
- @PathVariable("start") Integer start,
- @PathVariable("end") Integer end
- ) {
- return userRepository.findByAgeBetween(start, end);
- }
- /**
- * 根据年龄段查询:流式返回
- * @param start
- * @param end
- * @return
- */
- @GetMapping(value = "/stream/age/{start}/{end}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
- public Flux<User> streamFindByAge(
- @PathVariable("start") Integer start,
- @PathVariable("end") Integer end
- ) {
- return userRepository.findByAgeBetween(start, end);
- }
- /**
- * 查询年龄在20-30的用户
- * @return
- */
- @GetMapping(value = "/age/oldUser")
- public Flux<User> oldUser() {
- return userRepository.oldUser();
- }
- /**
- * 查询年龄在20-30的用户
- * @return
- */
- @GetMapping(value = "/stream/age/oldUser", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
- public Flux<User> streamFindByAge() {
- return userRepository.oldUser();
- }
- }
5 参数校验
技巧01:参数校验和MVC模式相同,只需要在实体类的成员属性上添加相应的注解即可;然后在请求控制方法的参数上加上@Valid即可
5.1 实体类
- package cn.xiangxu.webflux_test.domain.domain_do;
- import lombok.Data;
- import org.hibernate.validator.constraints.Range;
- import org.springframework.data.annotation.Id;
- import org.springframework.data.mongodb.core.mapping.Document;
- import javax.validation.constraints.NotBlank;
- /**
- * @author 王杨帅
- * @create 2018-08-02 15:30
- * @desc 学生实体类
- **/
- @Document(collection = "student")
- @Data
- public class StudentDO {
- @Id
- private String id;
- @NotBlank
- private String name;
- private String address;
- @Range(min = 12, max = 50)
- private Integer age;
- }
StudentDO.java
5.2 持久层
- package cn.xiangxu.webflux_test.reposigory;
- import cn.xiangxu.webflux_test.domain.domain_do.StudentDO;
- import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
- import org.springframework.stereotype.Repository;
- /**
- * @author 王杨帅
- * @create 2018-08-02 15:31
- * @desc 学生持久层
- **/
- @Repository
- public interface StudentRepository extends ReactiveMongoRepository<StudentDO, String> {
- }
StudentRepository.java
5.3 控制层
坑01:在MVC模式时可以在控制方法上使用 BindingResult ,但是在 WebFlux 模式下不可以使用;只能通过创建切面进行异常捕获
技巧02:添加了@Valid注解后如果有参数不合法时抛出的异常是 WebExchangeBindException
- package cn.xiangxu.webflux_test.controller;
- import cn.xiangxu.webflux_test.domain.domain_do.StudentDO;
- import cn.xiangxu.webflux_test.reposigory.StudentRepository;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.http.HttpStatus;
- import org.springframework.http.ResponseEntity;
- import org.springframework.web.bind.annotation.*;
- import reactor.core.publisher.Flux;
- import reactor.core.publisher.Mono;
- import javax.validation.Valid;
- import static cn.xiangxu.webflux_test.util.CheckUtil.checkeName;
- /**
- * @author 王杨帅
- * @create 2018-08-02 15:44
- * @desc 学生控制层
- **/
- @RestController
- @RequestMapping(value = "/stu")
- @Slf4j
- public class StudentController {
- @Autowired
- private StudentRepository studentRepository;
- @GetMapping
- public Flux<StudentDO> findList() {
- return studentRepository.findAll();
- }
- @PostMapping
- public Mono<StudentDO> create(
- @Valid @RequestBody StudentDO studentDO) {
- System.out.println(studentDO);
- checkeName(studentDO.getName());
- return studentRepository.save(studentDO);
- }
- @PutMapping()
- public Mono<ResponseEntity<String>> update(
- @Valid @RequestBody StudentDO studentDO
- ) {
- log.info("前端传过来的数据为:" + studentDO);
- checkeName(studentDO.getName());
- return studentRepository.findById(studentDO.getId())
- .flatMap(student -> studentRepository.save(studentDO))
- .map(student -> new ResponseEntity<String>("更新成功", HttpStatus.OK))
- .defaultIfEmpty(new ResponseEntity<String>("ID不合法", HttpStatus.BAD_REQUEST));
- }
- @DeleteMapping(value = "/{id}")
- public Mono<ResponseEntity<Void>> delete(
- @PathVariable(value = "id") String id
- ) {
- log.info("从前端获取到的ID信息为:" + id);
- return studentRepository.findById(id)
- .flatMap(
- student -> {
- return studentRepository.deleteById(student.getId())
- .then(Mono.just(new ResponseEntity<Void>(HttpStatus.OK)));
- }
- )
- .defaultIfEmpty(new ResponseEntity<Void>(HttpStatus.NOT_FOUND));
- }
- @GetMapping(value = "/{id}")
- public Mono<ResponseEntity<StudentDO>> findById(
- @PathVariable("id") String id
- ) {
- log.info("从前端获取到的参数信息为:" + id);
- return studentRepository.findById(id)
- .map(student -> new ResponseEntity<StudentDO>(student, HttpStatus.OK))
- .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
- }
- }
StudentController.java
5.4 编写异常处理切面
- package cn.xiangxu.webflux_test.exception.handler;
- import cn.xiangxu.webflux_test.exception.CheckException;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
- import org.springframework.http.HttpStatus;
- import org.springframework.http.ResponseEntity;
- import org.springframework.web.bind.annotation.ControllerAdvice;
- import org.springframework.web.bind.annotation.ExceptionHandler;
- import org.springframework.web.bind.support.WebExchangeBindException;
- import java.util.Optional;
- /**
- * @author 王杨帅
- * @create 2018-08-02 16:31
- * @desc
- **/
- @ControllerAdvice
- @Slf4j
- public class CheckAdvice {
- @ExceptionHandler(WebExchangeBindException.class)
- public ResponseEntity handleWebExchangeBindException(WebExchangeBindException e) {
- log.error(e.getMessage());
- return new ResponseEntity<String>(toStr(e), HttpStatus.BAD_REQUEST);
- }
- @ExceptionHandler(CheckException.class)
- public ResponseEntity handleCheckException(CheckException e) {
- log.error(e.getMessage());
- return new ResponseEntity<String>(toStr(e), HttpStatus.BAD_REQUEST);
- }
- private String toStr(CheckException e) {
- return e.getFiledName() + " : " + e.getFiledValue();
- }
- /**
- * 把校验异常转化成字符串
- * @param e
- * @return
- */
- private String toStr(WebExchangeBindException e) {
- return e.getFieldErrors().stream()
- .map(error -> error.getField() + " : " + error.getDefaultMessage())
- .reduce("", (s1, s2) -> s1 + " \n " + s2);
- }
- }
CheckAdvice.java
5.5 自定义校验方法
自定义校验方法有两种方式,一种自定义一个参数校验注解,另外一种是自定义一个参数校验方法【PS: 本博文基于后者】
5.5.1 自定义异常
- package cn.xiangxu.webflux_test.exception;
- import lombok.Data;
- /**
- * @author 王杨帅
- * @create 2018-08-02 17:01
- * @desc 检查异常
- **/
- @Data
- public class CheckException extends RuntimeException {
- /**
- * 出错字段
- */
- private String filedName;
- /**
- * 出错值
- */
- private String filedValue;
- public CheckException(String message, String filedName, String filedValue) {
- super(message);
- this.filedName = filedName;
- this.filedValue = filedValue;
- }
- public CheckException(String filedName, String filedValue) {
- this.filedName = filedName;
- this.filedValue = filedValue;
- }
- }
CheckException.java
5.5.2 编写捕获自定义异常的切面
- package cn.xiangxu.webflux_test.exception.handler;
- import cn.xiangxu.webflux_test.exception.CheckException;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
- import org.springframework.http.HttpStatus;
- import org.springframework.http.ResponseEntity;
- import org.springframework.web.bind.annotation.ControllerAdvice;
- import org.springframework.web.bind.annotation.ExceptionHandler;
- import org.springframework.web.bind.support.WebExchangeBindException;
- import java.util.Optional;
- /**
- * @author 王杨帅
- * @create 2018-08-02 16:31
- * @desc
- **/
- @ControllerAdvice
- @Slf4j
- public class CheckAdvice {
- @ExceptionHandler(WebExchangeBindException.class)
- public ResponseEntity handleWebExchangeBindException(WebExchangeBindException e) {
- log.error(e.getMessage());
- return new ResponseEntity<String>(toStr(e), HttpStatus.BAD_REQUEST);
- }
- @ExceptionHandler(CheckException.class)
- public ResponseEntity handleCheckException(CheckException e) {
- log.error(e.getMessage());
- return new ResponseEntity<String>(toStr(e), HttpStatus.BAD_REQUEST);
- }
- private String toStr(CheckException e) {
- return e.getFiledName() + " : " + e.getFiledValue();
- }
- /**
- * 把校验异常转化成字符串
- * @param e
- * @return
- */
- private String toStr(WebExchangeBindException e) {
- return e.getFieldErrors().stream()
- .map(error -> error.getField() + " : " + error.getDefaultMessage())
- .reduce("", (s1, s2) -> s1 + " \n " + s2);
- }
- }
CheckAdvice.java
5 .5.3 自定义校验方法
- package cn.xiangxu.webflux_test.util;
- import cn.xiangxu.webflux_test.exception.CheckException;
- import java.util.Arrays;
- import java.util.stream.Stream;
- /**
- * @author 王杨帅
- * @create 2018-08-02 17:00
- * @desc 检查工具类
- **/
- public class CheckUtil {
- private static final String[] INVALID_NAMES = {"admin", "fury"};
- /**
- * 校验名字:不成功时抛出自定义异常
- * @param value
- */
- public static void checkeName(String value) {
- Stream.of(INVALID_NAMES)
- .filter(name -> name.equalsIgnoreCase(value))
- .findAny()
- .ifPresent(
- name -> {
- throw new CheckException("name", value);
- }
- );
- }
- }
CheckUtil.java
5.5.4 使用自定义参数校验方法
- package cn.xiangxu.webflux_test.controller;
- import cn.xiangxu.webflux_test.domain.domain_do.StudentDO;
- import cn.xiangxu.webflux_test.reposigory.StudentRepository;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.http.HttpStatus;
- import org.springframework.http.ResponseEntity;
- import org.springframework.validation.BindingResult;
- import org.springframework.web.bind.annotation.*;
- import reactor.core.publisher.Flux;
- import reactor.core.publisher.Mono;
- import javax.validation.Valid;
- import static cn.xiangxu.webflux_test.util.CheckUtil.checkeName;
- /**
- * @author 王杨帅
- * @create 2018-08-02 15:44
- * @desc 学生控制层
- **/
- @RestController
- @RequestMapping(value = "/stu")
- @Slf4j
- public class StudentController {
- @Autowired
- private StudentRepository studentRepository;
- @GetMapping
- public Flux<StudentDO> findList() {
- return studentRepository.findAll();
- }
- @PostMapping
- public Mono<StudentDO> create(
- @Valid @RequestBody StudentDO studentDO) {
- System.out.println(studentDO);
- checkeName(studentDO.getName());
- return studentRepository.save(studentDO);
- }
- @PutMapping()
- public Mono<ResponseEntity<String>> update(
- @Valid @RequestBody StudentDO studentDO
- ) {
- log.info("前端传过来的数据为:" + studentDO);
- checkeName(studentDO.getName());
- return studentRepository.findById(studentDO.getId())
- .flatMap(student -> studentRepository.save(studentDO))
- .map(student -> new ResponseEntity<String>("更新成功", HttpStatus.OK))
- .defaultIfEmpty(new ResponseEntity<String>("ID不合法", HttpStatus.BAD_REQUEST));
- }
- @DeleteMapping(value = "/{id}")
- public Mono<ResponseEntity<Void>> delete(
- @PathVariable(value = "id") String id
- ) {
- log.info("从前端获取到的ID信息为:" + id);
- return studentRepository.findById(id)
- .flatMap(
- student -> {
- return studentRepository.deleteById(student.getId())
- .then(Mono.just(new ResponseEntity<Void>(HttpStatus.OK)));
- }
- )
- .defaultIfEmpty(new ResponseEntity<Void>(HttpStatus.NOT_FOUND));
- }
- @GetMapping(value = "/{id}")
- public Mono<ResponseEntity<StudentDO>> findById(
- @PathVariable("id") String id
- ) {
- log.info("从前端获取到的参数信息为:" + id);
- return studentRepository.findById(id)
- .map(student -> new ResponseEntity<StudentDO>(student, HttpStatus.OK))
- .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
- }
- }
StudentController.java
6 SpringBootWebflux 整合 MongoDB实现CRUD参考代码
WebFlux04 SpringBootWebFlux集成MongoDB之Windows版本、WebFlux实现CRUD、WebFlux实现JPA、参数校验的更多相关文章
- spring集成mongodb jar包版本问题
在开发过程中,spring集成mongodb的jar包. 如果需要使用排序功能. spring-data-mongodb-1.4.1.RELEASE.jar 的版本为1.4.1,如果使用如下代码: Q ...
- Mongodb在Windows 7下的安装及配置
第一步 下载MongoDB: 下载mongodb的windows版本,有32位和64位版本,根据操作系统情况下载,下载地址:http://www.mongodb.org/downloads 解压缩至指 ...
- Mongodb在Windows下安装及配置 【转】
1.下载mongodb的windows版本,有32位和64位版本,根据系统情况下载,下载地址:http://www.mongodb.org/downloads 2.解压缩至E:/mongodb即可 3 ...
- Spring Boot 2 快速教程:WebFlux 集成 Mongodb(四)
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 这是泥瓦匠的第104篇原创 文章工程:* JDK 1.8* M ...
- Spring Boot WebFlux 集成 Mongodb 数据源操作
WebFlux 整合 Mongodb 前言 上一讲用 Map 数据结构内存式存储了数据.这样数据就不会持久化,本文我们用 MongoDB 来实现 WebFlux 对数据源的操作. 什么是 MongoD ...
- asp.net core集成MongoDB
0.目录 整体架构目录:ASP.NET Core分布式项目实战-目录 一.前言及MongoDB的介绍 最近在整合自己的框架,顺便把MongoDBD的最简单CRUD重构一下作为组件化集成到asp.net ...
- mongoDB在windows下安装与配置方案
首先在官网下载mongoDB的安装包: https://www.mongodb.org/downloads 百度云盘下载:http://pan.baidu.com/s/1slUSGYp (安装版 wi ...
- 【MongoDB】windows平台搭建Mongo数据库复制集(类似集群)(转)
原文链接:[MongoDB]windows平台搭建Mongo数据库复制集(类似集群)(一) Replica Sets(复制集)是在mongodDB1.6版本开始新增的功能,它可以实现故障自动切换和自 ...
- Mongodb在Windows上的配置
1.打开mongodb的官网:https://www.mongodb.org/进行下载相应平台的安装包 2.我们选择最新版的3.2版本来下载,选择对应的操作系统版本来下载,这里选择windows Mo ...
随机推荐
- 安装使用lynis扫描Linux的安全漏洞
Lynis是Linux平台上的一款安全漏洞扫描工具.它可以扫描系统的安全漏洞.收集系统信息.安装的软件信息.配置问题.没有设置密码的用户和防火墙等等. Lynis是流行可靠的安全扫描工具. 前不久,L ...
- python学习之准备
快速入门:十分钟学会Pythonhttp://python.jobbole.com/43922/python框架http://www.elias.cn/Python/HomePage#toc14[Py ...
- storm-kafka源码走读之KafkaSpout
from: http://blog.csdn.net/wzhg0508/article/details/40903919 (五)storm-kafka源码走读之KafkaSpout 原创 2014年1 ...
- 微博6月底升级后 报 10017/2/statuses/share.json或者10014/2/statuses/share.json错误
一,背景 2017-06-26微博公告替换了一些接口,导致以前的: statuses/repost 转发一条微博 statuses/update 发布一条微博 statuses/upload 上传图片 ...
- Windows 系统定时自动重启
1.创建新文本并输入 shutdown -r -t 0 保存成.bat文件 2.创建系统任务计划 2.1 在开始中打开[任务计划程序] 2.2 新建创建任务计划目录 2.3 在新目录下新建任务计划即可 ...
- 把color转成image的方法
- (UIImage*)createImageWithColor:(UIColor*) color { CGRect rect=CGRectMake(0.0f, 0.0f, 1.0f, 1.0f); ...
- maven编译问题:maven编译成功,eclipse文件未编译
我们先来看一个正常的编译流程: 1.从svn上检出一个项目: 2.看该工程是否为maven项目,不是则先转为maven项目:右键单击项目,选择configure->Convert to Mave ...
- Volley的post使用
直接看代码,注意在manifest中加入Internet权限 <uses-permission android:name="android.permission.INTERNET&qu ...
- php通过时间戳处理时间!
1.获取当前时间方法date() 很简单,这就是获取时间的方法,格式为:date(format,format,timestamp),format为格式.timestamp为时间戳–可填参数. 2.获取 ...
- Linux: How to delete a disk or LUN reference from /dev
In AIX, there is rmdev command to remove a disk/LUN from /dev directory i.e to make the disk/LUN una ...