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、参数校验的更多相关文章

  1. spring集成mongodb jar包版本问题

    在开发过程中,spring集成mongodb的jar包. 如果需要使用排序功能. spring-data-mongodb-1.4.1.RELEASE.jar 的版本为1.4.1,如果使用如下代码: Q ...

  2. Mongodb在Windows 7下的安装及配置

    第一步 下载MongoDB: 下载mongodb的windows版本,有32位和64位版本,根据操作系统情况下载,下载地址:http://www.mongodb.org/downloads 解压缩至指 ...

  3. Mongodb在Windows下安装及配置 【转】

    1.下载mongodb的windows版本,有32位和64位版本,根据系统情况下载,下载地址:http://www.mongodb.org/downloads 2.解压缩至E:/mongodb即可 3 ...

  4. Spring Boot 2 快速教程:WebFlux 集成 Mongodb(四)

    摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 这是泥瓦匠的第104篇原创 文章工程:* JDK 1.8* M ...

  5. Spring Boot WebFlux 集成 Mongodb 数据源操作

    WebFlux 整合 Mongodb 前言 上一讲用 Map 数据结构内存式存储了数据.这样数据就不会持久化,本文我们用 MongoDB 来实现 WebFlux 对数据源的操作. 什么是 MongoD ...

  6. asp.net core集成MongoDB

    0.目录 整体架构目录:ASP.NET Core分布式项目实战-目录 一.前言及MongoDB的介绍 最近在整合自己的框架,顺便把MongoDBD的最简单CRUD重构一下作为组件化集成到asp.net ...

  7. mongoDB在windows下安装与配置方案

    首先在官网下载mongoDB的安装包: https://www.mongodb.org/downloads 百度云盘下载:http://pan.baidu.com/s/1slUSGYp (安装版 wi ...

  8. 【MongoDB】windows平台搭建Mongo数据库复制集(类似集群)(转)

    原文链接:[MongoDB]windows平台搭建Mongo数据库复制集(类似集群)(一) Replica  Sets(复制集)是在mongodDB1.6版本开始新增的功能,它可以实现故障自动切换和自 ...

  9. Mongodb在Windows上的配置

    1.打开mongodb的官网:https://www.mongodb.org/进行下载相应平台的安装包 2.我们选择最新版的3.2版本来下载,选择对应的操作系统版本来下载,这里选择windows Mo ...

随机推荐

  1. 互联网公司面试必问的mysql题目(下)

    这是mysql系列的下篇,上篇文章地址我附在文末. 什么是数据库索引?索引有哪几种类型?什么是最左前缀原则?索引算法有哪些?有什么区别? 索引是对数据库表中一列或多列的值进行排序的一种结构.一个非常恰 ...

  2. Java实现7种常见的排序算法

    数据结构中的内部排序:不需要访问外存便能完成,是一个逐步扩大记录的有序序列长度的过程. 可以分为5类: 1.插入排序:直接插入排序,稳定排序,时间复杂度为O(n^2)非递减有序,设置r[0]为哨兵进行 ...

  3. Linux性能评测工具之一:gprof篇介绍

    转:http://blog.csdn.net/stanjiang2010/article/details/5655143 这些天自己试着对项目作一些压力测试和性能优化,也对用过的测试工具作一些总结,并 ...

  4. EL and JSTL(Jsp Standard Tag Libary)(转)

    一.什么是 EL 语言. 表达式语言(EL)是 JSP 2.0 引入的一种计算和输出 Java 对象的简单语音. 二.EL 语言的作用. 为了使JSP写起来更加简单.表达式语言的灵感来自于 ECMAS ...

  5. Google网站遭到域名劫持

    今天晚上,包括Google.com在内的绝大多数Google国际网站,例如Google.com.Gmail.Google Reader.Google Docs等,在中国部分省市均出现无法访问的情况. ...

  6. 【android】Socket简单用法

    Socket通常也称做”套接字“,用于描述IP地址和端口,废话不多说,它就是网络通信过程中端点的抽象表示.值得一提的是,Java在包java.net中提供了两个类Socket和ServerSocket ...

  7. Eclipse中调试Jar包的源码(调试Struts2源码)

    首先在Eclipse中创建一个新的项目,加入运行Struts2所需要的JAR文件,并将它们加到项目的CLASSPATH中(在Lisbs中右击 build path 如下图: ),成功后的界面如图 1- ...

  8. Hibernate问题集锦: 概述

    Hibernate问题集锦: 概述   ImportNew注: 本文是ImportNew编译整理的Java面试题系列文章之一.你可以从这里查看全部的Java面试系列. Q.怎么配置hibernate? ...

  9. LaunchImage添加以及设置无效处理

    1.添加LaunchImage 2.添加所需要图片即可,出现un..可以删除,警告也随之而去,并删除LauchImage Assets之后重新添加 3.确定设置是否一样 4.发现启动后加载不了启动图, ...

  10. 程序或-内存区域分配& ELF分析 ***

    一.在学习之前我们先看看ELF文件. ELF分为三种类型: 1. .o 可重定位文件(relocalble file) 2. 可执行文件 3. 共享库(shared library) 三种格式基本上从 ...