Reputation: 203
I am using web flux in my project and am trying to do simple CRUD operations using JPA repository. However I' unable to persist the object in the DB. The object is always being persisted with null values instead in the DB. Please help. Thanks in advance.
Here is my pojo:
@Entity
@Table(name="tbl_student")
@Data
public class Student {
@Id
@GeneratedValue(strategy= GenerationType.SEQUENCE,generator = "student_seq")
@SequenceGenerator(name="student_seq",allocationSize = 1)
@Column(name="id",insertable =false,nullable = false,updatable = false)
private Long id;
@Column(name="name")
private String name;
@Column(name="school")
private String school;
}
My Repo:
public interface StudentRepository extends JpaRepository<Student,Long> {
}
My controller:
@RestController
@RequiredArgsConstructor
@Slf4j
public class StudentApiControllerImpl extends StudentApiController {
private final StudentRepository StudentRepository;
private final ModelMapper modelMapper;
public Mono<Void> addStudent(@Valid @RequestBody(required = false) Mono<StudentDetails> studentDetails, ServerWebExchange exchange) {
StudentRepository.save(modelMapper.map(studentDetails, StudentDTO.class));
return Mono.empty();
}
public Flux<StudentDetails> getStudent(ServerWebExchange exchange) {
return Flux.fromStream(StudentRepository.findAll().stream().map(v ->
modelMapper.map(v,LoginCredential.class))).subscribeOn(Schedulers.boundedElastic());
}
}
Upvotes: 2
Views: 2401
Reputation: 14732
You are breaking the reactive chain, thats why nothing happens.
Reactive programming is a lot different than standard java programming so you can't just do what you have done before and think that it will work the same.
In reactive programming one of the most important things to understand is nothing happens until you subscribe
A reactive chain is build by a producer
and a subscriber
which means someone produces (your application) and someone subscribes (the calling client/application). A Flux/Mono
is a producer and nothing will happen until someone subscribes.
// Nothing happens, this is just a declaration
Mono.just("Foobar");
But when we subscribe:
// This will print FooBar
Mono.just("Foobar").subscribe(s -> System.out.println(s));
So if we look at your code, especially this line
// This is just a declaration, no one is subscribing, so nothing will happen
StudentRepository.save(modelMapper.map(studentDetails, StudentDTO.class));
A common misconception is that people will solve this by just subscribing.
// This is in most cases wrong
StudentRepository.save(modelMapper.map(studentDetails, StudentDTO.class)).subscribe();
Because the subscriber is the calling client, the one that initiated the call. Doing as such might lead to very bad performance under heavier loads. Instead what you do is that you need to return the Mono out to the client, so that the client can subscribe.
public Mono<Void> addStudent(@Valid @RequestBody(required = false) Mono<StudentDetails> studentDetails, ServerWebExchange exchange) {
return StudentRepository.save(modelMapper.map(studentDetails, StudentDTO.class))
.then();
}
I am using Mono#then here to throw away the return value and just return a void value to the calling client.
But as you can see, you need to think about it like callbacks, you need to always return so it will build a chain that gets returned to the calling client, so that the client can subscribe.
I highly suggest you read the reactive documentation so you understand the core concepts before starting out with reactive programming.
Reactive programming getting started
Also, another thing, you can not use JpaRepository
in a reactive world because it is using a blocking
database driver. Which means basically it is not written to work well with Webflux and will give very poor performance. If you want to use a database connection i suggest you look into using R2DBC by spring here is a tutuorial to get it up and running R2DBC getting started
Update:
i see now that you have placed the entire call on its own scheduler
which is good since you are using JPA and that will result in less bad performance. But i do recommend still, if possible, to look into using R2DBC for a fully reactive application.
Also, if you insist on using JPA
with a blocking database driver, you need to perform your call in a reactive context, since it will return a concrete value. So for instance:
return Mono.fromCallable(() -> StudentRepository.save(modelMapper.map(studentDetails, StudentDTO.class)))
.then()
Which means we are basically executing our function and immediately placing the returned type into a Mono
, and then as the above example using Mono#then
to discard the return value and just signal that the execution has completed.
Upvotes: 1