SpringWebFlux之增删改查(函数式)

这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战


Spring WebFlux提供了注解式函数式的使用方式。注解式类似于Spring MVC使用注解开发接口,函数式使用RouterFunction (路由函数)HandlerFunction (处理函数)开发接口。本文演示函数式的开发方式。

示例

  1. 新建项目

image.png
2. 添加依赖
r2dbc-mysql响应式的MySQL连接驱动Spring Data R2DBC需要r2dbc-mysql配合工作。引入spring-boot-starter-validation依赖可以进行参数效验。

<!-- 响应式的MySQL驱动-->
<dependency>
    <groupId>dev.miku</groupId>
    <artifactId>r2dbc-mysql</artifactId>
</dependency>

<!-- Spring参数效验-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

复制代码
  1. 编写yml配置文件

Spring Data R2DBCSpring Data JPA一样支持Repository模式。需要注意的是Spring Data R2DBC的url使用r2dbc开头而不是jdbc

server:
  port: 8099
spring:
  data:
    r2dbc:
      repositories:
        enabled: true  # 开启Repository模式

  r2dbc:
    url: r2dbc:mysql://**********:****/user?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    username: ****
    password: ****
    pool:
      enabled: true
      max-size: 10
      initial-size: 10
      validation-query: select 1
  
复制代码
  1. 编写创建实体类

Spring WebFlux支持使用spring validation进行参数效验。如实体类中的name字段加上了不能为空的效验注解

@Data
@Table(value = "user")
public class User {

    @Id
    private Integer id;

    // NotBlank:效验注解
    @NotBlank(message = "name为空")
    private String name;
    
    // Java 8 新时间类型
    private LocalDate birthday;

    private String sex;
    
}
复制代码

时间类型不能使用Date,不然会出现Cannot encode value of type 'class java.util.Date异常

  1. 创建实体类的Repository

创建接口,继承R2dbcRepository接口。R2dbcRepository接口提供了响应式的增删改查方法类似于JPA中的JpaRepository接口。

public interface UserRepository extends R2dbcRepository<User, Integer> {
}
复制代码

R2dbcRepository提供了增删改查、分页查询、命名查询、jpql等功能,继承图如下所示:
image.png

  1. 编写参数验证类

新建UserValidator类并实现org.springframework.validation.Validator接口,以进行参数效验

public class UserValidator implements Validator {

    @Override
    public boolean supports(Class<?> aClass) {
        return User.class.equals(aClass);
    }

    @Override
    public void validate(Object o, Errors errors) {

        ValidationUtils.rejectIfEmpty(errors, "name", "name.empty");
    }
}
复制代码
  1. 编写Controller

Spring WebFlux 函数式使用RouterFunction (路由函数)HandlerFunction (处理函数)开发接口。RouterFunction负责接收HTTP请求,并找到对应的HandlerFunctionHandlerFunction处理HTTP请求,并返回一个延迟的ServerResponse
7.1 编写HandlerFunction
HandlerFunction负责处理请求和响应即从请求中获取参数处理后返回响应。HandlerFunction使用ServerRequestServerResponse处理请求和响应。
HandlerFunction是一个函数式接口,定义如下:
image.png

编写User的增删改查方法,所有方法的入参类型是ServerRequest,返回值类型是ServerResponse,以符合HandlerFunction的定义

@Component
public class UserHandlerFunction {

    @Resource
    private UserRepository userRepository;

    public Mono<ServerResponse> createUser(ServerRequest request){

        // 使用doOnNext方法在获取参数时进行验证
        Mono<User> userMono = request.bodyToMono(User.class).doOnNext(this::validate);
        Flux<Integer> integerFlux = userRepository.saveAll(userMono).map(User::getId);
        return ServerResponse.ok().body(integerFlux, Integer.class);
    }


    public Mono<ServerResponse> getUsers(ServerRequest request){

        Flux<User> userFlux = userRepository.findAll();
        return ServerResponse.ok().body(userFlux, User.class);
    }

    public Mono<ServerResponse> getUserById(ServerRequest request){

        String id = request.pathVariable("id");
        Mono<User> userMono = userRepository.findById(Integer.valueOf(id));
        return ServerResponse.ok().body(userMono, User.class);
    }

    public Mono<ServerResponse> updateUser(ServerRequest request){

        Mono<User> userMono = request.bodyToMono(User.class);
        userRepository.saveAll(userMono);
        return ServerResponse.ok().bodyValue("修改成功");
    }

    public Mono<ServerResponse> delUser(ServerRequest request){

        String id = request.pathVariable("id");
        userRepository.deleteById(Integer.valueOf(id));
        return ServerResponse.ok().bodyValue("删除成功");
    }
}


// 定义验证方法
private void validate(User user) {

    // 实例化实体验证器
    Validator validator = new UserValidator();
    Errors errors = new BeanPropertyBindingResult(user, "user");
    validator.validate(user, errors);
    if (errors.hasErrors()) {
        throw new ServerWebInputException(errors.toString());
    }
}

复制代码

7.2 编写RouterFunction
RouterFunction负责为请求找到对应的HandlerFunction进行处理。

新建一个配置类实现WebFluxConfigurer接口,并加@Configuration@EnableWebFlux注解,在配置类里面使用@Bean注解配置RouterFunctionBean。

RouterFunctionsRouterFunction的工具类

@Configuration
@EnableWebFlux
public class WebFLuxRoute implements WebFluxConfigurer {

    @Bean
    public RouterFunction<ServerResponse> userRouterFunction(UserHandlerFunction userHandlerFunction) {

        RouterFunction<ServerResponse> routerFunction = RouterFunctions.route()
                .GET("/users/{id}", userHandlerFunction::getUserById)
                .GET("/users", userHandlerFunction::getUsers)
                .POST("/users", userHandlerFunction::createUser)
                .PUT("/users", userHandlerFunction::updateUser)
                .DELETE("/users/{id}", userHandlerFunction::delUser)
                .build();

        return routerFunction;
    }

}
复制代码

测试

  1. 新增User

image.png

新增时不传name字段时,效验未通过
image.png

  1. 查询所有User

image.png

  1. 根据id查询User

image.png

  1. 修改User

image.png

  1. 删除User

image.png