Skip to content

Latest commit

 

History

History
191 lines (163 loc) · 4.93 KB

File metadata and controls

191 lines (163 loc) · 4.93 KB

R2DBC Generic Specification

This repository provides a generic specification for querying entities reactively using Spring Data R2DBC. It supports filtering, projection, pagination, and relational data fetching.

Adding This Library

To use this library in your project, add the following dependency to your pom.xml (for Maven):

<dependency>
    <groupId>io.kmaker</groupId>
    <artifactId>r2dbc-specification</artifactId>
    <version>1.0.0</version>
</dependency>

Or if you are using Gradle:

dependencies {
    implementation 'io.kmaker:r2dbc-generic-specification:1.0.0'
}

Example Entity

Book Entity

import org.springframework.data.relational.core.mapping.Column;

@Table("book")
public class Book {
    @Id
    private Long id;
    private String title;
    private String genre;
    @Column("publication_year")
    private int publicationYear;
    @Column("available_copies")
    private int availableCopies;
    @Column("author_id")
    private Long authorId;
    // Getters and Setters
}

Author Entity

@Table("Author")
public class Author {
    @Id
    private Long id;
    private String name;
    @Column("birth_year")
    private int birthYear;
    // Getters and Setters
}

Member Entity

@Table("member")
public class Member {
    @Id
    private Integer id;
    private String fullName;
    private String email;
    private String phoneNumber;
    // Getters and Setters
}

Example DTO for Projection

BookInfoDto

public class BookInfoDto {
    private Integer id;
    private String title;
    private String genre;
    private Integer publicationYear;
    // Getters and Setters
}

BookDetailDto (With Author Information)

public class BookDetailDto {
    private Long id;
    private String title;
    private String genre;
    private int publicationYear;
    private int availableCopies;
    @FetchRelatedEntity(
            relatedEntity = Author.class,
            foreignKey = "author_id",
            dto = AuthorDto.class,
            joinType = FetchRelatedEntity.JOIN_TYPE.JOIN,
            type = FetchRelatedEntity.RelationType.ONE_TO_ONE
    )
    private AuthorDto authorDto;
    // Getters and Setters
}

BorrowingInfoDto (With Book and Member Relations)

public class BorrowingInfoDto {
    private Long id;
    @FetchRelatedEntity(
            relatedEntity = Book.class,
            dto = BookWithIdAndTitle.class,
            foreignKey = "book_id",
            joinType = FetchRelatedEntity.JOIN_TYPE.JOIN,
            type = FetchRelatedEntity.RelationType.ONE_TO_ONE
    )
    private BookWithIdAndTitle book;
    @FetchRelatedEntity(
            relatedEntity = Member.class,
            dto = MemberIdAndFullname.class,
            foreignKey = "member_id",
            joinType = FetchRelatedEntity.JOIN_TYPE.JOIN,
            type = FetchRelatedEntity.RelationType.ONE_TO_ONE
    )
    private MemberIdAndFullname member;
    private LocalDate borrowDate;
    private LocalDate returnDate;
    // Getters and Setters
}

Example Code

Finding an Entity by Specification

var idSpec = Criteria.where("id").is(2);
var bookMono = r2dbcGenericSpecification.findOneBySpec(idSpec, Book.class, BookInfoDto.class);

Finding an Entity by Specification with Relations

var idSpec = Criteria.where("book.id").is(10);
var bookDetailMono = r2dbcGenericSpecification.findOneBySpecWithRel(idSpec, Book.class, BookDetailDto.class);

Paginated Query with Relations

var emptySpec = Criteria.empty();
var pageable = PageRequest.of(0, 3, Sort.Direction.ASC, "borrowing.id");
var borrowingPageMono = r2dbcGenericSpecification.getPageBySpecWithRel(emptySpec, pageable, Borrowing.class, BorrowingInfoDto.class);

Paginated Query Without Relations

var pageable = PageRequest.of(0, 30, Sort.Direction.DESC, "member.id");
var memberSpec = Criteria.empty();
var memberPageMono = r2dbcGenericSpecification.getPageBySpec(memberSpec, pageable, Member.class, Member.class);

Learn more about Criteria method

Ignore Field Mapping

In some cases, we may have fields in a DTO (Data Transfer Object) class that do not exist in the database. To prevent errors when mapping the fields to an SQL query, we can use the @IgnoreMapping annotation. This annotation tells the system to ignore the field during the mapping process.

Example:

import io.kmaker.r2dbcspecification.annotation.IgnoreMapping;

public class BookInfoDto {
    private Integer id;
    private String title;
    private String genre;
    private Integer publicationYear;

    // // This field does not exist in the database, so we mark it to be ignored during mapping
    @IgnoreMapping
    private String doesNotExist;
    // Getters and Setters
}

Running Tests

To run the test cases, execute:

./mvnw test