Skip to content

Commit

Permalink
feat: refactor pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
rajadilipkolli committed Oct 1, 2023
1 parent 0beaad8 commit b29a238
Show file tree
Hide file tree
Showing 17 changed files with 386 additions and 192 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@
import com.example.jooq.r2dbc.config.logging.Loggable;
import com.example.jooq.r2dbc.model.response.PostCommentResponse;
import com.example.jooq.r2dbc.model.response.PostResponse;
import com.example.jooq.r2dbc.testcontainersflyway.db.tables.records.PostCommentsRecord;
import com.example.jooq.r2dbc.testcontainersflyway.db.tables.records.PostsRecord;
import com.example.jooq.r2dbc.testcontainersflyway.db.tables.records.PostsTagsRecord;
import com.example.jooq.r2dbc.testcontainersflyway.db.tables.records.TagsRecord;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jooq.DSLContext;
import org.jooq.DeleteUsingStep;
import org.jooq.Record1;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
Expand All @@ -29,12 +34,24 @@ public class Initializer implements CommandLineRunner {
@Loggable
public void run(String... args) {
log.info("Running Initializer.....");
Mono.from(
dslContext
.insertInto(POSTS)
.columns(POSTS.TITLE, POSTS.CONTENT)
.values("jooq test", "content of Jooq test")
.returningResult(POSTS.ID))
DeleteUsingStep<PostsTagsRecord> postsTagsRecordDeleteUsingStep =
dslContext.deleteFrom(POSTS_TAGS);
DeleteUsingStep<TagsRecord> tagsRecordDeleteUsingStep = dslContext.deleteFrom(TAGS);
DeleteUsingStep<PostCommentsRecord> postCommentsRecordDeleteUsingStep =
dslContext.deleteFrom(POST_COMMENTS);
DeleteUsingStep<PostsRecord> postsRecordDeleteUsingStep = dslContext.deleteFrom(POSTS);

Mono.from(postsTagsRecordDeleteUsingStep)
.then(Mono.from(tagsRecordDeleteUsingStep))
.then(Mono.from(postCommentsRecordDeleteUsingStep))
.then(Mono.from(postsRecordDeleteUsingStep))
.then(
Mono.from(
dslContext
.insertInto(POSTS)
.columns(POSTS.TITLE, POSTS.CONTENT)
.values("jooq test", "content of Jooq test")
.returningResult(POSTS.ID)))
.flatMap(
postId ->
Mono.from(
Expand Down Expand Up @@ -71,8 +88,7 @@ public void run(String... args) {
pid.component1(),
"test comments 2")
.returningResult(POST_COMMENTS.ID))
.collectList() // Collect all comment IDs into a list
)
.collectList())
.thenMany(
dslContext
.select(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.example.jooq.r2dbc.model.response.PaginatedResult;
import com.example.jooq.r2dbc.model.response.PostSummary;
import com.example.jooq.r2dbc.service.PostService;
import com.example.jooq.r2dbc.utils.AppConstants;
import java.net.URI;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
Expand Down Expand Up @@ -65,16 +66,22 @@ public Mono<ServerResponse> update(ServerRequest req) {

@Loggable
public Mono<ServerResponse> search(ServerRequest req) {
String sortDir = req.queryParam("sortDir").orElse("asc");
String sortBy = req.queryParam("sortBy").orElse("id");
String sortDir = req.queryParam("sortDir").orElse(AppConstants.DEFAULT_SORT_DIRECTION);
String sortBy = req.queryParam("sortBy").orElse(AppConstants.DEFAULT_SORT_BY);
Sort sort =
sortDir.equalsIgnoreCase(Sort.Direction.ASC.name())
? Sort.by(sortBy).ascending()
: Sort.by(sortBy).descending();

// create Pageable instance
int pageNo = req.queryParam("pageNo").map(Integer::parseInt).orElse(0);
int pageSize = req.queryParam("pageSize").map(Integer::parseInt).orElse(10);
int pageNo =
req.queryParam("pageNo")
.map(Integer::parseInt)
.orElse(AppConstants.DEFAULT_PAGE_NUMBER);
int pageSize =
req.queryParam("pageSize")
.map(Integer::parseInt)
.orElse(AppConstants.DEFAULT_PAGE_SIZE);
Pageable pageable = PageRequest.of(pageNo, pageSize, sort);
return postService
.findByKeyword(req.queryParam("keyword").orElse(""), pageable)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,61 @@
import com.example.jooq.r2dbc.config.logging.Loggable;
import com.example.jooq.r2dbc.entities.Tags;
import com.example.jooq.r2dbc.model.request.TagDto;
import com.example.jooq.r2dbc.model.response.PaginatedResult;
import com.example.jooq.r2dbc.service.TagService;
import com.example.jooq.r2dbc.utils.AppConstants;
import java.net.URI;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

@Component
@RequiredArgsConstructor
@Loggable
public class TagHandler {

// Service responsible for handling Tag-related business logic
private final TagService tagService;

@Loggable
// Retrieve all tags based on query parameters for sorting and pagination
public Mono<ServerResponse> getAll(ServerRequest req) {
return ok().body(this.tagService.findAll(), Tags.class);
// Extracting and setting sort direction and field from query parameters
String sortDir = req.queryParam("sortDir").orElse(AppConstants.DEFAULT_SORT_DIRECTION);
String sortBy = req.queryParam("sortBy").orElse(AppConstants.DEFAULT_SORT_BY);
Sort sort =
sortDir.equalsIgnoreCase(Sort.Direction.ASC.name())
? Sort.by(sortBy).ascending()
: Sort.by(sortBy).descending();

// Creating Pageable instance for pagination
int pageNo =
req.queryParam("pageNo")
.map(Integer::parseInt)
.orElse(AppConstants.DEFAULT_PAGE_NUMBER);
int pageSize =
req.queryParam("pageSize")
.map(Integer::parseInt)
.orElse(AppConstants.DEFAULT_PAGE_SIZE);
Pageable pageable = PageRequest.of(pageNo, pageSize, sort);

// Returning paginated result of tags
return ok().body(this.tagService.findAll(pageable), PaginatedResult.class);
}

@Loggable
// Retrieve a specific tag by its ID
public Mono<ServerResponse> get(ServerRequest req) {
return this.tagService
.findById(req.pathVariable("id"))
.flatMap(tags -> ServerResponse.ok().body(Mono.just(tags), Tags.class))
.switchIfEmpty(ServerResponse.notFound().build());
}

@Loggable
// Create a new tag based on the request body
public Mono<ServerResponse> create(ServerRequest req) {
return req.bodyToMono(TagDto.class)
.flatMap(this.tagService::create)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
package com.example.jooq.r2dbc.repository;

import com.example.jooq.r2dbc.entities.Post;
import com.example.jooq.r2dbc.repository.custom.CustomPostRepository;
import java.util.UUID;
import org.springframework.data.r2dbc.repository.Query;
import org.springframework.data.r2dbc.repository.R2dbcRepository;
import reactor.core.publisher.Flux;

public interface PostRepository extends R2dbcRepository<Post, UUID> {

@Query("SELECT * FROM posts where title like :title")
public Flux<Post> findByTitleContains(String title);
}
public interface PostRepository extends R2dbcRepository<Post, UUID>, CustomPostRepository {}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.example.jooq.r2dbc.repository;

import com.example.jooq.r2dbc.entities.Tags;
import com.example.jooq.r2dbc.repository.custom.CustomTagRepository;
import java.util.UUID;
import org.springframework.data.r2dbc.repository.R2dbcRepository;

public interface TagRepository extends R2dbcRepository<Tags, UUID> {}
public interface TagRepository extends R2dbcRepository<Tags, UUID>, CustomTagRepository {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example.jooq.r2dbc.repository.custom;

import com.example.jooq.r2dbc.model.response.PostResponse;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import reactor.core.publisher.Mono;

public interface CustomPostRepository {
Mono<Page<PostResponse>> findByKeyword(String keyword, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.jooq.r2dbc.repository.custom;

import com.example.jooq.r2dbc.entities.Tags;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import reactor.core.publisher.Mono;

public interface CustomTagRepository {

Mono<Page<Tags>> findAll(Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.example.jooq.r2dbc.repository.custom.impl;

import static com.example.jooq.r2dbc.testcontainersflyway.db.Tables.*;
import static com.example.jooq.r2dbc.testcontainersflyway.db.Tables.POSTS;
import static org.jooq.impl.DSL.multiset;
import static org.jooq.impl.DSL.select;

import com.example.jooq.r2dbc.model.response.PostCommentResponse;
import com.example.jooq.r2dbc.model.response.PostResponse;
import com.example.jooq.r2dbc.repository.custom.CustomPostRepository;
import com.example.jooq.r2dbc.testcontainersflyway.db.tables.PostComments;
import com.example.jooq.r2dbc.testcontainersflyway.db.tables.Posts;
import org.jooq.Condition;
import org.jooq.DSLContext;
import org.jooq.Record1;
import org.jooq.impl.DSL;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public class CustomPostRepositoryImpl extends JooqSorting implements CustomPostRepository {

private final DSLContext dslContext;

public CustomPostRepositoryImpl(DSLContext dslContext) {
this.dslContext = dslContext;
}

@Override
public Mono<Page<PostResponse>> findByKeyword(String keyword, Pageable pageable) {
Condition where = DSL.trueCondition();
if (StringUtils.hasText(keyword)) {
where = where.and(POSTS.TITLE.likeIgnoreCase("%" + keyword + "%"));
}
var dataSql =
dslContext
.select(
POSTS.ID,
POSTS.TITLE,
POSTS.CONTENT,
multiset(
select(
PostComments.POST_COMMENTS.ID,
PostComments.POST_COMMENTS.CONTENT,
PostComments.POST_COMMENTS
.CREATED_AT)
.from(PostComments.POST_COMMENTS)
.where(
PostComments.POST_COMMENTS.POST_ID
.eq(Posts.POSTS.ID)))
.as("comments")
.convertFrom(
record3s ->
record3s.into(PostCommentResponse.class)),
multiset(
select(TAGS.NAME)
.from(TAGS)
.join(POSTS_TAGS)
.on(TAGS.ID.eq(POSTS_TAGS.TAG_ID))
.where(POSTS_TAGS.POST_ID.eq(POSTS.ID)))
.as("tags")
.convertFrom(record -> record.map(Record1::value1)))
.from(POSTS.leftJoin(POST_COMMENTS).on(POST_COMMENTS.POST_ID.eq(POSTS.ID)))
.where(where)
.groupBy(POSTS.ID)
.orderBy(getSortFields(pageable.getSort(), POSTS))
.limit(pageable.getPageSize())
.offset(pageable.getOffset());

var countSql = dslContext.selectCount().from(POSTS).where(where);

return Mono.zip(
Flux.from(dataSql)
.map(
r ->
new PostResponse(
r.value1(),
r.value2(),
r.value3(),
r.value4(),
r.value5()))
.collectList(),
Mono.from(countSql).map(Record1::value1))
.map(it -> new PageImpl<>(it.getT1(), pageable, it.getT2()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.example.jooq.r2dbc.repository.custom.impl;

import static com.example.jooq.r2dbc.testcontainersflyway.db.Tables.TAGS;

import com.example.jooq.r2dbc.entities.Tags;
import com.example.jooq.r2dbc.repository.custom.CustomTagRepository;
import org.jooq.*;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public class CustomTagRepositoryImpl extends JooqSorting implements CustomTagRepository {

private final DSLContext dslContext;

public CustomTagRepositoryImpl(DSLContext dslContext) {
this.dslContext = dslContext;
}

@Override
public Mono<Page<Tags>> findAll(Pageable pageable) {
var dataSql =
dslContext
.select(TAGS.ID, TAGS.NAME)
.from(TAGS)
.orderBy(getSortFields(pageable.getSort(), TAGS))
.limit(pageable.getPageSize())
.offset(pageable.getOffset());
var countSql = dslContext.selectCount().from(TAGS);
return Mono.zip(
Flux.from(dataSql).map(r -> new Tags(r.value1(), r.value2())).collectList(),
Mono.from(countSql).map(Record1::value1))
.map(it -> new PageImpl<>(it.getT1(), pageable, it.getT2()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.example.jooq.r2dbc.repository.custom.impl;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.jooq.SortField;
import org.jooq.TableField;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.domain.Sort;

public class JooqSorting {

<T> List<SortField<?>> getSortFields(Sort sortSpecification, T tableType) {
List<SortField<?>> querySortFields = new ArrayList<>();

if (sortSpecification == null) {
return querySortFields;
}

for (Sort.Order specifiedField : sortSpecification) {
String sortFieldName = specifiedField.getProperty();
Sort.Direction sortDirection = specifiedField.getDirection();

TableField tableField = getTableField(sortFieldName, tableType);
SortField<?> querySortField = convertTableFieldToSortField(tableField, sortDirection);
querySortFields.add(querySortField);
}

return querySortFields;
}

private <T> TableField getTableField(String sortFieldName, T tableType) {
TableField sortField;
try {
Field tableField =
tableType.getClass().getField(sortFieldName.toUpperCase(Locale.ROOT));
sortField = (TableField) tableField.get(tableType);
} catch (NoSuchFieldException | IllegalAccessException ex) {
String errorMessage = String.format("Could not find table field: %s", sortFieldName);
throw new InvalidDataAccessApiUsageException(errorMessage, ex);
}
return sortField;
}

private SortField<?> convertTableFieldToSortField(
TableField tableField, Sort.Direction sortDirection) {
if (sortDirection == Sort.Direction.ASC) {
return tableField.asc();
} else {
return tableField.desc();
}
}
}
Loading

0 comments on commit b29a238

Please sign in to comment.