想要了解更多关于响应式API的信息?看看这篇文章,我们将探索使用R2DBC,Spring Data JDBC等构建响应式Web应用程序。
我知道 - 标题中已列出很多技术。 Spring WebFlux,作为构建响应堆栈Web应用程序的项目,已经被Spring 5和Spring Boot 2中引入。我曾在这篇文章中-Spring WebFlux和Spring Cloud的响应式微服务描述了它如何与Spring Boot和Spring Cloud来构建响应式微服务。 Spring 5还引入了支持对NoSQL数据库进行响应性访问的项目,如Cassandra,MongoDB或Couchbase。但是,仍然缺乏对访问关系型数据库的响应性的支持。这一变化将随着R2DBC(响应关系数据库连接)项目一起发生变化。该项目也由Pivotal成员开发。这似乎是一个非常有趣的行为,然而,它正处于起步阶段。无论如何,有一个与Postgres集成的模块,我们将用它来演示应用程序。
R2DBC不是本文中描述的唯一一个新的有趣解决方案。我还将向您展示如何使用Spring Data JDBC - 这是最近发布的另一个非常有趣的项目。值得一提的是Spring Data JDBC的功能。该项目已经发布,可在1.0版本下使用。它是更大的Spring Data框架的一部分。它提供了基于JDBC的存储库抽象。创建该库的主要原因是允许使用Spring Data(通过CrudRepository接口)访问关系数据库,而不将JPA库包含到应用程序依赖项中。当然,JPA仍然是用于Java应用程序的主要持久性API。 Spring Data JDBC旨在通过不实现延迟加载,缓存,脏上下文和会话等流行模式,在概念上比JPA更简单。它还为基于注释的映射提供非常有限的支持。最后,它提供了使用R2DBC访问关系数据库的反应式存储库的实现。虽然该模块仍在开发中(只有SNAPSHOT版本可用),但我们将在演示应用程序中尝试使用它。让我们继续做下去。
依赖包
我们使用Kotlin来实现。首先,我们包含一些必需的Kotlin依赖项。
org.jetbrains.kotlin
kotlin-stdlib
${kotlin.version}
com.fasterxml.jackson.module
jackson-module-kotlin
org.jetbrains.kotlin
kotlin-reflect
org.jetbrains.kotlin
kotlin-test-junit
${kotlin.version}
test
我们还应该添加kotlin-maven-plugin来支持Spring。
org.jetbrains.kotlin
kotlin-maven-plugin
${kotlin.version}
compile
compile
compile
test-compile
test-compile
test-compile
-Xjsr305=strict
spring
然后,我们继续包含演示实现所需的框架。我们需要包含专用于使用R2DBC访问数据库的Spring Data JDBC的特殊SNAPSHOT版本。我们还必须添加R2DBC库和Spring WebFlux。正如您在下面看到的,只有Spring WebFlux是稳定版本(作为Spring Boot RELEASE的一部分)。
org.springframework.boot
spring-boot-starter-webflux
org.springframework.data
spring-data-jdbc
1.0.0.r2dbc-SNAPSHOT
io.r2dbc
r2dbc-spi
1.0.0.M5
io.r2dbc
r2dbc-postgresql
1.0.0.M5
另一个重要的事是为Spring Data项目设置依赖关系管理。
org.springframework.data
spring-data-releasetrain
Lovelace-RELEASE
import
pom
Repositories(仓库)
我们使用著名的Spring Data风格的CRUD存储库实现。在这种情况下,我们需要创建一个扩展ReactiveCrudRepository接口的接口。
这是用于管理Employee对象的存储库的实现。
interface EmployeeRepository : ReactiveCrudRepository<Employee, Int< {
@Query("select id, name, salary, organization_id from employee e where e.organization_id = $1")
fun findByOrganizationId(organizationId: Int) : Flux<Employee>
}
这里是存储库的另一个实现——这次,我们使用它来管理Organization对象。
interface OrganizationRepository : ReactiveCrudRepository<Organization, Int< { }
Entities和DTOs实现
Kotlin提供了一种通过将实体类声明为数据类从而创建实体类的便捷方法。使用Spring Data JDBC时,我们必须通过使用@Id注释字段来为实体设置主键。它假定主键由数据库设置自动递增。如果未使用自动增量列,则必须使用BeforeSaveEvent侦听器,该侦听器设置实体的ID。但是,我尝试为实体设置这样的监听器,但它只是在Spring Data JDBC的响应式版本中不起作用。
这是Employee实体类的实现。值得一提的是,Spring Data JDBC会自动将类字段organizationId映射到数据库列organization_id。
data class Employee(
val name: String,
val salary: Int,
val organizationId: Int)
{ @Id var id: Int? = null }
这是Organization实体类的实现。
data class Organization(var name: String) {
@Id
var id: Int? = null
}
R2DBC不支持任何列表或集合。因为我想在其中一个API端点中返回Organization对象内的员工列表,所以我创建了一个包含这样一个列表的DTO,如下所示。
data class OrganizationDTO(var id: Int?, var name: String) {
var employees : MutableList = ArrayList()
constructor(employees: MutableList) : this(null, "") {
this.employees = employees
}
}
与创建的实体对应的SQL脚本如下所示。字段类型serial将自动创建序列并将其附加到字段ID。
CREATE TABLE employee (
name character varying NOT NULL,
salary integer NOT NULL,
id serial PRIMARY KEY,
organization_id integer
);
CREATE TABLE organization (
name character varying NOT NULL,
id serial PRIMARY KEY
);
构建示例Web应用程序
出于演示目的,我们将构建两个独立的应用程序:employee-service (员工服务)和 organization-service(组织服务)。应用程序组织服务使用WebFlux WebClient与员工服务进行通信。它获取分配给组织的员工列表,并包含它们以与组织对象一起响应。示例应用程序源代码可在GitHub上的存储库sample-spring-data-webflux下获得。
准备就绪,让我们从声明Spring Boot主类开始吧。我们需要通过使用@EnableJdbcRepositories注释主类来启用Spring Data JDBC存储库。
@SpringBootApplication
@EnableJdbcRepositories
class EmployeeApplication
fun main(args: Array<String>) {
runApplication<EmployeeApplication>(*args)
}
使用R2DBC和Postgres需要一些配置。由于早期Spring Data JDBC和R2DBC开发阶段,Postgres没有Spring Boot自动配置。我们需要在bean中用@Configuration声明连接工厂,客户端和存储库。
@Configuration
class EmployeeConfiguration {
@Bean
fun repository(factory: R2dbcRepositoryFactory): EmployeeRepository {
return factory.getRepository(EmployeeRepository::class.java)
}
@Bean
fun factory(client: DatabaseClient): R2dbcRepositoryFactory {
val context = RelationalMappingContext()
context.afterPropertiesSet()
return R2dbcRepositoryFactory(client, context)
}
@Bean
fun databaseClient(factory: ConnectionFactory): DatabaseClient {
return DatabaseClient.builder().connectionFactory(factory).build()
}
@Bean
fun connectionFactory(): PostgresqlConnectionFactory {
val config = PostgresqlConnectionConfiguration.builder() //
.host("192.168.99.100") //
.port(5432) //
.database("reactive") //
.username("reactive") //
.password("reactive123") //
.build()
return PostgresqlConnectionFactory(config)
}
}
最后,我们可以创建包含定义的响应API方法的REST控制器。使用Kotlin,它不会占用太多空间。以下控制器定义包含三种GET方法,这些方法允许我们查找所有员工,查找分配给给定组织的所有员工或根据id查找单个员工。
@RestController
@RequestMapping("/employees")
class EmployeeController {
@Autowired
lateinit var repository : EmployeeRepository
@GetMapping
fun findAll() : Flux<Employee> = repository.findAll()
@GetMapping("/{id}")
fun findById(@PathVariable id : Int) : Mono<Employee> = repository.findById(id)
@GetMapping("/organization/{organizationId}")
fun findByorganizationId(@PathVariable organizationId : Int) : Flux<Employee> = repository.findByOrganizationId(organizationId)
@PostMapping
fun add(@RequestBody employee: Employee) : Mono<Employee> = repository.save(employee)
}
内部服务交互
对于OrganizationController,实现有点复杂。由于组织服务正在与员工服务进行通信,因此我们首先需要声明响应式WebFlux WebClientbuilder。
@Bean
fun clientBuilder() : WebClient.Builder {
return WebClient.builder()
}
然后,类似于存储库bean,构建器被注入控制器。它在findByIdWithEmployees方法中用于调用employee-service公开的方法GET / employees / organization / {organizationId}。正如您在下面的代码片段中看到的,它提供了一个响应式API并返回包含已找到员工列表的Flux对象。使用zipWith Reactor方法将此列表注入到OrganizationDTO对象中。
@RequestMapping("/organizations")
class OrganizationController {
@Autowired
lateinit var repository : OrganizationRepository
@Autowired
lateinit var clientBuilder : WebClient.Builder
@GetMapping
fun findAll() : Flux<Organization> = repository.findAll()
@GetMapping("/{id}")
fun findById(@PathVariable id : Int) : Mono<Organization> = repository.findById(id)
@GetMapping("/{id}/withEmployees")
fun findByIdWithEmployees(@PathVariable id : Int) : Mono<OrganizationDTO> {
val employees : Flux<Employee> = clientBuilder.build().get().uri("http://localhost:8090/employees/organization/$id")
.retrieve().bodyToFlux(Employee::class.java)
val org : Mono = repository.findById(id)
return org.zipWith(employees.collectList())
.map { tuple -> OrganizationDTO(tuple.t1.id as Int, tuple.t1.name, tuple.t2) }
}
@PostMapping
fun add(@RequestBody employee: Organization) : Mono<Organization> = repository.save(employee)
}
它是如何工作的?
在测试运行之前,我们需要启动Postgres数据库。这是用于运行Postgres容器的Docker命令。它正在创建具有密码的用户,并设置默认数据库。
$ docker run -d --name postgres -p 5432:5432 -e POSTGRES_USER=reactive -e POSTGRES_PASSWORD=reactive123 -e POSTGRES_DB=reactive postgres
然后,我们需要创建一些测试表,因此您必须运行放置在实现实体和DTO部分中的SQL脚本。之后,您可以启动我们的测试应用程序。如果不覆盖application.yml文件中提供的默认设置,则employee-service端口8090上监听和organization-service在端口8095上监听。下图说明了我们的示例系统的体系结构。
现在,让我们使用应用程序公开的响应API添加一些测试数据。
$ curl -d '{"name":"Test1"}' -H "Content-Type: application/json" -X POST http://localhost:8095/organizations
$ curl -d '{"name":"Name1", "balance":5000, "organizationId":1}' -H "Content-Type: application/json" -X POST http://localhost:8090/employees
$ curl -d '{"name":"Name2", "balance":10000, "organizationId":1}' -H "Content-Type: application/json" -X POST http://localhost:8090/employees
最后,您可以使用Web浏览器调用GET方法: / {id} / withEmployees。结果应类似于下图。
原文链接:https://dzone.com/articles/introduction-to-reactive-apis-with-postgres-r2dbc
作者:Piotr Mińkowski
译者:Emma
推荐: Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现
上一篇:新时代的开发姿势
关注公众号
点击原文阅读更多