WebFlux系列之MongoDB数据操作

时间:2022-10-30 09:57:22


概述

WebFlux对于数据库操作的支持,尤其是函数式CRUD编程。

实例

直入主题,引入依赖Spring Data Reactive MongoDB。配置文件:

spring.data.mongodb.port=27017
spring.data.mongodb.host=127.0.0.1
spring.data.mongodb.username=madmin
spring.data.mongodb.password=m123
spring.data.mongodb.database=test
spring.data.mongodb.authentication-database=admin

实体类:

@Document
@Data
public class User {
@Id
private String id;
private String username;
private String address;
}

继承自ReactiveMongoRepository的空接口,即可完成绝大多数实体类操作,当然支持自定义:

@EnableMongoRepositories
public interface UserRepository extends ReactiveMongoRepository<User,String> {
Flux<User> findUserByUsernameContaining(String name);
}

简单CRUD

@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserRepository userRepository;

@PostMapping("/")
public Mono<User> addUser(@RequestBody User user) {
return userRepository.save(user);
}

@GetMapping("/")
public Flux<User> getAll() {
return userRepository.findAll();
}

@GetMapping(value = "/stream/all", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<User> streamGetAll() {
return userRepository.findAll();
}

@GetMapping("/byname")
public Flux<User> getUserByName(String name) {
return userRepository.findUserByUsernameContaining(name);
}

@DeleteMapping("/{id}")
public Mono<ResponseEntity<Void>> deleteUser(@PathVariable String id) {
return userRepository.findById(id)
.flatMap(user -> userDao.delete(user).then(Mono.just(new ResponseEntity<Void>(HttpStatus.OK))))
.defaultIfEmpty(new ResponseEntity(HttpStatus.NOT_FOUND));
}

@PutMapping("/")
public Mono<ResponseEntity<User>> updateUser(@RequestBody User user) {
return userRepository.findById(user.getId())
.flatMap(u -> userDao.save(user))
.map(u->new ResponseEntity<User>(u,HttpStatus.OK))
.defaultIfEmpty(new ResponseEntity(HttpStatus.NOT_FOUND));
}
}

提供两个查询接口,一个返回 Flux,里边包含多个对象;一个设置响应Content-Type 为 text/event-stream,通过响应式流返回数据

WebFlux系列之MongoDB数据操作


可见两种不同的查询方式返回的数据格式也有差异。前者是以数组形式一次性返回数据,后者是以 SSE 的形式多次返回数据。

请求地址路由

在 SpringMVC 中,可以通过如下一些注解来控制请求 URL 和处理器之间的映射关系:
@RequestMapping
@GetMapping
@PostMapping
@DeleteMapping
@PutMapping

这些注解在 WebFlux 中依然还可以继续使用,WebFlux 也提供了自己的方案–Router。

开发处理器

@Component
public class UserHandler {
@Autowired
private UserRepository userRepository;

public Mono<ServerResponse> addUser(ServerRequest serverRequest) {
return ok().contentType(APPLICATION_JSON)
.body(userRepository.saveAll(serverRequest.bodyToMono(User.class)), User.class);
}

public Mono<ServerResponse> deleteUser(ServerRequest serverRequest) {
return userRepository.findById(parseLong(serverRequest.pathVariable("id")))
.flatMap(p -> userRepository.delete(p).then(ok().build()))
.switchIfEmpty(notFound().build());
}

public Mono<ServerResponse> getAllUser(ServerRequest serverRequest) {
return ok().contentType(APPLICATION_JSON)
.body(userRepository.findAll(), User.class);
}
}
  • 处理器需要使用@Component注入到 Spring 容器中
  • 所有方法的返回值类型都是​​Mono<ServerResponse>​​,参数类型都是 ServerRequest,因为一会配置 Router 时涉及到的 HandlerFunction 里边就是这样定义的,换句话说,这里定义的每一个方法都满足 HandlerFunction 函数式接口

配置路由
将请求的 URL 地址和这些处理器之间关联起来:

@Configuration
public class RouterConfiguration {
@Bean
RouterFunction<ServerResponse> userRouter(UserHandler userHandler) {
return RouterFunctions.nest(RequestPredicates.path("/user"),
RouterFunctions.route(RequestPredicates.POST("/"), userHandler::addUser)
.andRoute(RequestPredicates.GET("/"), userHandler::getAllUser)
.andRoute(RequestPredicates.DELETE("/{id}"), userHandler::deleteUser));
}
}
  • 配置类类似于SpringMVC 中的 DispatcherServlet,负责请求的分发,根据不同的请求 URL找到对应的处理器去处理
  • 通过RouterFunctions工具类来创建 RouterFunction 实例,nest方法第一个参数配置地址的前缀,类似于在 Controller 类上直接写 @RequestMapping 注解去配置地址
  • nest 方法的第二个参数就是 RouterFunction 实例,每一个 RouterFunction 实例通过 RouterFunctions.route 方法来构建,第一个参数就是请求的 URL 地址(注意配置地址都有一个共同前缀),第二个参数通过方法引用的方式配置HandlerFunction,即当前请求对应的处理器
  • 通过 addRoute 方法可以配置多个路由策略

参考