(一)基于Spring Reactor框架响应式异步编程|道法术器

                       (一)基于Spring Reactor框架响应式异步编程|道法术器_第1张图片


Spring WebFlux 响应式异步编程|道法术器(一)


R2DBC简介

Spring data R2DBC是更大的Spring data 系列的一部分,它使得实现基于R2DBC的存储库变得容易。R2DBC代表反应式关系数据库连接,这是一种使用反应式驱动程序集成SQL数据库的规范。Spring Data R2DBC使用属性的Spring抽象和Repository支持应用于R2DBC。它使得在反应式应用程序堆栈中使用关系数据访问技术构建Spring驱动的应用程序变得更加容易。

Spring Data R2DBC的目标是在概念上变得简单。为了实现这一点,它不提供缓存、延迟加载、写后处理或ORM框架的许多其他特性。这使得Spring Data R2DBC成为一个简单、有限、固执己见的对象映射器。

Spring Data R2DBC允许一种 functional 方法与数据库交互,提供R2dbcEntityTemplate作为应用程序的入口点。

首先选择数据库驱动程序并创建R2dbcEntityTemplate实例


Spring Data R2DBC可以与Spring Data JPA结合使用,其实R2DBC与原来的JPA使用方式差别不大,使用非常简单。
只是Spring Data JPA中方法返回的是真实的值,而R2DBC中,返回的是数据流Mono,Flux。

简单介绍一个Spring Data JPA。Spring Data JPA是Spring基于ORM框架、JPA规范的基础上封装的一套 JPA (Java Persistence API) 应用框架,简单说,就是类似Mybatis,Hibernate的框架(Spring Data JPA底层通过Hibernate操作数据库)。

Repository是Spring Data R2DBC中的重要概念,封装了对一个实体的操作,相当于一个dao(Data Access Object,数据访问对象)
 


官网连接:Spring Data R2DBC - Reference Documentation


5. Requirements

The Spring Data R2DBC 1.x binaries require:

  • JDK level 8.0 and above

  • Spring Framework 5.3.8 and above

  • R2DBC Arabba-SR10 and above

  • 这是官网对搭建非阻塞响应式编程的环境要求:


 一,本节将从简单的搭建开,体验下响应式非阻塞编程的大致概况:

    1.1 搭建环境:

    



    org.springframework.boot
    spring-boot-starter-parent
    2.6.5 
     

  



    org.springframework.boot
    spring-boot-starter-webflux


    mysql
    mysql-connector-java
    ${mysql-connector-java.version}


    org.springframework.boot
    spring-boot-starter-data-r2dbc

    com.github.jasync-sql
    jasync-r2dbc-mysql
    1.2.3

额外可有可无


    io.projectreactor
    reactor-test

 第二: 基础配置application.yml文件

server:
  port: 9999
  servlet:
    context-path: /
spring:
  #连接数据库的url,前缀不再是jdbc而是换成r2dbc
  #这里可以配置连接池相关的其它属性,这里为了简洁不配置
  r2dbc:
    url: mysql://localhost:3306/tope-pay-user?&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&useSSL=false
    username: root
    password: 123456

logging:
  level:
    org.springframework.r2dbc: INFO  #输出执行的sql
    org.springframework.cloud.web.reactive: info
    reactor.ipc.netty: info










第三: javaConfig文件编写,读取初始化化R2dbc连接的相关参数

package org.jd.websocket.auth.data.reactor.config;

import com.github.jasync.r2dbc.mysql.JasyncConnectionFactory;
import com.github.jasync.sql.db.mysql.pool.MySQLConnectionFactory;
import io.r2dbc.spi.ConnectionFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.r2dbc.core.R2dbcEntityOperations;
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
import org.springframework.r2dbc.connection.R2dbcTransactionManager;
import org.springframework.transaction.ReactiveTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import java.net.URI;
import java.net.URISyntaxException;

/**
 * R2dbcProperties 看源代码中,数据库连接池的相关配置
 */
@Configuration
@EnableTransactionManagement // 开启事务的支持
public class DatabaseConfiguration {

    @Bean
    @Qualifier("mysqlConnectionFactory")
    public ConnectionFactory connectionFactory(R2dbcProperties properties) throws URISyntaxException {
        // 从R2dbcProperties中,解析出 host、port、database
        URI uri = new URI(properties.getUrl());
        String host = uri.getHost();
        int port = uri.getPort();
        String database = uri.getPath().substring(1); // 去掉首位的 / 斜杠
        // 创建 Configuration 配置配置对象
        com.github.jasync.sql.db.Configuration configuration = new com.github.jasync.sql.db.Configuration(
                properties.getUsername(), host, port, properties.getPassword(), database);
        // 创建 ConnectionFactory 对象
        JasyncConnectionFactory jasyncConnectionFactory = new JasyncConnectionFactory(new MySQLConnectionFactory(configuration));
        return jasyncConnectionFactory;
    }

    @Bean
    public R2dbcEntityOperations mysqlR2dbcEntityOperations(@Qualifier("mysqlConnectionFactory") ConnectionFactory connectionFactory) {
        return new R2dbcEntityTemplate(connectionFactory);
    }

    @Bean
    public ReactiveTransactionManager transactionManager(R2dbcProperties properties) throws URISyntaxException {
        return new R2dbcTransactionManager(this.connectionFactory(properties));
    }
}

四:数据持久层: 响应式非阻塞编程 

package org.jd.websocket.auth.data.reactor.repository;


import org.jd.websocket.auth.data.reactor.entity.RSysSystem;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.data.repository.reactive.ReactiveSortingRepository;
import org.springframework.stereotype.Repository;


/**
 * 持久层:非阻塞异步访问
 */
@Repository
public interface RSysSystemReactiveRepository extends ReactiveCrudRepository, ReactiveSortingRepository {
}

五:业务层:

package org.jd.websocket.auth.data.reactor.service;

import org.jd.websocket.auth.data.reactor.entity.RSysSystem;
import reactor.core.publisher.Mono;


public interface RSysSystemService {
    /**
     * 通过ID查找单条记录
     *
     * @param systemId 系统服务ID
     * @return {@link Mono}
     */
    Mono findById(Long systemId);

    /**
     * 插入记录信息
     *
     * @param system
     * @return {@link Mono)
     */
    Mono insert(RSysSystem system);

    /**
     * 通过ID查询是否存在记录
     *
     * @param systemId 系统ID
     * @return {@link Mono}
     */
    Mono exists(Long systemId);

    /**
     * 查询记录数
     *
     * @return {@link Mono}
     */
    Mono count();
}

package org.jd.websocket.auth.data.reactor.service.impl;f

import lombok.extern.slf4j.Slf4j;
import org.jd.websocket.auth.data.reactor.entity.RSysSystem;
import org.jd.websocket.auth.data.reactor.repository.RSysSystemReactiveRepository;
import org.jd.websocket.auth.data.reactor.service.RSysSystemService;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;


/**
 * 构建全调用链路异步响应式编程
 * 系统响应式查询服务
 */
@Slf4j
@Service
public class RSysSystemServiceImpl implements RSysSystemService {
    
    @Resource
    private RSysSystemReactiveRepository sysSystemReactiveRepository;


    @Override
    public Mono findById(Long systemId) {
        return sysSystemReactiveRepository.findById(systemId);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Mono insert(RSysSystem system) {
        return sysSystemReactiveRepository.save(system);
    }

    @Override
    public Mono exists(Long systemId) {
        return sysSystemReactiveRepository.existsById(systemId);
    }

    @Override
    public Mono count() {
        return sysSystemReactiveRepository.count();
    }
}

 六:服务器访问层

基于注解方式编程

package org.jd.websocket.auth.data.reactor.controller;


import org.jd.websocket.auth.data.reactor.entity.RSysSystem;
import org.jd.websocket.auth.data.reactor.service.RSysSystemService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;


import javax.annotation.Resource;


@RestController
@RequestMapping("/system")
public class SysSystemController {
    @Resource
    private RSysSystemService rSysSystemService;

    @GetMapping("/getSysSystemById/{systemId}")
    public Mono getSySystem(@PathVariable("systemId") Long systemId) {
        Mono result = rSysSystemService.findById(systemId);
        System.out.println("result:" + result.toString());
        return result;
    }


}

七: 领域模型类

package org.jd.websocket.auth.data.reactor.entity;

import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.annotation.Version;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;
import org.springframework.format.annotation.DateTimeFormat;

/**
 * 属性上的注解使用Spring-data中的相关注解
 */
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;

@Data
@RequiredArgsConstructor
@Table(value = "sys_system")
public class RSysSystem implements Serializable {
    @Transient
    private static final long serialVersionUID = 7481799808203597699L;

    // 主键自增
    @Id
    @Column(value = "system_id")
    private Long systemId;

    /**
     * 系统名称
     * 字段映射和约束条件
     * //对应数据库表中哪个列字段及对该字段的自定义
     */
    @Column(value = "system_name")
    private String systemName;

    /**
     * 详细功能描述: 描述该系统主要包含那些那些模块,每个模块的大致功能
     */
    @Column(value = "system_detail_desc")
    private String systemDetailDesc;
    /**
     * 系统跳转到功能版块路径
     */
    @Column(value = "path_function_url")
    private String pathFunctionUrl;
    /**
     * 系统包含那些模块
     * 该字段不参与数据库映射
     */
    @Transient
    private List sysModules;
    /**
     *
     * 创建时间
     */


    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @Column(value = "create_time")
    private LocalDateTime createTime;
    /**
     * 更新时间
     */

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @Column(value = "update_time")
    private LocalDateTime updateTime;
    /**
     * 版本号(用于乐观锁, 默认为 1)
     * 使用 @Version 注解标注对应的实体类。
     * 可以通过 @TableField 进行数据自动填充。
     */
    @Version
    private Integer version;
}

测试脚本:


CREATE TABLE `sys_system` (
  `system_id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '系统主键',
  `system_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '' COMMENT '系统短名称',
  `system_detail_desc` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '系统简介',
  `path_function_url` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '跳转到版块页路径',
  `create_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `version` int(11) NOT NULL DEFAULT '0' COMMENT '版本号',
  PRIMARY KEY (`system_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

 

运行测试结果:

http://localhost:9999/system/getSysSystemById/1

(一)基于Spring Reactor框架响应式异步编程|道法术器_第2张图片


 可能会遇到时间字段(LocalDateTime)转换的问题:使用下面的配置转换类即可

package org.jd.websocket.auth.data.reactor.config;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.time.*;
import java.time.format.DateTimeFormatter;

@Configuration
public class LocalDateTimeSerializerConfig {
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> {
            //序列化
            builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            //反序列化
            builder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer());
        };
    }

    // 反序列化
    public static class LocalDateTimeDeserializer extends JsonDeserializer {
        @Override
        public LocalDateTime deserialize(JsonParser p, DeserializationContext deserializationContext)
                throws IOException {
            long timestamp = p.getValueAsLong();
            if (timestamp > 0) {
                return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.systemDefault());
            } else {
                return null;
            }
        }
    }
}

至此,基础搭建完成,后续会持续系列多篇讲解,撸下源代码及相关知识........待续..... 

参考序列:

* 官方文档
* https://github.com/spring-projects/spring-data-examples/tree/master/r2dbc/example
* https://www.reactiveprinciples.org/  中文官网

你可能感兴趣的:(spring,数据库,java)