Skip to content

Commit b1f9cb8

Browse files
Suman DasSuman Das
Suman Das
authored and
Suman Das
committed
added sample code for postgres r2dbc
1 parent f69b252 commit b1f9cb8

34 files changed

+362
-227
lines changed

README.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
# Sample Reactive - Spring Boot application
2-
The purpose of this project is to demonstrate how we can use [Spring WebFlux](https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html) to create a reactive web application
2+
The purpose of this project is to demonstrate how we can use [Spring WebFlux](https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html) to create a simple reactive web application.
3+
4+
This project uses [PostgreSQL](https://github.com/r2dbc/r2dbc-postgresql) implementation of the R2DBC SPI.
35

46
# How to build and run
57

68
project can be compiled with JDK 8 and above `javac`.
79

8-
To compile just do `mvn clean install`.
10+
To compile just do `mvn clean package`.
911

1012
To run the application execute the following:
1113
```
1214
java -jar target/reactive-examples*.jar
1315
```
1416
You can also use the Swagger-ui.html to test the application.
15-
![alt text](swagger-ui.png)
17+
![alt text](react-starter-demo.png)
1618

1719
for more detailed technical information please check my post :
1820

@@ -36,7 +38,6 @@ The application contains the following REST APIs
3638
3739
4. GET /users/events - Stream users to a browser as Server-Sent Events
3840
```
39-
It also creates sample data in the database so that it can be tested. It is idempotent in nature.
41+
It contain a sample WebClient to retrieve data from our User Management application.
4042

41-
It also contain a sample WebClient to retrieve data from our User Management application.
4243

pom.xml

+34-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<groupId>org.springframework.boot</groupId>
77
<artifactId>spring-boot-starter-parent</artifactId>
8-
<version>2.2.6.RELEASE</version>
8+
<version>2.3.0.RC1</version>
99
<relativePath/> <!-- lookup parent from repository -->
1010
</parent>
1111
<groupId>com.reactive.examples</groupId>
@@ -21,13 +21,33 @@
2121
<dependencies>
2222
<dependency>
2323
<groupId>org.springframework.boot</groupId>
24-
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
24+
<artifactId>spring-boot-starter-data-r2dbc</artifactId>
2525
</dependency>
2626
<dependency>
2727
<groupId>org.springframework.boot</groupId>
2828
<artifactId>spring-boot-starter-webflux</artifactId>
2929
</dependency>
3030

31+
<dependency>
32+
<groupId>com.h2database</groupId>
33+
<artifactId>h2</artifactId>
34+
<scope>runtime</scope>
35+
</dependency>
36+
<dependency>
37+
<groupId>io.r2dbc</groupId>
38+
<artifactId>r2dbc-h2</artifactId>
39+
<scope>runtime</scope>
40+
</dependency>
41+
<dependency>
42+
<groupId>io.r2dbc</groupId>
43+
<artifactId>r2dbc-postgresql</artifactId>
44+
<scope>runtime</scope>
45+
</dependency>
46+
<dependency>
47+
<groupId>org.postgresql</groupId>
48+
<artifactId>postgresql</artifactId>
49+
<scope>runtime</scope>
50+
</dependency>
3151
<dependency>
3252
<groupId>org.projectlombok</groupId>
3353
<artifactId>lombok</artifactId>
@@ -44,11 +64,6 @@
4464
</exclusion>
4565
</exclusions>
4666
</dependency>
47-
<dependency>
48-
<groupId>de.flapdoodle.embed</groupId>
49-
<artifactId>de.flapdoodle.embed.mongo</artifactId>
50-
<scope>test</scope>
51-
</dependency>
5267
<dependency>
5368
<groupId>io.projectreactor</groupId>
5469
<artifactId>reactor-test</artifactId>
@@ -86,6 +101,18 @@
86101
<name>jcenter</name>
87102
<url>http://oss.jfrog.org/artifactory/oss-snapshot-local/</url>
88103
</repository>
104+
<repository>
105+
<id>spring-milestones</id>
106+
<name>Spring Milestones</name>
107+
<url>https://repo.spring.io/milestone</url>
108+
</repository>
89109
</repositories>
110+
<pluginRepositories>
111+
<pluginRepository>
112+
<id>spring-milestones</id>
113+
<name>Spring Milestones</name>
114+
<url>https://repo.spring.io/milestone</url>
115+
</pluginRepository>
116+
</pluginRepositories>
90117

91118
</project>

react-starter-demo.png

293 KB
Loading

spring-boot-reactive-webflux.iml

+71-75
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.reactive.examples.config;
2+
3+
import io.r2dbc.spi.ConnectionFactory;
4+
import org.springframework.context.annotation.Bean;
5+
import org.springframework.context.annotation.Configuration;
6+
import org.springframework.core.io.ClassPathResource;
7+
import org.springframework.data.r2dbc.connectionfactory.init.CompositeDatabasePopulator;
8+
import org.springframework.data.r2dbc.connectionfactory.init.ConnectionFactoryInitializer;
9+
import org.springframework.data.r2dbc.connectionfactory.init.ResourceDatabasePopulator;
10+
11+
@Configuration
12+
public class CustomConnectionFactoryInitializer {
13+
@Bean
14+
public ConnectionFactoryInitializer initializer(ConnectionFactory connectionFactory) {
15+
ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer();
16+
initializer.setConnectionFactory(connectionFactory);
17+
CompositeDatabasePopulator populator = new CompositeDatabasePopulator();
18+
populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("schema.sql")));
19+
initializer.setDatabasePopulator(populator);
20+
return initializer;
21+
}
22+
}

src/main/java/com/reactive/examples/controller/UserController.java

+20-9
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
package com.reactive.examples.controller;
22

3+
import com.reactive.examples.dto.UserDepartmentDTO;
34
import com.reactive.examples.model.User;
4-
import com.reactive.examples.model.UserCapped;
55
import com.reactive.examples.service.UserService;
66
import org.springframework.beans.factory.annotation.Autowired;
77
import org.springframework.http.HttpStatus;
8-
import org.springframework.http.MediaType;
98
import org.springframework.http.ResponseEntity;
109
import org.springframework.web.bind.annotation.*;
1110
import reactor.core.publisher.Flux;
1211
import reactor.core.publisher.Mono;
1312

13+
import java.util.List;
14+
1415
@RestController
1516
@RequestMapping("/users")
1617
public class UserController {
@@ -29,29 +30,39 @@ public Flux<User> getAllUsers(){
2930
}
3031

3132
@GetMapping("/{userId}")
32-
public Mono<ResponseEntity<User>> getUserById(@PathVariable String userId){
33+
public Mono<ResponseEntity<User>> getUserById(@PathVariable Integer userId){
3334
Mono<User> user = userService.findById(userId);
3435
return user.map( u -> ResponseEntity.ok(u))
3536
.defaultIfEmpty(ResponseEntity.notFound().build());
3637
}
3738

3839
@PutMapping("/{userId}")
39-
public Mono<ResponseEntity<User>> updateUserById(@PathVariable String userId, @RequestBody User user){
40+
public Mono<ResponseEntity<User>> updateUserById(@PathVariable Integer userId, @RequestBody User user){
4041
return userService.updateUser(userId,user)
4142
.map(updatedUser -> ResponseEntity.ok(updatedUser))
4243
.defaultIfEmpty(ResponseEntity.badRequest().build());
4344
}
4445

4546
@DeleteMapping("/{userId}")
46-
public Mono<ResponseEntity<Void>> deleteUserById(@PathVariable String userId){
47+
public Mono<ResponseEntity<Void>> deleteUserById(@PathVariable Integer userId){
4748
return userService.deleteUser(userId)
4849
.map( r -> ResponseEntity.ok().<Void>build())
4950
.defaultIfEmpty(ResponseEntity.notFound().build());
5051
}
5152

52-
// Users are Sent to the client as Server Sent Events
53-
@GetMapping(value = "/events", produces = MediaType.APPLICATION_STREAM_JSON_VALUE)
54-
public Flux<UserCapped> streamAllUsers(){
55-
return userService.getStreamedUsers();
53+
@GetMapping("/age/{age}")
54+
public Flux<User> getUsersByAge(@PathVariable int age) {
55+
return userService.findUsersByAge(age);
56+
}
57+
58+
@PostMapping("/search/id")
59+
public Flux<User> fetchUsersByIds(@RequestBody List<Integer> ids) {
60+
return userService.fetchUsers(ids);
5661
}
62+
63+
@GetMapping("/{userId}/department")
64+
public Mono<UserDepartmentDTO> fetchUserAndDepartment(@PathVariable Integer userId){
65+
return userService.fetchUserAndDepartment(userId);
66+
}
67+
5768
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.reactive.examples.dto;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
8+
@Data
9+
@AllArgsConstructor
10+
@Builder
11+
@NoArgsConstructor
12+
public class UserDepartmentDTO {
13+
private Integer userId;
14+
private String userName;
15+
private int age;
16+
private double salary;
17+
private Integer departmentId;
18+
private String departmentName;
19+
private String loc;
20+
}
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
package com.reactive.examples.initialize;
22

3+
import com.reactive.examples.model.Department;
34
import com.reactive.examples.model.User;
4-
import com.reactive.examples.model.UserCapped;
5-
import com.reactive.examples.repository.UserCappedRepository;
5+
import com.reactive.examples.repository.DepartmentRepository;
66
import com.reactive.examples.repository.UserRepository;
77
import lombok.extern.slf4j.Slf4j;
88
import org.springframework.beans.factory.annotation.Autowired;
99
import org.springframework.boot.CommandLineRunner;
1010
import org.springframework.context.annotation.Profile;
11-
import org.springframework.data.mongodb.core.CollectionOptions;
12-
import org.springframework.data.mongodb.core.MongoOperations;
1311
import org.springframework.stereotype.Component;
1412
import reactor.core.publisher.Flux;
1513

16-
import java.time.Duration;
1714
import java.util.Arrays;
1815
import java.util.List;
1916

@@ -26,35 +23,22 @@ public class UserInitializer implements CommandLineRunner {
2623
private UserRepository userRepository;
2724

2825
@Autowired
29-
private UserCappedRepository userCappedRepository;
30-
31-
@Autowired
32-
private MongoOperations mongoOperations;
26+
private DepartmentRepository departmentRepository;
3327

3428
@Override
3529
public void run(String... args) {
3630
initialDataSetup();
37-
createCappedCollection();
38-
dataSetupForCappedCollection();
39-
}
40-
41-
private void createCappedCollection() {
42-
mongoOperations.dropCollection(UserCapped.class);
43-
mongoOperations.createCollection(UserCapped.class, CollectionOptions.empty().maxDocuments(20).size(50000).capped());
4431
}
4532

46-
private void dataSetupForCappedCollection(){
47-
Flux<UserCapped> userCappedFlux = Flux.interval(Duration.ofSeconds(5))
48-
.map(i -> new UserCapped(null,"Stream User " + i,20,1000));
49-
userCappedRepository.insert(userCappedFlux).subscribe(
50-
item -> log.info("UserCapped Inserted from CommandLineRunner " + item));
51-
33+
private List<User> getData(){
34+
return Arrays.asList(new User(null,"Suman Das",30,10000),
35+
new User(null,"Arjun Das",5,1000),
36+
new User(null,"Saurabh Ganguly",40,1000000));
5237
}
5338

54-
private List<User> getData(){
55-
return Arrays.asList(new User("1","Suman Das",30,10000),
56-
new User("2","Arjun Das",5,1000),
57-
new User("3","Saurabh Ganguly",40,1000000));
39+
private List<Department> getDepartments(){
40+
return Arrays.asList(new Department(null,"Mechanical",1,"Mumbai"),
41+
new Department(null,"Computer",2,"Bangalore"));
5842
}
5943

6044
private void initialDataSetup() {
@@ -65,5 +49,15 @@ private void initialDataSetup() {
6549
.subscribe(user -> {
6650
log.info("User Inserted from CommandLineRunner " + user);
6751
});
52+
53+
departmentRepository.deleteAll()
54+
.thenMany(Flux.fromIterable(getDepartments()))
55+
.flatMap(departmentRepository::save)
56+
.thenMany(departmentRepository.findAll())
57+
.subscribe(user -> {
58+
log.info("Department Inserted from CommandLineRunner " + user);
59+
});
60+
6861
}
62+
6963
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.reactive.examples.model;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Data;
5+
import lombok.NoArgsConstructor;
6+
import org.springframework.data.annotation.Id;
7+
import org.springframework.data.relational.core.mapping.Column;
8+
import org.springframework.data.relational.core.mapping.Table;
9+
10+
@Data
11+
@AllArgsConstructor
12+
@NoArgsConstructor
13+
@Table("department")
14+
public class Department {
15+
@Id
16+
private Integer id;
17+
private String name;
18+
@Column("user_id")
19+
private Integer userId;
20+
private String loc;
21+
}

src/main/java/com/reactive/examples/model/User.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@
44
import lombok.Data;
55
import lombok.NoArgsConstructor;
66
import org.springframework.data.annotation.Id;
7-
import org.springframework.data.mongodb.core.mapping.Document;
7+
import org.springframework.data.relational.core.mapping.Table;
8+
89

9-
@Document
1010
@Data
1111
@AllArgsConstructor
1212
@NoArgsConstructor
13+
@Table("users")
1314
public class User {
15+
1416
@Id
15-
private String id;
17+
private Integer id;
1618
private String name;
1719
private int age;
1820
private double salary;

src/main/java/com/reactive/examples/model/UserCapped.java

-19
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.reactive.examples.repository;
2+
3+
import com.reactive.examples.model.Department;
4+
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
5+
import reactor.core.publisher.Mono;
6+
7+
public interface DepartmentRepository extends ReactiveCrudRepository<Department,Integer> {
8+
Mono<Department> findByUserId(Integer userId);
9+
}

src/main/java/com/reactive/examples/repository/UserCappedRepository.java

-11
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package com.reactive.examples.repository;
22

33
import com.reactive.examples.model.User;
4-
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
4+
import org.springframework.data.r2dbc.repository.Query;
5+
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
6+
import reactor.core.publisher.Flux;
57

6-
public interface UserRepository extends ReactiveMongoRepository<User,String> {
8+
public interface UserRepository extends ReactiveCrudRepository<User,Integer> {
9+
@Query("select * from users where age >= $1")
10+
Flux<User> findByAge(int age);
711
}

0 commit comments

Comments
 (0)