krishnakumarp
krishnakumarp

Reputation: 9295

Spring Data Reactive R2DBC Pagination

Spring Data Reactive does not support Page as a return type. I'm trying to implement this using two different queries (one to get result list and another to get total count) from Service layer.

The first query gives the contents as Flux<Person> and the second query gives the count as Mono<Integer>.

Can I combine these two and return a Mono<Page<Person>>? How?

Please help.

Upvotes: 4

Views: 4315

Answers (1)

krishnakumarp
krishnakumarp

Reputation: 9295

So finally I coded this as below.

I'm new to this whole reactive paradigm. So the below code may not be the best way to do this. Eventually, I hope Spring Data R2DBC adds support for Page return type. The answer to the above question is in the search method implementation in the CustomMentorRepositoryImpl class.

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Version;
import org.springframework.data.relational.core.mapping.Column;
import org.springframework.data.relational.core.mapping.Table;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Table("mentors")
public class Mentor {

    @Id
    private Long id;
    boolean hasId() {
        return id != null;
    }

    @Version
    @Column("version")
    private Integer version;

    @Column("name")
    private String name;

    @Column("age")
    private Integer age;

}

Repository class

import com.sample.app.domain.Mentor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.r2dbc.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Repository
public interface MentorRepository extends 
    ReactiveCrudRepository<Mentor, String>, CustomMentorRepository {    
}

// CustomRepository class

import com.sample.app.domain.Mentor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.query.Param;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public interface CustomMentorRepository {
    public Mono<Page<Mentor>> search(String name, Pageable page);
}

// CustomRepositoryImpl class

import lombok.AllArgsConstructor;
import com.sample.app.domain.Mentor;
import com.sample.app.repository.CustomMentorRepository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.r2dbc.core.DatabaseClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@AllArgsConstructor
public class CustomMentorRepositoryImpl implements CustomMentorRepository {

    DatabaseClient databaseClient;

    @Override
    public Mono<Page<Mentor>> search(String name, Pageable page) {
        // Get the contents
        Flux<Mentor> contents = databaseClient.execute("SELECT * FROM mentors where name like :name limit :limit offset :offset")
                .bind("name", "%" + name + "%")
                .bind("limit", page.getPageSize())
                .bind("offset", page.getOffset())
                .as(Mentor.class)
                .fetch().all();
        // Get the count
        Mono<Long> count = databaseClient.execute("SELECT count(*) FROM mentors where name like :name")
                .bind("name", "%" + name + "%")
                .as(Long.class)
                .fetch().first();
        return contents.collectList().zipWith(count).flatMap(objects ->
                Mono.just(new PageImpl<Mentor>(objects.getT1(), page, objects.getT2()))
        );
    }
}

Upvotes: 2

Related Questions