springboot3+r2dbc响应式编程实践

网友投稿 520 2022-08-29


springboot3+r2dbc响应式编程实践

目录r2dbc工程依赖配置文件配置类beanDAOcontroller

Spring boot3已经M1了,最近群佬们也开始蠢蠢欲动的开始整活Reactive+Spring Boot3,跟着大家的步伐,我也来整一篇工程入门,我们将用java17+Spring Boot3+r2dbc+Reactive栈来讲述,欢迎大家来讨论。(关于响应式,请大家异步到之前的文章里,有详细介绍。)

r2dbc

Reactor还有基于其之上的Spring WebFlux框架。包括vert.x,rxjava等等reactive技术。我们实际上在应用层已经有很多优秀的响应式处理框架。

但是有一个问题就是所有的框架都需要获取底层的数据,而基本上关系型数据库的底层读写都还是同步的。

为了解决这个问题,出现了两个标准,一个是oracle提出的 ADBC (Asynchronous Database Access API),另一个就是Pivotal提出的R2DBC (Reactive Relational Database Connectivity)。

R2DBC是基于Reactive Streams标准来设计的。通过使用R2DBC,你可以使用reactive API来操作数据。

同时R2DBC只是一个开放的标准,而各个具体的数据库连接实现,需要实现这个标准。

今天我们以r2dbc-h2为例,讲解一下r2dbc在Spring webFlux中的使用。

工程依赖

以下是 pom.xml清单

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

org.springframework.boot

spring-boot-starter-parent

3.0.0-M1

wang.datahub

springboot3demo

0.0.1-SNAPSHOT

springboot3demo

Demo project for Spring Boot

17

org.springframework.boot

spring-boot-starter-data-r2dbc

org.springframework.boot

spring-boot-starter-data-redis-reactive

org.springframework.boot

spring-boot-starter-data-rest

org.springframework.boot

spring-boot-starter-groovy-templates

org.springframework.boot

spring-boot-starter-hateoas

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-webflux

org.springframework.boot

spring-boot-configuration-processor

true

org.springframework.boot

spring-boot-devtools

io.r2dbc

r2dbc-h2

com.h2database

h2

mysql

mysql-connector-java

runtime

org.springframework.boot

spring-boot-starter-test

test

io.projectreactor

reactor-test

test

io.projectreactor

reactor-test

org.springframework.boot

spring-boot-maven-plugin

spring-milestones

Spring Milestones

https://repo.spring.io/milestone

false

spring-snapshots

Spring Snapshots

https://repo.spring.io/snapshot

false

spring-milestones

Spring Milestones

https://repo.spring.io/milestone

false

spring-snapshots

Spring Snapshots

https://repo.spring.io/snapshot

false

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

4.0.0

org.springframework.boot

spring-boot-starter-parent

3.0.0-M1

wang.datahub

springboot3demo

0.0.1-SNAPSHOT

springboot3demo

Demo project for Spring Boot

17

org.springframework.boot

spring-boot-starter-data-r2dbc

org.springframework.boot

spring-boot-starter-data-redis-reactive

org.springframework.boot

spring-boot-starter-data-rest

org.springframework.boot

spring-boot-starter-groovy-templates

org.springframework.boot

spring-boot-starter-hateoas

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-webflux

org.springframework.boot

spring-boot-configuration-processor

true

org.springframework.boot

spring-boot-devtools

io.r2dbc

r2dbc-h2

com.h2database

h2

mysql

mysql-connector-java

runtime

org.springframework.boot

spring-boot-starter-test

test

io.projectreactor

reactor-test

test

io.projectreactor

reactor-test

org.springframework.boot

spring-boot-maven-plugin

spring-milestones

Spring Milestones

https://repo.spring.io/milestone

false

spring-snapshots

Spring Snapshots

https://repo.spring.io/snapshot

false

spring-milestones

Spring Milestones

https://repo.spring.io/milestone

false

spring-snapshots

Spring Snapshots

https://repo.spring.io/snapshot

false

配置文件

这里我们只配置了r2dbc链接信息

配置类

用于配置默认链接,创建初始化数据

package wang.datahub.springboot3demo.config;

import io.netty.util.internal.StringUtil;

import io.r2dbc.spi.ConnectionFactories;

import io.r2dbc.spi.ConnectionFactory;

import io.r2dbc.spi.ConnectionFactoryOptions;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.boot.CommandLineRunner;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import reactor.core.publisher.Flux;

import static io.r2dbc.spi.ConnectionFactoryOptions.*;

@Configuration

@ConfigurationProperties(prefix = "r2dbc")

public class DBConfig {

private String url;

private String user;

private String password;

public String getUrl() {

return url;

}

public void setUrl(String url) {

this.url = url;

}

public String getUser() {

return user;

}

public void setUser(String user) {

this.user = user;

}

public String getPassword() {

return password;

}

public void setPassword(String password) {

this.password = password;

}

@Bean

public ConnectionFactory connectionFactory() {

System.out.println("url ==> "+url);

ConnectionFactoryOptions baseOptions = ConnectionFactoryOptions.parse(url);

ConnectionFactoryOptions.Builder ob = ConnectionFactoryOptions.builder().from(baseOptions);

if (!StringUtil.isNullOrEmpty(user)) {

ob = ob.option(USER, user);

}

if (!StringUtil.isNullOrEmpty(password)) {

ob = ob.option(PASSWORD, password);

}

return ConnectionFactories.get(ob.build());

}

@Bean

public CommandLineRunner initDatabase(ConnectionFactory cf) {

return (args) ->

Flux.from(cf.create())

.flatMap(c ->

Flux.from(c.createBatch()

.add("drop table if exists Users")

.add("create table Users(" +

"id IDENTITY(1,1)," +

"firstname varchar(80) not null," +

"lastname varchar(80) not null)")

.add("insert into Users(firstname,lastname)" +

"values('Jacky','Li')")

.add("insert into Users(firstname,lastname)" +

"values('Doudou','Li')")

.add("insert into Users(firstname,lastname)" +

"values('Maimai','Li')")

.execute())

.doFinally((st) -> c.close())

)

.log()

.blockLast();

}

}

bean

创建用户bean

package wang.datahub.springboot3demo.bean;

import org.springframework.data.annotation.Id;

public class Users {

@Id

private Long id;

private String firstname;

private String lastname;

public Users(){

}

public Users(Long id, String firstname, String lastname) {

this.id = id;

this.firstname = firstname;

this.lastname = lastname;

}

public Long getId() {

return id;

}

public void setId(Long id) {

this.id = id;

}

public String getFirstname() {

return firstname;

}

public void setFirstname(String firstname) {

this.firstname = firstname;

}

public String getLastname() {

return lastname;

}

public void setLastname(String lastname) {

this.lastname = lastname;

}

@Override

public String toString() {

return "User{" +

"id=" + id +

", firstname='" + firstname + '\'' +

", lastname='" + lastname + '\'' +

'}';

}

}

DAO

dao代码清单如下,包含查询列表、按id查询,以及创建用户等操作

package wang.datahub.springboot3demo.dao;

import io.r2dbc.spi.Connection;

import io.r2dbc.spi.ConnectionFactory;

import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;

import org.springframework.data.relational.core.query.Query;

import org.springframework.stereotype.Component;

import reactor.core.publisher.Flux;

import reactor.core.publisher.Mono;

import wang.datahub.springboot3demo.bean.Users;

import static org.springframework.data.r2dbc.query.Criteria.where;

import static org.springframework.data.relational.core.query.Query.query;

@Component

public class UsersDao {

private ConnectionFactory connectionFactory;

private R2dbcEntityTemplate template;

public UsersDao(ConnectionFactory connectionFactory) {

this.connectionFactory = connectionFactory;

this.template = new R2dbcEntityTemplate(connectionFactory);

}

public Mono findById(long id) {

return this.template.selectOne(query(where("id").is(id)),Users.class);

// return Mono.from(connectionFactory.create())

// .flatMap(c -> Mono.from(c.createStatement("select id,firstname,lastname from Users where id = $1")

// .bind("$1", id)

// .execute())

// .doFinally((st) -> close(c)))

// .map(result -> result.map((row, meta) ->

// new Users(row.get("id", Long.class),

// row.get("firstname", String.class),

// row.get("lastname", String.class))))

// .flatMap( p -> Mono.from(p));

}

public Flux findAll() {

return this.template.select(Users.class).all();

// return Mono.from(connectionFactory.create())

// .flatMap((c) -> Mono.from(c.createStatement("select id,firstname,lastname from users")

// .execute())

// .doFinally((st) -> close(c)))

// .flatMapMany(result -> Flux.from(result.map((row, meta) -> {

// Users acc = new Users();

// acc.setId(row.get("id", Long.class));

// acc.setFirstname(row.get("firstname", String.class));

// acc.setLastname(row.get("lastname", String.class));

// return acc;

// })));

}

public Mono createAccount(Users account) {

return Mono.from(connectionFactory.create())

.flatMap(c -> Mono.from(c.beginTransaction())

.then(Mono.from(c.createStatement("insert into Users(firstname,lastname) values($1,$2)")

.bind("$1", account.getFirstname())

.bind("$2", account.getLastname())

.returnGeneratedValues("id")

.execute()))

.map(result -> result.map((row, meta) ->

new Users(row.get("id", Long.class),

account.getFirstname(),

account.getLastname())))

.flatMap(pub -> Mono.from(pub))

.delayUntil(r -> c.commitTransaction())

.doFinally((st) -> c.close()));

}

private Mono close(Connection connection) {

return Mono.from(connection.close())

.then(Mono.empty());

}

}

controller

controller代码清单如下,包含了查询列表、按id查询,以及创建用户等操作

package wang.datahub.springboot3demo.controller;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.http.HttpStatus;

import org.springframework.http.ResponseEntity;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.*;

import reactor.core.publisher.Flux;

import reactor.core.publisher.Mono;

import wang.datahub.springboot3demo.bean.Users;

import wang.datahub.springboot3demo.dao.UsersDao;

@RestController

public class UsersController {

@Autowired

private final UsersDao usersDao;

public UsersController(UsersDao usersDao) {

this.usersDao = usersDao;

}

@GetMapping("/users/{id}")

public Mono> getUsers(@PathVariable("id") Long id) {

return usersDao.findById(id)

.map(acc -> new ResponseEntity<>(acc, HttpStatus.OK))

.switchIfEmpty(Mono.just(new ResponseEntity<>(null, HttpStatus.NOT_FOUND)));

}

@GetMapping("/users")

public Flux getAllAccounts() {

return usersDao.findAll();

}

@PostMapping("/createUser")

public Mono> createUser(@RequestBody Users user) {

return usersDao.createAccount(user)

.map(acc -> new ResponseEntity<>(acc, HttpStatus.CREATED))

.log();

}

}

启动类清单:

package wang.datahub.springboot3demo;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.boot.context.properties.EnableConfigurationProperties;

import wang.datahub.springboot3demo.config.DBConfig;

@SpringBootApplication

@EnableConfigurationProperties(DBConfig.class)

public class WebFluxR2dbcApp {

public static void main(String[] args) {

SpringApplication.run(WebFluxR2dbcApp.class, args);

}

}

好了,致此我们整个 Demo 就实现完成了

参考链接:

https://zhuanlan.zhihu.com/p/299069835


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:django学习第十一天---django操作cookie和session(django session,cookie三者区别)
下一篇:django的orm多表查询作业第五题答案(django orm多表查询)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~