springboot3+r2dbc響應(yīng)式編程實(shí)踐
Spring boot3已經(jīng)M1了,最近群佬們也開始蠢蠢欲動(dòng)的開始整活Reactive+Spring Boot3,跟著大家的步伐,我也來(lái)整一篇工程入門,我們將用java17+Spring Boot3+r2dbc+Reactive棧來(lái)講述,歡迎大家來(lái)討論。(關(guān)于響應(yīng)式,請(qǐng)大家異步到之前的文章里,有詳細(xì)介紹。)
r2dbc
Reactor還有基于其之上的Spring WebFlux框架。包括vert.x,rxjava等等reactive技術(shù)。我們實(shí)際上在應(yīng)用層已經(jīng)有很多優(yōu)秀的響應(yīng)式處理框架。
但是有一個(gè)問(wèn)題就是所有的框架都需要獲取底層的數(shù)據(jù),而基本上關(guān)系型數(shù)據(jù)庫(kù)的底層讀寫都還是同步的。
為了解決這個(gè)問(wèn)題,出現(xiàn)了兩個(gè)標(biāo)準(zhǔn),一個(gè)是oracle提出的 ADBC (Asynchronous Database Access API),另一個(gè)就是Pivotal提出的R2DBC (Reactive Relational Database Connectivity)。
R2DBC是基于Reactive Streams標(biāo)準(zhǔn)來(lái)設(shè)計(jì)的。通過(guò)使用R2DBC,你可以使用reactive API來(lái)操作數(shù)據(jù)。
同時(shí)R2DBC只是一個(gè)開放的標(biāo)準(zhǔn),而各個(gè)具體的數(shù)據(jù)庫(kù)連接實(shí)現(xiàn),需要實(shí)現(xiàn)這個(gè)標(biāo)準(zhǔn)。
今天我們以r2dbc-h2為例,講解一下r2dbc在Spring webFlux中的使用。
工程依賴
以下是 pom.xml清單
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ?? ?xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> ?? ?<modelVersion>4.0.0</modelVersion> ?? ?<parent> ?? ??? ?<groupId>org.springframework.boot</groupId> ?? ??? ?<artifactId>spring-boot-starter-parent</artifactId> ?? ??? ?<version>3.0.0-M1</version> ?? ??? ?<relativePath/> <!-- lookup parent from repository --> ?? ?</parent> ?? ?<groupId>wang.datahub</groupId> ?? ?<artifactId>springboot3demo</artifactId> ?? ?<version>0.0.1-SNAPSHOT</version> ?? ?<name>springboot3demo</name> ?? ?<description>Demo project for Spring Boot</description> ?? ?<properties> ?? ??? ?<java.version>17</java.version> ?? ?</properties> ?? ?<dependencies> ?? ??? ?<dependency> ?? ??? ??? ?<groupId>org.springframework.boot</groupId> ?? ??? ??? ?<artifactId>spring-boot-starter-data-r2dbc</artifactId> ?? ??? ?</dependency> ?? ??? ?<dependency> ?? ??? ??? ?<groupId>org.springframework.boot</groupId> ?? ??? ??? ?<artifactId>spring-boot-starter-data-redis-reactive</artifactId> ?? ??? ?</dependency> ?? ??? ?<dependency> ?? ??? ??? ?<groupId>org.springframework.boot</groupId> ?? ??? ??? ?<artifactId>spring-boot-starter-data-rest</artifactId> ?? ??? ?</dependency> ?? ??? ?<dependency> ?? ??? ??? ?<groupId>org.springframework.boot</groupId> ?? ??? ??? ?<artifactId>spring-boot-starter-groovy-templates</artifactId> ?? ??? ?</dependency> ?? ??? ?<dependency> ?? ??? ??? ?<groupId>org.springframework.boot</groupId> ?? ??? ??? ?<artifactId>spring-boot-starter-hateoas</artifactId> ?? ??? ?</dependency> ?? ??? ?<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-configuration-processor</artifactId> ?? ??? ??? ?<optional>true</optional> ?? ??? ?</dependency> ?? ??? ?<dependency> ?? ??? ??? ?<groupId>org.springframework.boot</groupId> ?? ??? ??? ?<artifactId>spring-boot-devtools</artifactId> ?? ??? ?</dependency> ?? ??? ?<dependency> ?? ??? ??? ?<groupId>io.r2dbc</groupId> ?? ??? ??? ?<artifactId>r2dbc-h2</artifactId> ?? ??? ?</dependency> ?? ??? ?<dependency> ?? ??? ??? ?<groupId>com.h2database</groupId> ?? ??? ??? ?<artifactId>h2</artifactId> ?? ??? ?</dependency> ?? ??? ?<dependency> ?? ??? ??? ?<groupId>mysql</groupId> ?? ??? ??? ?<artifactId>mysql-connector-java</artifactId> ?? ??? ??? ?<scope>runtime</scope> ?? ??? ?</dependency> ?? ??? ?<dependency> ?? ??? ??? ?<groupId>org.springframework.boot</groupId> ?? ??? ??? ?<artifactId>spring-boot-starter-test</artifactId> ?? ??? ??? ?<scope>test</scope> ?? ??? ?</dependency> ?? ??? ?<dependency> ?? ??? ??? ?<groupId>io.projectreactor</groupId> ?? ??? ??? ?<artifactId>reactor-test</artifactId> ?? ??? ??? ?<scope>test</scope> ?? ??? ?</dependency> ?? ??? ?<dependency> ?? ??? ??? ?<groupId>io.projectreactor</groupId> ?? ??? ??? ?<artifactId>reactor-test</artifactId> <!--?? ??? ??? ?<version>3.4.14</version>--> <!--?? ??? ??? ?<scope>compile</scope>--> ?? ??? ?</dependency> ?? ?</dependencies> ?? ?<build> ?? ??? ?<plugins> ?? ??? ??? ?<plugin> ?? ??? ??? ??? ?<groupId>org.springframework.boot</groupId> ?? ??? ??? ??? ?<artifactId>spring-boot-maven-plugin</artifactId> ?? ??? ??? ?</plugin> ?? ??? ?</plugins> ?? ?</build> ?? ?<repositories> ?? ??? ?<repository> ?? ??? ??? ?<id>spring-milestones</id> ?? ??? ??? ?<name>Spring Milestones</name> ?? ??? ??? ?<url>https://repo.spring.io/milestone</url> ?? ??? ??? ?<snapshots> ?? ??? ??? ??? ?<enabled>false</enabled> ?? ??? ??? ?</snapshots> ?? ??? ?</repository> ?? ??? ?<repository> ?? ??? ??? ?<id>spring-snapshots</id> ?? ??? ??? ?<name>Spring Snapshots</name> ?? ??? ??? ?<url>https://repo.spring.io/snapshot</url> ?? ??? ??? ?<releases> ?? ??? ??? ??? ?<enabled>false</enabled> ?? ??? ??? ?</releases> ?? ??? ?</repository> ?? ?</repositories> ?? ?<pluginRepositories> ?? ??? ?<pluginRepository> ?? ??? ??? ?<id>spring-milestones</id> ?? ??? ??? ?<name>Spring Milestones</name> ?? ??? ??? ?<url>https://repo.spring.io/milestone</url> ?? ??? ??? ?<snapshots> ?? ??? ??? ??? ?<enabled>false</enabled> ?? ??? ??? ?</snapshots> ?? ??? ?</pluginRepository> ?? ??? ?<pluginRepository> ?? ??? ??? ?<id>spring-snapshots</id> ?? ??? ??? ?<name>Spring Snapshots</name> ?? ??? ??? ?<url>https://repo.spring.io/snapshot</url> ?? ??? ??? ?<releases> ?? ??? ??? ??? ?<enabled>false</enabled> ?? ??? ??? ?</releases> ?? ??? ?</pluginRepository> ?? ?</pluginRepositories> </project>
配置文件
這里我們只配置了r2dbc鏈接信息
配置類
用于配置默認(rèn)鏈接,創(chuàng)建初始化數(shù)據(jù)
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
創(chuàng)建用戶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查詢,以及創(chuàng)建用戶等操作
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<Users> 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<Users> 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<Users> 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 <T> Mono<T> close(Connection connection) { ? ? ? ? return Mono.from(connection.close()) ? ? ? ? ? ? ? ? .then(Mono.empty()); ? ? } }
controller
controller代碼清單如下,包含了查詢列表、按id查詢,以及創(chuàng)建用戶等操作
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<ResponseEntity<Users>> 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<Users> getAllAccounts() { return usersDao.findAll(); } @PostMapping("/createUser") public Mono<ResponseEntity<Users>> createUser(@RequestBody Users user) { return usersDao.createAccount(user) .map(acc -> new ResponseEntity<>(acc, HttpStatus.CREATED)) .log(); } }
啟動(dòng)類清單:
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); ? ? } }
好了,致此我們整個(gè) Demo 就實(shí)現(xiàn)完成了
參考鏈接:
https://zhuanlan.zhihu.com/p/299069835
到此這篇關(guān)于springboot3+r2dbc響應(yīng)式編程實(shí)踐的文章就介紹到這了,更多相關(guān)springboot3 r2dbc響應(yīng)式編程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring基于xml實(shí)現(xiàn)自動(dòng)裝配流程詳解
自動(dòng)裝配是使用spring滿足bean依賴的一種方法,spring會(huì)在應(yīng)用上下文中為某個(gè)bean尋找其依賴的bean,Spring中bean有三種裝配機(jī)制,分別是:在xml中顯式配置、在java中顯式配置、隱式的bean發(fā)現(xiàn)機(jī)制和自動(dòng)裝配2023-01-01Spring?MVC基于注解的使用之JSON數(shù)據(jù)處理的方法
這篇文章主要介紹了Spring?MVC基于注解的使用JSON數(shù)據(jù)處理,json是一種輕量級(jí)的數(shù)據(jù)交換格式,是一種理想的數(shù)據(jù)交互語(yǔ)言,它易于閱讀和編寫,同時(shí)也易于機(jī)器解析和生成,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05淺析Android系統(tǒng)中HTTPS通信的實(shí)現(xiàn)
這篇文章主要介紹了淺析Android系統(tǒng)中HTTPS通信的實(shí)現(xiàn),實(shí)現(xiàn)握手的源碼為Java語(yǔ)言編寫,需要的朋友可以參考下2015-07-07自定義@RequestBody注解如何獲取JSON數(shù)據(jù)
這篇文章主要介紹了自定義@RequestBody注解如何獲取JSON數(shù)據(jù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04SpringCloud項(xiàng)目的log4j2漏洞解決方案詳解流程
很多小伙伴因?yàn)長(zhǎng)og4j2的驚爆0Day漏洞一時(shí)束手無(wú)策,這里提供最終解決方案可以進(jìn)行一個(gè)版本號(hào)的升級(jí),感興趣的朋友來(lái)看看吧2022-04-04