spring-r2dbc by claude-dev-suite/claude-dev-suite
npx skills add https://github.com/claude-dev-suite/claude-dev-suite --skill spring-r2dbc<!-- pom.xml -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>r2dbc-postgresql</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
# application.yml
spring:
r2dbc:
url: r2dbc:postgresql://localhost:5432/mydb
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
pool:
enabled: true
initial-size: 5
max-size: 20
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.relational.core.mapping.Table;
import org.springframework.data.relational.core.mapping.Column;
@Table("products")
public class Product {
@Id
private Long id;
@Column("product_name")
private String name;
private String description;
private BigDecimal price;
@Column("category_id")
private Long categoryId;
@CreatedDate
private Instant createdAt;
@LastModifiedDate
private Instant updatedAt;
private boolean active;
// 构造函数、getter、setter...
}
// 使用 Java 17+ 记录(record)定义不可变实体
@Table("orders")
public record Order(
@Id Long id,
@Column("customer_id") Long customerId,
BigDecimal total,
OrderStatus status,
@CreatedDate Instant createdAt
) {
public Order withStatus(OrderStatus newStatus) {
return new Order(id, customerId, total, newStatus, createdAt);
}
}
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
import org.springframework.data.r2dbc.repository.R2dbcRepository;
import org.springframework.data.r2dbc.repository.Query;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public interface ProductRepository extends R2dbcRepository<Product, Long> {
// 自动派生的查询
Flux<Product> findByActiveTrue();
Flux<Product> findByNameContainingIgnoreCase(String name);
Flux<Product> findByCategoryId(Long categoryId);
Flux<Product> findByPriceBetween(BigDecimal min, BigDecimal max);
Mono<Product> findByNameIgnoreCase(String name);
// 排序和限制
Flux<Product> findTop10ByActiveTrueOrderByCreatedAtDesc();
// 计数和存在性检查
Mono<Long> countByActiveTrue();
Mono<Boolean> existsByName(String name);
// 自定义查询
@Query("SELECT * FROM products WHERE category_id = :categoryId AND price < :maxPrice")
Flux<Product> findByCategoryWithMaxPrice(Long categoryId, BigDecimal maxPrice);
@Query("UPDATE products SET active = false WHERE id = :id")
@Modifying
Mono<Integer> deactivateProduct(Long id);
// 使用 DTO 进行投影
@Query("SELECT id, product_name as name, price FROM products WHERE active = true")
Flux<ProductSummary> findAllSummaries();
}
public record ProductSummary(Long id, String name, BigDecimal price) {}
完整参考:关于使用 DatabaseClient 进行复杂查询,请参阅 database-client.md。
@Service
@RequiredArgsConstructor
@Slf4j
public class ProductService {
private final ProductRepository productRepository;
// 创建
public Mono<Product> createProduct(CreateProductRequest request) {
Product product = Product.create(request.name(), request.description(), request.price());
product.setCategoryId(request.categoryId());
return productRepository.save(product)
.doOnSuccess(p -> log.info("Created product: {}", p.getId()));
}
// 读取
public Mono<Product> getProduct(Long id) {
return productRepository.findById(id)
.switchIfEmpty(Mono.error(new ProductNotFoundException(id)));
}
public Flux<Product> getAllActiveProducts() {
return productRepository.findByActiveTrue();
}
// 更新
public Mono<Product> updateProduct(Long id, UpdateProductRequest request) {
return productRepository.findById(id)
.switchIfEmpty(Mono.error(new ProductNotFoundException(id)))
.map(product -> {
if (request.name() != null) product.setName(request.name());
if (request.price() != null) product.setPrice(request.price());
return product;
})
.flatMap(productRepository::save);
}
// 删除(软删除)
public Mono<Void> deactivateProduct(Long id) {
return productRepository.deactivateProduct(id)
.filter(count -> count > 0)
.switchIfEmpty(Mono.error(new ProductNotFoundException(id)))
.then();
}
}
@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository orderRepository;
private final OrderItemRepository orderItemRepository;
@Transactional
public Mono<Order> createOrder(CreateOrderRequest request) {
return validateProducts(request.items())
.then(calculateTotal(request.items()))
.flatMap(total -> {
Order order = new Order(null, request.customerId(), total, OrderStatus.PENDING, null);
return orderRepository.save(order);
})
.flatMap(order -> saveOrderItems(order.id(), request.items())
.then(Mono.just(order)));
}
private Mono<BigDecimal> calculateTotal(List<OrderItemRequest> items) {
return Flux.fromIterable(items)
.flatMap(item -> productRepository.findById(item.productId())
.map(p -> p.getPrice().multiply(BigDecimal.valueOf(item.quantity()))))
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
}
完整参考:关于 TransactionalOperator 和关系处理,请参阅 transactions.md。
| 应该做 | 不应该做 |
|---|---|
| 在 WebFlux 应用中使用 R2DBC | 混合使用 JDBC 和 R2DBC |
| 配置连接池 | 不使用连接池 |
| 使用批量查询手动处理关系 | 期望类似 JPA 的延迟加载 |
使用 @Transactional 或 TransactionalOperator | 忘记事务管理 |
使用 StepVerifier 进行测试 | 在生产环境中使用 .block() |
spring-data-jdbc 或 spring-data-jpaspring-data-mongodb 响应式仓库| 错误 | 原因 | 解决方案 |
|---|---|---|
NoSuchBeanDefinitionException: ConnectionFactory | 缺少 R2DBC 驱动 | 添加 r2dbc-postgresql/mysql 依赖 |
Connection timeout | 连接池耗尽 | 增加 max-size,检查连接泄漏 |
TransactionRequiredException | 缺少 @Transactional | 添加注解或使用 TransactionalOperator |
| 实体未映射 | 缺少注解 | 验证 @Table, @Id, @Column |
| N+1 查询 | 加载关系 | 使用带有 IN 子句的批量查询 |
| 反模式 | 问题 | 解决方案 |
|---|---|---|
| 在生产环境中使用 .block() | 阻塞事件循环 | 使用响应式操作符 |
| 关系的 N+1 查询 | 性能问题 | 使用带有 IN 的批量查询 |
| 缺少连接池 | 连接耗尽 | 配置 r2dbc-pool |
| 大型事务 | 连接持有时间过长 | 保持事务简短 |
| 没有错误处理 | 静默失败 | 使用 onErrorResume, onErrorMap |
| 问题 | 诊断 | 修复 |
|---|---|---|
| 连接超时 | 检查连接池设置 | 增加 max-size,检查泄漏 |
| 实体未映射 | 检查注解 | 添加 @Table, @Id, @Column |
| 事务不工作 | 检查 @Transactional | 如果需要,使用 TransactionalOperator |
| 查询返回空结果 | 检查列名 | 验证映射是否与数据库匹配 |
| 连接池耗尽 | 监控活动连接 | 增加连接池大小,修复泄漏 |
| 文件 | 内容 |
|---|---|
| database-client.md | DatabaseClient、动态查询、聚合 |
| transactions.md | 事务、关系、分页 |
| advanced.md | 审计、转换器、连接池配置、测试 |
每周安装次数
1
代码仓库
首次出现
3 天前
安全审计
已安装于
amp1
cline1
openclaw1
opencode1
cursor1
kimi-cli1
<!-- pom.xml -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>r2dbc-postgresql</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
# application.yml
spring:
r2dbc:
url: r2dbc:postgresql://localhost:5432/mydb
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
pool:
enabled: true
initial-size: 5
max-size: 20
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.relational.core.mapping.Table;
import org.springframework.data.relational.core.mapping.Column;
@Table("products")
public class Product {
@Id
private Long id;
@Column("product_name")
private String name;
private String description;
private BigDecimal price;
@Column("category_id")
private Long categoryId;
@CreatedDate
private Instant createdAt;
@LastModifiedDate
private Instant updatedAt;
private boolean active;
// Constructors, getters, setters...
}
// For immutable entities with records (Java 17+)
@Table("orders")
public record Order(
@Id Long id,
@Column("customer_id") Long customerId,
BigDecimal total,
OrderStatus status,
@CreatedDate Instant createdAt
) {
public Order withStatus(OrderStatus newStatus) {
return new Order(id, customerId, total, newStatus, createdAt);
}
}
import org.springframework.data.r2dbc.repository.R2dbcRepository;
import org.springframework.data.r2dbc.repository.Query;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public interface ProductRepository extends R2dbcRepository<Product, Long> {
// Automatic derived queries
Flux<Product> findByActiveTrue();
Flux<Product> findByNameContainingIgnoreCase(String name);
Flux<Product> findByCategoryId(Long categoryId);
Flux<Product> findByPriceBetween(BigDecimal min, BigDecimal max);
Mono<Product> findByNameIgnoreCase(String name);
// Ordering and limiting
Flux<Product> findTop10ByActiveTrueOrderByCreatedAtDesc();
// Count and Exists
Mono<Long> countByActiveTrue();
Mono<Boolean> existsByName(String name);
// Custom query
@Query("SELECT * FROM products WHERE category_id = :categoryId AND price < :maxPrice")
Flux<Product> findByCategoryWithMaxPrice(Long categoryId, BigDecimal maxPrice);
@Query("UPDATE products SET active = false WHERE id = :id")
@Modifying
Mono<Integer> deactivateProduct(Long id);
// Projection with DTO
@Query("SELECT id, product_name as name, price FROM products WHERE active = true")
Flux<ProductSummary> findAllSummaries();
}
public record ProductSummary(Long id, String name, BigDecimal price) {}
Full Reference : See database-client.md for complex queries with DatabaseClient.
@Service
@RequiredArgsConstructor
@Slf4j
public class ProductService {
private final ProductRepository productRepository;
// Create
public Mono<Product> createProduct(CreateProductRequest request) {
Product product = Product.create(request.name(), request.description(), request.price());
product.setCategoryId(request.categoryId());
return productRepository.save(product)
.doOnSuccess(p -> log.info("Created product: {}", p.getId()));
}
// Read
public Mono<Product> getProduct(Long id) {
return productRepository.findById(id)
.switchIfEmpty(Mono.error(new ProductNotFoundException(id)));
}
public Flux<Product> getAllActiveProducts() {
return productRepository.findByActiveTrue();
}
// Update
public Mono<Product> updateProduct(Long id, UpdateProductRequest request) {
return productRepository.findById(id)
.switchIfEmpty(Mono.error(new ProductNotFoundException(id)))
.map(product -> {
if (request.name() != null) product.setName(request.name());
if (request.price() != null) product.setPrice(request.price());
return product;
})
.flatMap(productRepository::save);
}
// Delete (soft delete)
public Mono<Void> deactivateProduct(Long id) {
return productRepository.deactivateProduct(id)
.filter(count -> count > 0)
.switchIfEmpty(Mono.error(new ProductNotFoundException(id)))
.then();
}
}
@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository orderRepository;
private final OrderItemRepository orderItemRepository;
@Transactional
public Mono<Order> createOrder(CreateOrderRequest request) {
return validateProducts(request.items())
.then(calculateTotal(request.items()))
.flatMap(total -> {
Order order = new Order(null, request.customerId(), total, OrderStatus.PENDING, null);
return orderRepository.save(order);
})
.flatMap(order -> saveOrderItems(order.id(), request.items())
.then(Mono.just(order)));
}
private Mono<BigDecimal> calculateTotal(List<OrderItemRequest> items) {
return Flux.fromIterable(items)
.flatMap(item -> productRepository.findById(item.productId())
.map(p -> p.getPrice().multiply(BigDecimal.valueOf(item.quantity()))))
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
}
Full Reference : See transactions.md for TransactionalOperator and relation handling.
| Do | Don't |
|---|---|
| Use R2DBC for WebFlux applications | Mix JDBC and R2DBC |
| Configure connection pool | Use without pool |
| Handle relations manually with batch queries | Expect JPA-like lazy loading |
Use @Transactional or TransactionalOperator | Forget transaction management |
Use StepVerifier for testing | Use .block() in production |
spring-data-jdbc or spring-data-jpaspring-data-mongodb reactive repositories| Error | Cause | Solution |
|---|---|---|
NoSuchBeanDefinitionException: ConnectionFactory | Missing R2DBC driver | Add r2dbc-postgresql/mysql dependency |
Connection timeout | Pool exhausted | Increase max-size, check connection leaks |
TransactionRequiredException | Missing @Transactional | Add annotation or use TransactionalOperator |
| Entity not mapped | Missing annotations | Verify @Table, @Id, @Column |
| N+1 queries | Loading relations | Use batch queries with IN clause |
| Anti-Pattern | Problem | Solution |
|---|---|---|
| Using .block() in production | Blocks event loop | Use reactive operators |
| N+1 queries for relations | Performance issues | Use batch queries with IN |
| Missing connection pool | Connection exhaustion | Configure r2dbc-pool |
| Large transactions | Connection held too long | Keep transactions short |
| No error handling | Silent failures | Use onErrorResume, onErrorMap |
| Problem | Diagnostic | Fix |
|---|---|---|
| Connection timeout | Check pool settings | Increase max-size, check leaks |
| Entity not mapped | Check annotations | Add @Table, @Id, @Column |
| Transaction not working | Check @Transactional | Use TransactionalOperator if needed |
| Query returns empty | Check column names | Verify mapping matches DB |
| Pool exhausted | Monitor active connections | Increase pool, fix leaks |
| File | Content |
|---|---|
| database-client.md | DatabaseClient, Dynamic Queries, Aggregations |
| transactions.md | Transactions, Relations, Pagination |
| advanced.md | Auditing, Converters, Pool Config, Testing |
Weekly Installs
1
Repository
First Seen
3 days ago
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
amp1
cline1
openclaw1
opencode1
cursor1
kimi-cli1
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
109,600 周安装
Docnify自动化:通过Rube MCP和Composio工具包实现文档操作自动化
1 周安装
Docmosis自动化集成指南:通过Rube MCP与Composio实现文档生成自动化
1 周安装
Dictionary API自动化教程:通过Rube MCP和Composio实现词典API操作自动化
1 周安装
detrack-automation:自动化追踪技能,集成Claude AI提升开发效率
1 周安装
Demio自动化工具包:通过Rube MCP和Composio实现Demio操作自动化
1 周安装
Deel自动化工具:通过Rube MCP与Composio实现HR与薪资操作自动化
1 周安装