Skip to main content
Advertisement

14.3 Reactive Database CRUD with R2DBC

To build a fully non-blocking pipeline with WebFlux, the data layer must also be non-blocking. R2DBC (Reactive Relational Database Connectivity) is the reactive database driver standard that eliminates the blocking nature of traditional JDBC.

1. Dependencies and Configuration

implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc'
runtimeOnly 'io.asyncer:r2dbc-mysql:1.2.0' // MySQL R2DBC driver example
spring:
r2dbc:
url: r2dbc:mysql://localhost:3306/mydb
username: root
password: secret
warning

R2DBC cannot be used alongside JPA (Hibernate). The reactive stack (WebFlux + R2DBC) and the servlet stack (MVC + JPA) are entirely different architectures. Never mix them within a single service.

2. Extending ReactiveCrudRepository

// 1. Entity definition (@Table → table mapping, @Id → primary key)
@Table("users")
@Data
@NoArgsConstructor
public class User {
@Id
private Long id;
private String name;
private String email;
}

// 2. Reactive repository (note the Mono/Flux return types)
@Repository
public interface UserR2dbcRepository extends ReactiveCrudRepository<User, Long> {
Flux<User> findByName(String name);
Mono<User> findByEmail(String email);
}

// 3. Service layer — non-blocking DB operations
@Service
@RequiredArgsConstructor
public class UserReactiveService {

private final UserR2dbcRepository userRepository;

public Flux<User> getAllUsers() {
return userRepository.findAll();
}

public Mono<User> createUser(User user) {
return userRepository.save(user);
}

public Mono<User> getUserById(Long id) {
return userRepository.findById(id)
.switchIfEmpty(Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found.")));
}

public Mono<Void> deleteUser(Long id) {
return userRepository.findById(id)
.switchIfEmpty(Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND)))
.flatMap(userRepository::delete);
}
}

// 4. WebFlux controller — completing the non-blocking response pipeline
@RestController
@RequestMapping("/api/v2/users")
@RequiredArgsConstructor
public class UserReactiveController {
private final UserReactiveService userService;

@GetMapping
public Flux<User> getAll() { return userService.getAllUsers(); }

@GetMapping("/{id}")
public Mono<User> getById(@PathVariable Long id) { return userService.getUserById(id); }

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Mono<User> create(@RequestBody User user) { return userService.createUser(user); }
}

With Controller → Service → Repository all returning Mono/Flux, from request to DB response, not a single thread is blocked — a truly complete non-blocking chain.

Advertisement