Spring Boot WebFlux增删改查

一、前言

在 《Spring Boot 2.0 WebFlux编程》 一节我们大致了解了WebFlux的用法,这节我们将结合Mongo DB在WebFlux的架构下实现增删改查样例。和 《Spring Boot整合Mongo DB》 不同的是,我们使用的是Reactive Mongo DB依赖,所有增删改查方法返回值类型为Flux或者Mono。

二、项目准备

新建一个Spring Boot项目

2.1 引入webflux和reactive mongodb依赖

xml 复制代码
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
        </dependency>

2.2 开启Reactive Mongo DB

要开启Reactive Mongo DB的相关配置,需要在Spring Boot启动类上添加@EnableReactiveMongoRepositories注解:

java 复制代码
@EnableReactiveMongoRepositories
@SpringBootApplication
public class WebFluxCrudApplication {
    public static void main(String[] args) {
        SpringApplication.run(WebFluxCrudApplication.class, args);
    }
}

2.3 配置application.yml

接着在配置文件application.yml里配置Mongo DB连接:

yml 复制代码
spring:
  data:
    mongodb:
      host: localhost
      port: 27017
      database: webflux

使用的是webflux数据库,所以需要在Mongo DB里新建一个webflux数据库(并创建user文档/表,以供待会使用):

三、简单增删改查

3.1 创建实体

创建User实体类:

java 复制代码
@Setter
@Getter
@Document(collection = "user")
public class User {

    @Id
    private String id;

    private String name;

    private Integer age;

    private String description;

}

3.2 UserDao

创建UserDao接口,继承自ReactiveMongoRepository:

java 复制代码
@Repository
public interface UserDao extends ReactiveMongoRepository<User, String> {
}

和 《Spring Boot整合Mongo DB》 不同的是,我们继承的是ReactiveMongoRepository而非MongoRepository,它所提供的方法都是响应式的:

3.2 UserService

在UserService里通过UserDao定义简单增删改查方法:

UserService.java

java 复制代码
public interface UserService {

    Flux<User> getUsers();

    Mono<User> getUser(String id);

    Mono<User> createUser(User user);

    Mono<Void> deleteUser(String id);

    Mono<User> updateUser(String id, User user);

    Flux<User> getUserByCondition(int size, int page, User user);

    Mono<Long> getUserByConditionCount(User user);

}

实现类:

UserServiceImpl.java

java 复制代码
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private ReactiveMongoTemplate reactiveMongoTemplate;

    @Override
    public Flux<User> getUsers() {
        return userDao.findAll();
    }

    @Override
    public Mono<User> getUser(String id) {
        return this.userDao.findById(id);
    }

    @Override
    public Mono<User> createUser(User user) {
        return userDao.save(user);
    }

    @Override
    public Mono<Void> deleteUser(String id) {
        return this.userDao.findById(id)
                .flatMap(user -> this.userDao.delete(user));
    }

    @Override
    public Mono<User> updateUser(String id, User user) {
        return this.userDao.findById(id)
                .flatMap(u -> {
                    u.setName(user.getName());
                    u.setAge(user.getAge());
                    u.setDescription(user.getDescription());
                    return this.userDao.save(u);
                });
    }
}

大致上和 Spring Boot整合Mongo DB 中的UserService差不多,不同的是返回值类型为Flux或者Mono,即它们是响应式非阻塞的方法。

3.3 UserController

编写RESTfulUserController:

java 复制代码
@RestController
@RequestMapping("user")
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * 以数组的形式一次性返回所有数据
     */
    @GetMapping
    public Flux<User> getUsers() {
        return userService.getUsers();
    }

    /**
     * 以 Server sent events形式多次返回数据
     */
    @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<User> getUsersStream() {
        return userService.getUsers();
    }

    @PostMapping
    public Mono<User> createUser(User user) {
        return userService.createUser(user);
    }

    /**
     * 存在返回 200,不存在返回 404
     */
    @DeleteMapping("/{id}")
    public Mono<ResponseEntity<Void>> deleteUser(@PathVariable String id) {
        return userService.deleteUser(id)
                .then(Mono.just(new ResponseEntity<Void>(HttpStatus.OK)))
                .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }

    /**
     * 存在返回修改后的 User
     * 不存在返回 404
     */
    @PutMapping("/{id}")
    public Mono<ResponseEntity<User>> updateUser(@PathVariable String id, User user) {
        return userService.updateUser(id, user)
                .map(u -> new ResponseEntity<>(u, HttpStatus.OK))
                .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }

    /**
     * 根据用户 id查找
     * 存在返回,不存在返回 404
     */
    @GetMapping("/{id}")
    public Mono<ResponseEntity<User>> getUser(@PathVariable String id) {
        return userService.getUser(id)
                .map(user -> new ResponseEntity<>(user, HttpStatus.OK))
                .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
    }
}

对于返回值为Flux类型的方法,推荐定义两个一样的方法,一个以普通形式返回,一个以Server Sent Event的形式返回。对于修改和删除,如果需要修改和删除的用户不存在,我们返回404。

对于Flux和Mono的操作,在 《Spring Boot 2.0 WebFlux编程》 一节中已经介绍过了,这里就不再赘述了。

3.4 测试

增加用户

查找用户

删除用户

四、排序与分页

4.1 Service代码实现

在 Spring Boot整合Mongo DB 一节中,我们通过MongoTemplate实现了排序与分页。与MongoTemplate对于的响应式的对象为ReactiveMongoTemplate,所以我们照葫芦画瓢,仿照MongoTemplate的写法来实现:

java 复制代码
    @Autowired
    private ReactiveMongoTemplate reactiveMongoTemplate;

    /**
     * 分页查询,只返回分页后的数据,count值需要通过 getUserByConditionCount
     * 方法获取
     */
    @Override
    public Flux<User> getUserByCondition(int size, int page, User user) {
        Query query = getQuery(user);
        Sort sort = Sort.by("age");
        Pageable pageable = PageRequest.of(page, size, sort);
        return reactiveMongoTemplate.find(query.with(pageable), User.class);
    }

    /**
     * 返回 count,配合 getUserByCondition使用
     */
    @Override
    public Mono<Long> getUserByConditionCount(User user) {
        Query query = getQuery(user);
        return reactiveMongoTemplate.count(query, User.class);
    }

    private Query getQuery(User user) {
        Query query = new Query();
        Criteria criteria = new Criteria();
        if (!StringUtils.isEmpty(user.getName())) {
            criteria.and("name").is(user.getName());
        }
        if (!StringUtils.isEmpty(user.getDescription())) {
            criteria.and("description").regex(user.getDescription());
        }
        query.addCriteria(criteria);
        return query;
    }

之所以拆分是因为没找到与PageableExecutionUtils类的getPage方法类似的方法,如果是响应式的话,返回值类型应该是Mono<Page>,

4.2 Controller改造

新增以下方法

java 复制代码
    @GetMapping("/condition")
    public Flux<User> getUserByCondition(int size, int page, User user) {
        return userService.getUserByCondition(size, page, user);
    }
相关推荐
vx-程序开发14 小时前
【计算机毕设】51300童装超市管理系统的设计与实现
java·vue.js·spring boot·源码·课程设计·毕设·大作业
ym hyd 11114 小时前
题库建立与考试系统源码 Java+SpringBoot+Vue3 前后分离
java·开发语言·vue.js·spring boot·毕设
risc12345614 小时前
什么是码点?
java
geovindu14 小时前
rust: Facade Pattern
开发语言·后端·设计模式·rust·外观模式
JavaPub-rodert15 小时前
公共 Docker 镜像源总失效?用 Harbor Proxy Cache 自建 Docker Hub 镜像缓存
java·缓存·docker
智鸟科技GemeOpen开发者智能设备15 小时前
平安校园 AI 智能实时告警系统 智鸟科技·GemeOpen + 谷华科技·融合创新方案白皮书
java·开发语言·python·物联网·智能家居
自强的小白15 小时前
nacos配置中心面试题(导入配置的优先级),注意是如果后导入相同配置是以 后导入优先。外部入优先和nacos的数据隔离。
java·微服务
Wang's Blog15 小时前
Java框架 SpringCloud 快速入门: Feign 的自定义配置与日志级别
java·开发语言·spring cloud
成旭先生15 小时前
企业全景信息查询 API:工商照面 33 项、股东出资、变更与社保一次查全
java·开发语言·api接口·企业信息查询·工商数据·风控尽调·供应商准入
颜进强15 小时前
20 · NestJs循环依赖与 forwardRef:容器为什么在环面前会死,"占位再补齐"怎么救
前端·后端·ai编程