Spring Boot with R2DBC — CRUD Operations
Today we’ll learn about connecting to SQL (Relational) databases with reactive repositories with spring boot. In this project i’ll be using
- Spring boot 2.3.0.RELEASE
- PostgreSQL for database
Project Setup
application.properties
spring.r2dbc.url=r2dbc:pool:postgresql://localhost:6432/r2dbc
spring.r2dbc.username=username
spring.r2dbc.password=password
Creating Entity
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;@Table
public class Employee {
@Id
private Long id;
private String name;
private String department;
}
SQL Code for creating employee table
CREATE TABLE employee(
id bigserial NOT NULL,
name character varying(255) ,
department character varying(255) ,
CONSTRAINT pk_emp_id PRIMARY KEY (id)
);
Notice that, annotation @Id
comes from spring data and not from java persistence when we use spring data jpa. Also, we have used bigserial
as the data type for id to make auto-increment. By default behaviour when we use spring r2dbc will be, if field which contains @Id
annotation is considered as identifier for entity and if that field is empty then it is considered as new entity and triggers insert operation when we call save()
function of repository. But if the identifier field is non empty then it’s considered as not a new entity and will trigger update. So, if we don’t use bigserial
and try to save the entity with id field not null, then it’ll trigger update and will throw that unable to update row with identifier. That’s why we used bigserial
.
Repository
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
public interface EmployeeRepository extends ReactiveCrudRepository<Employee, Long> {
}
Here, we have used ReactiveCrudRepository to extend our EmployeeRepository. It gives reactive implementation of accessing relational databases.
Controller
@RestController
@RequestMapping("/employees")
public class EmployeeController {
@Autowired
private EmployeeRepository employeeRepository;}
Now we’ll add endpoints for CRUD operations.
Add Employee
@PostMapping
public Mono<Employee> saveEmployee(@RequestBody Employee employee){
return employeeRepository.save(employee);
}
As discussed previously, when we want to create new entity then we will not supply any data in id
field and it’ll be auto-generated by database.
Read Employee
//fetch employee by id
@GetMapping("/{id}")
public Mono<Employee> findOne(@PathVariable("id") Long id){
return employeeRepository.findById(id);
}//fetch all employee
@GetMapping
public Flux<Employee> findAll(){
return employeeRepository.findAll();
}
When we want to find single entity, then we can use findById()
which will return Mono (when we want to emit 0 or 1 element). Alternatively, if we want to emit more than 1 element then we can use Flux, as showed in above example with findAll()
.
Update Employee
@PutMapping
public Mono<Employee> updateEmployee(@RequestBody Employee employee){
return employeeRepository.save(employee);
}
Now, here we want to update then entity so we’ll supply id
which will help identify which row we want to update in database.
Delete Employee
@DeleteMapping("/{id}")
public Mono<Employee> deleteEmployee(@PathVariable("id") Long id){
return employeeRepository.findById(id)
.doOnSuccess(employee -> employeeRepository.delete(employee).subscribe());
}
Here, we call subscribe()
on delete()
function of repository, because if don’t use subscribe then it’ll not call execute this function. delete()
returns Mono<Void>
, mono/flux will only executed when there is someone to consume it, else they will not be executed. So when we call subscribe()
over mono then only it executes. In other cases we didn’t call subscribe()
because we directly returned mono, here we don’t return the mono which is returned by delete()
, instead we return the mono from findById()
that’s why we didn’t call subscribe()
over this mono.
That’s it !
Your reactive relational database repositories are working.
Code is available on github : https://github.com/rsbeoriginal/r2dbc