一、在Postgresql中创建表
CREATE TABLE product
(
id SERIAL PRIMARY KEY,
price numeric,
description varchar
)
二、创建Spring boot 项目,添加依赖,完整依赖类似如下:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.edu.tju</groupId>
<artifactId>springboot2023</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>io.r2dbc</groupId>
<artifactId>r2dbc-postgresql</artifactId>
<version>0.8.13.RELEASE</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
</dependencies>
</project>
三、在application.properties配置数据库连接:
spring.r2dbc.url=r2dbc:postgresql://localhost:5432/test
spring.r2dbc.username=postgres
spring.r2dbc.password=root
spring.datasource.driver-class-name=org.postgresql.Driver
也可以使用配置类来配置数据库:
package cn.edu.tju.config;
import io.r2dbc.spi.ConnectionFactories;
import io.r2dbc.spi.ConnectionFactory;
import io.r2dbc.spi.ConnectionFactoryOptions;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static io.r2dbc.spi.ConnectionFactoryOptions.DATABASE;
import static io.r2dbc.spi.ConnectionFactoryOptions.DRIVER;
import static io.r2dbc.spi.ConnectionFactoryOptions.HOST;
import static io.r2dbc.spi.ConnectionFactoryOptions.PASSWORD;
import static io.r2dbc.spi.ConnectionFactoryOptions.PORT;
import static io.r2dbc.spi.ConnectionFactoryOptions.USER;
@Configuration
public class R2DBCConfig {
@Bean
public ConnectionFactory connectionFactory() {
return ConnectionFactories.get(
ConnectionFactoryOptions.builder()
.option(DRIVER, "postgresql")
.option(HOST, "localhost")
.option(PORT, 5432)
.option(USER, "postgres")
.option(PASSWORD, "root")
.option(DATABASE, "test")
.build());
}
}
四、创建相关类
实体类
package cn.edu.tju.domain;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.domain.Persistable;
public class Product implements Persistable<Integer> {
@Id
private Integer id;
private String description;
private Double price;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
@Transient
public boolean isNew() {
return newFlag || id == null;
}
@Transient
private boolean newFlag;
public Product setNewFlag() {
newFlag = true;
return this;
}
}
Repository类
package cn.edu.tju.repository;
import cn.edu.tju.domain.Product;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductRepository extends ReactiveCrudRepository<Product, Integer> {
}
Service类
package cn.edu.tju.service;
import cn.edu.tju.repository.ProductRepository;
import cn.edu.tju.domain.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Service
public class ProductService {
@Autowired
private ProductRepository repository;
@Transactional
public Mono<Product> createProduct(Product product) {
return repository.save(product);
}
@Transactional
public Mono<Product> deleteProduct(int id) {
return repository
.findById(id)
.flatMap(p -> repository.deleteById(p.getId()).thenReturn(p));
}
@Transactional
public Mono<Product> updateProduct(Product product) {
return repository.findById(product.getId())
.flatMap(p -> {
p.setDescription(product.getDescription());
p.setPrice(product.getPrice());
return repository.save(p);
});
}
public Flux<Product> getProductList() {
return repository.findAll();
}
}
最后创建contoller类
package cn.edu.tju.controller;
import cn.edu.tju.service.ProductService;
import cn.edu.tju.domain.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
* The traditional way to handle the rest endpoints
*/
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
@PostMapping("/add")
public Mono<Product> add(@RequestBody Product product) {
return productService.createProduct(product);
}
@DeleteMapping("/{id}")
public Mono<Product> deleteProduct(@PathVariable int id) {
return productService.deleteProduct(id);
}
@PostMapping("/update")
public Mono<Product> updateProduct(@RequestBody Product product) {
return productService.updateProduct(product);
}
@GetMapping("/list")
public Flux<Product> listProduct() {
return productService.getProductList();
}
}
或者用RouterFunction
Handler类
package cn.edu.tju.handler;
import cn.edu.tju.domain.Product;
import cn.edu.tju.service.ProductService;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.net.URI;
@Component
public class ProductHandler {
@Autowired
private ProductService productService;
public Mono<ServerResponse> create(ServerRequest request) {
Flux<Product> flux = request
.bodyToFlux(Product.class)
.flatMap(p -> productService.createProduct(p));
return writeResponse(flux);
}
public Mono<ServerResponse> deleteById(ServerRequest request) {
return readResponse(productService.deleteProduct(id(request)));
}
public Mono<ServerResponse> update(ServerRequest request) {
Flux<Product> flux = request
.bodyToFlux(Product.class)
.flatMap(p -> productService.updateProduct(p));
return writeResponse(flux);
}
public Mono<ServerResponse> list(ServerRequest request) {
return readResponse(productService.getProductList());
}
private static Mono<ServerResponse> readResponse(Publisher<Product> products) {
return ServerResponse
.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(products, Product.class);
}
private static Mono<ServerResponse> writeResponse(Publisher<Product> product) {
return Mono
.from(product)
.flatMap(p -> ServerResponse
.created(URI.create("/product"))
.contentType(MediaType.APPLICATION_JSON)
.build()
);
}
private static int id(ServerRequest request) {
return Integer.parseInt(request.pathVariable("id"));
}
}
package cn.edu.tju.config;
import cn.edu.tju.handler.ProductHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import static org.springframework.web.reactive.function.server.RequestPredicates.DELETE;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
@Configuration
public class EndpointConfig {
private static String PRODUCT = "/product";
@Bean
RouterFunction<ServerResponse> routes(ProductHandler handler) {
return route(POST(PRODUCT+"/add"), handler::create)
.andRoute(DELETE(PRODUCT + "/{id}"), handler::deleteById)
.andRoute(POST(PRODUCT+"/update" ), handler::update)
.andRoute(GET(PRODUCT+"/list"), handler::list);
}
}
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
也可以使用DatabaseClient类来操作数据库
@Autowired
private DatabaseClient databaseClient;
@GetMapping("/test")
public Flux<Map<String, Object>> someMethod() {
return this.databaseClient.sql("select * from product").fetch().all();
}