springboot3+r2dbc——响应式编程实践
已经了,最近群佬们也开始蠢蠢欲动的开始整活+,跟着大家的步伐,我也来整一篇工程入门,我们将用+++栈来讲述,欢迎大家来讨论。(关于响应式,请大家异步到之前的文章里,有详细介绍。)
r2dbc
还有基于其之上的框架。包括,等等技术。我们实际上在应用层已经有很多优秀的响应式处理框架。
但是有一个问题就是所有的框架都需要获取底层的数据,而基本上关系型数据库的底层读写都还是同步的。
为了解决这个问题,出现了两个标准,一个是提出的 (Asynchronous Database Access API),另一个就是提出的 (Reactive Relational Database Connectivity)。
是基于标准来设计的。通过使用,你可以使用来操作数据。
同时只是一个开放的标准,而各个具体的数据库连接实现,需要实现这个标准。
今天我们以为例,讲解一下在中的使用。
工程依赖
以下是 清单
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链接信息
r2dbc:
url: r2dbc:h2:mem:///test?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
配置类
用于配置默认链接,创建初始化数据
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);
}
}
好了,致此我们整个 就实现完成了
参考链接:
https://zhuanlan.zhihu.com/p/299069835