infobip / infobip-spring-data-querydsl

Infobip Spring Data Querydsl provides new functionality that enables the user to leverage the full power of Querydsl API on top of Spring Data repository infrastructure.
Apache License 2.0
280 stars 57 forks source link

someway to define my own repository #105

Open Peak-Song opened 4 hours ago

Peak-Song commented 4 hours ago

Thanks for your amazing project, I feels exceptionally smooth when using r2dbc and querydsl, so I strongly recommend it to my colleagues. However, I have a small question I'd like to ask you.

I want to extend the origin Repository to implement my own Repository, such as ReactivePagingRepository below.

package com.it.gm.cube.app.base

import com.it.gm.cube.bdpc.common.PageResult
import com.it.gm.cube.bdpc.common.PageVo
import com.querydsl.core.types.OrderSpecifier
import com.querydsl.core.types.Predicate
import com.querydsl.sql.RelationalPathBase
import org.springframework.data.repository.NoRepositoryBean
import org.springframework.data.repository.Repository
import reactor.core.publisher.Mono

@NoRepositoryBean
interface ReactivePagingRepository<T, ID>: Repository<T, ID> {

    fun <T> simplePaging(
        pageVo: PageVo,
        count: Long,
        table: RelationalPathBase<T>,
        predication: Predicate,
        orderBy: OrderSpecifier<String>
    ): Mono<PageResult<T>>
}

And Then implement the interface with

package com.it.gm.cube.app.base

import com.infobip.spring.data.r2dbc.QuerydslR2dbcFragment
import com.it.gm.cube.bdpc.common.PageResult
import com.it.gm.cube.bdpc.common.PageVo
import com.it.gm.cube.bdpc.common.PagingUtil
import com.querydsl.core.types.OrderSpecifier
import com.querydsl.core.types.Predicate
import com.querydsl.sql.RelationalPathBase
import org.springframework.context.ApplicationContext
import org.springframework.stereotype.Component
import reactor.core.publisher.Mono

@Component
class ReactivePagingRepositoryBean<T, ID>(private val context: ApplicationContext) :
    ReactivePagingRepository<T, ID> {

    override fun <T> simplePaging(
        pageVo: PageVo,
        count: Long,
        table: RelationalPathBase<T>,
        predication: Predicate,
        orderBy: OrderSpecifier<String>
    ): Mono<PageResult<T>> {

        val bean = context.getBean(QuerydslR2dbcFragment::class.java)

        return if (count == 0L)
            Mono.just(PageResult<T>(0, listOf<T>(), 1))
        else {
            val pagingInfo = PagingUtil.limitOffset(pageVo, count)
            bean.query { query ->
                query.select(table).from(table).where(predication)
                    .orderBy(orderBy)
                    .limit(pagingInfo.limit).offset(pagingInfo.offset)
            }.all().collectList().map { PageResult<T>(count, it as List<T>, pagingInfo.currentPage) }
        }

    }
}

Finally, I use it like this


@Configuration
@EnableR2dbcRepositories(repositoryBaseClass = ReactivePagingRepositoryBean::class)
class DbConfiguration {

    private val log by LoggerDelegate()

    @Bean
    fun sqlTemplates(): SQLTemplates {
        return APostgreSQLTemplates()
    }

}

@NoRepositoryBean
interface QuerydslRepository<T, ID> : ReactiveSortingRepository<T, ID>, ReactiveCrudRepository<T, ID>,
    ReactiveQuerydslPredicateExecutor<T>, ReactivePagingRepository<T, ID>, QuerydslR2dbcFragment<T>

@Configuration
@EnableR2dbcRepositories(repositoryBaseClass = ReactivePagingRepositoryBean::class)
class DbConfiguration {}

interface ApiTreeRepository: QuerydslRepository<ApiTree, Long>

But it raise exception whrn project start. Caused by: org.springframework.data.mapping.PropertyReferenceException: No property 'simplePaging' found for type 'ApiTree'

there is the project dependency I use -- springboot3.3.3 and

    implementation("org.postgresql:r2dbc-postgresql:1.0.5.RELEASE")
    implementation("org.springframework.boot:spring-boot-starter-data-r2dbc")
    implementation("com.infobip:infobip-spring-data-r2dbc-querydsl-boot-starter:9.0.7")
    compileOnly("com.infobip:infobip-spring-data-jdbc-annotation-processor:9.0.7")
    kapt("com.infobip:infobip-spring-data-jdbc-annotation-processor:9.0.7")

Is there Anyway to achieve this feature? Please tell me, thanks a lot~

lpandzic commented 1 hour ago

Hi, thank you for your kind words. I'd suggest using the fragment (custom repository approach) - https://docs.spring.io/spring-data/jpa/reference/repositories/custom-implementations.html

public interface ReactivePagingRepository<T> {

    Mono<T> simplePaging(String string);
}
import org.springframework.context.annotation.Lazy;
import reactor.core.publisher.Mono;

public class ReactivePagingRepositoryImpl<T> implements ReactivePagingRepository<T> {

    private final QuerydslR2dbcFragment querydslR2dbcFragment;

    public ReactivePagingRepositoryImpl(@Lazy QuerydslR2dbcFragment querydslR2dbcFragment) {
        this.querydslR2dbcFragment = querydslR2dbcFragment;
    }

    @Override
    public Mono<T> simplePaging(... parameters) {
        ... implementation
    }
}

This is implementation side, on use side you need to do something like

public interface PersonRepository extends QuerydslR2dbcRepository<Person, Long>, ReactivePagingRepository<Person> {
}

You can of course introduce another abstract interface that extends QuerydslR2dbcRepository<T, ID>, ReactivePagingRepository<T> like QuerydslR2dbcRepository does so your concrete repositories doesn't have to extend both but this approach is arbitrary. To keep the repository implementation clean I'd suggest keeping the repository hierarchies as separate as possible.