Master Reactive Database Access with Spring Boot WebFlux and R2DBC
Learn how to integrate Spring Boot WebFlux with R2DBC for reactive database operations, covering R2DBC basics, driver selection, configuration, entity and service implementation, controller endpoints, repository usage, optimistic locking, and practical code examples to build high-performance, non-blocking backend applications.
1. Introduction to R2DBC
Spring Data R2DBC is part of the larger Spring Data suite and simplifies building reactive repositories for relational databases. R2DBC (Reactive Relational Database Connectivity) provides a standard for integrating SQL databases in a reactive, non‑blocking manner. Spring Data R2DBC offers a functional API via R2dbcEntityTemplate as the entry point.
2. WebFlux Overview
Spring WebFlux, added in Spring 5, is a fully non‑blocking reactive web framework that runs on Netty, Undertow, or Servlet 3.1+ containers. It coexists with Spring Web MVC, allowing applications to use either or both modules.
3. Adding Dependencies
<code><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>dev.miku</groupId>
<artifactId>r2dbc-mysql</artifactId>
</dependency>
</dependencies></code>4. Application Configuration
<code>spring:
r2dbc:
url: r2dbc:mysql://localhost:3306/reactive_db
username: root
password: 123123
pool:
initialSize: 100
maxSize: 100
---
logging:
level:
org.springframework.r2dbc: DEBUG # output executed SQL</code>5. Entity and Service Implementation
<code>@Table("T_USERS")
public class Users {
@Id
private Long id;
private String name;
private String sex;
private Integer age;
}
@Service
@Resource
private R2dbcEntityTemplate template;
@Transactional
public Mono<Users> insertByTemplate(Users users) {
return template.insert(users);
}
public Mono<Integer> removeByTemplate(Long id) {
Query query = Query.query(Criteria.where("id").is(id));
return template.delete(query, Users.class);
}
public Mono<Integer> updateByTemplate(Users users) {
CriteriaDefinition criteria = Criteria.where("id").is(users.getId());
Query query = Query.query(criteria);
Update update = Update.update("name", users.getName());
return template.update(query, update, Users.class);
}
public Mono<Users> selectByTemplate(Long id) {
Query query = Query.query(Criteria.where("id").is(id));
return template.select(query, Users.class).single();
}
public Flux<Users> selectByTemplate(Integer page, Integer size) {
Query query = Query.empty().offset((page - 1) * size).limit(size);
return template.select(query, Users.class);
}
public Mono<Long> selecyByTemplateCount() {
return template.select(Users.class).count();
}
public Mono<ResponseEntity<List<Users>>> selectByTemplatePager(Integer page, Integer size) {
Mono<List<Users>> datas = this.selectByTemplate(page, size).collectList();
Mono<Long> count = this.selecyByTemplateCount();
return datas.zipWith(count, (list, c) -> {
return ResponseEntity.ok()
.header("count", c + "")
.header("page", page + "")
.header("size", size + "")
.body(list);
});
}</code>6. Controller Endpoints
<code>@Resource
private UsersService us;
@PostMapping("/insert")
public Mono<Users> insertByTemplate(@RequestBody Users users) {
return us.insertByTemplate(users);
}
@GetMapping("/remove/{id}")
public Mono<Integer> removeByTemplate(@PathVariable("id") Long id) {
return us.removeByTemplate(id);
}
@PostMapping("/update")
public Mono<Integer> updateByTemplate(@RequestBody Users users) {
return us.updateByTemplate(users);
}
@GetMapping("/query/{id}")
public Mono<Users> selectByTemplate(@PathVariable("id") Long id) {
return us.selectByTemplate(id).single();
}
@GetMapping("/pager")
public Mono<ResponseEntity<List<Users>>> selectByTemplate(Integer page, Integer size) {
return us.selectByTemplatePager(page, size);
}
@GetMapping("/count")
public Mono<Long> selecyByTemplateCount() {
return us.selecyByTemplateCount();
}</code>7. R2DBC Repository
Repositories can extend ReactiveCrudRepository or ReactiveSortingRepository . The following table (illustrated below) lists the query methods they provide.
<code>interface ReactivePersonRepository extends ReactiveSortingRepository<Person, String> {
Mono<Integer> deleteByLastname(String lastname);
Mono<Void> deletePersonByLastname(String lastname);
Mono<Boolean> deletePersonByLastname(String lastname);
}</code>8. Custom Modifying Query
<code>@Modifying
@Query("UPDATE person SET firstname = :firstname where lastname = :lastname")
Mono<Integer> setFixedFirstnameFor(String firstname, String lastname);
</code>9. Optimistic Lock Support
The @Version annotation adds a version column to the entity, enabling optimistic locking similar to JPA. If a concurrent update changes the version, an OptimisticLockingFailureException is thrown.
<code>@Table
public class Person {
@Id Long id;
String firstname;
String lastname;
@Version Long version;
}
</code>Example that triggers the optimistic‑lock exception:
<code>R2dbcEntityTemplate template = …;
// 1. Insert initial data (version = 0)
Mono<Person> daenerys = template.insert(new Person("Daenerys"));
// 2. Load the inserted row (version = 0)
Person other = template.select(Person.class)
.matching(query(where("id").is(daenerys.getId()))).first().block();
// 3. Update the row – version becomes 1
daenerys.setLastname("Targaryen");
template.update(daenerys);
// 4. Attempt to update the stale instance – triggers exception
template.update(other).subscribe();
</code>10. Conclusion
This article demonstrates how to combine Spring Boot WebFlux with R2DBC to achieve reactive database programming. By leveraging non‑blocking I/O, developers can handle high concurrency and improve application performance and responsiveness. The provided code samples illustrate configuration, entity modeling, service and controller layers, repository usage, and optimistic locking, offering a solid foundation for building scalable, maintainable backend systems.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.