From 5b1c5079c6f11f625cbedf5a47c10c1506f9cb04 Mon Sep 17 00:00:00 2001 From: ririnto Date: Sun, 17 Nov 2024 19:53:48 +0900 Subject: [PATCH 1/7] style: remove unnecessary whitespace char --- docs/ko/jpql-with-kotlin-jdsl/migration-2.x-to-3.x.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/ko/jpql-with-kotlin-jdsl/migration-2.x-to-3.x.md b/docs/ko/jpql-with-kotlin-jdsl/migration-2.x-to-3.x.md index 52814f9f7..72c7078e3 100644 --- a/docs/ko/jpql-with-kotlin-jdsl/migration-2.x-to-3.x.md +++ b/docs/ko/jpql-with-kotlin-jdsl/migration-2.x-to-3.x.md @@ -1,3 +1,4 @@ # Migration 2.X to 3.X -Kotlin JDSL 2.X와 3.X는 둘 사이에 패키지 충돌이 없기 때문에 하나의 어플리케이션에서 같이 사용할 수 있습니다. 그래서 신규로 작성하는 쿼리는 Kotlin JDSL 3.X로 작성하고 점진적으로 Kotlin JDSL 2.X로 작성된 쿼리를 Kotlin JDSL 3.X로 옮겨가면 됩니다. +Kotlin JDSL 2.X와 3.X는 둘 사이에 패키지 충돌이 없기 때문에 하나의 어플리케이션에서 같이 사용할 수 있습니다. +그래서 신규로 작성하는 쿼리는 Kotlin JDSL 3.X로 작성하고 점진적으로 Kotlin JDSL 2.X로 작성된 쿼리를 Kotlin JDSL 3.X로 옮겨가면 됩니다. From 492da8497e3c465efbd03b0cd2956a23c9163241 Mon Sep 17 00:00:00 2001 From: ririnto Date: Sun, 17 Nov 2024 19:58:36 +0900 Subject: [PATCH 2/7] docs: fix typo in documentation --- docs/ko/jpql-with-kotlin-jdsl/predicates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ko/jpql-with-kotlin-jdsl/predicates.md b/docs/ko/jpql-with-kotlin-jdsl/predicates.md index d4af8c089..615ffa714 100644 --- a/docs/ko/jpql-with-kotlin-jdsl/predicates.md +++ b/docs/ko/jpql-with-kotlin-jdsl/predicates.md @@ -12,7 +12,7 @@ Kotlin JDSL은 JPQL의 conditional expression을 표현하기 위해 `Predicate` {% hint style="info" %} 만약 `and()` 와 `or()`로 넘어온 `Predicate`가 모두 null 이거나 비어 있으면, `and()`의 경우에는 `1 = 1`로 `or()`의 경우에는 `0 = 1`로 해석됩니다. -그렇기 떄문에 다이나믹 쿼리를 만들 때 조심해야 합니다. +그렇기 때문에 다이나믹 쿼리를 만들 때 조심해야 합니다. {% endhint %} ```kotlin From 3f069b8b290d9a4a1b8136c0c6bcfb468a8e83bb Mon Sep 17 00:00:00 2001 From: ririnto Date: Sun, 17 Nov 2024 20:00:22 +0900 Subject: [PATCH 3/7] docs: improve wording for clarity --- docs/ko/jpql-with-kotlin-jdsl/predicates.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/ko/jpql-with-kotlin-jdsl/predicates.md b/docs/ko/jpql-with-kotlin-jdsl/predicates.md index 615ffa714..350dae935 100644 --- a/docs/ko/jpql-with-kotlin-jdsl/predicates.md +++ b/docs/ko/jpql-with-kotlin-jdsl/predicates.md @@ -168,8 +168,9 @@ function(Boolean::class, "myFunction", path(Book::isbn)) ``` {% hint style="info" %} -사용할 함수의 정보를 JPA 제공자에 등록할 필요가 있을 수 있습니다. -예를 들어 Hibernate를 사용하고 있다면 `FunctionContributor`를 반드시 등록해야 합니다. +사용하려는 함수에 대한 정보를 JPA Provider에 등록해야 할 수도 있습니다. + +예를 들어, Hibernate를 사용하고 있다면 `FunctionContributor`를 반드시 등록해야 합니다. {% endhint %} ## Custom predicate From f89ef67d95fdccf255a9b7bba8c17b732a3b259e Mon Sep 17 00:00:00 2001 From: ririnto Date: Sun, 17 Nov 2024 20:01:09 +0900 Subject: [PATCH 4/7] style: adjust indentation to match EditorConfig --- docs/en/SUMMARY.md | 20 ++++++++++---------- docs/ko/SUMMARY.md | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/en/SUMMARY.md b/docs/en/SUMMARY.md index 5b324fef7..e6fc808c4 100644 --- a/docs/en/SUMMARY.md +++ b/docs/en/SUMMARY.md @@ -2,16 +2,16 @@ * [Kotlin JDSL](README.md) * [JPQL with Kotlin JDSL](jpql-with-kotlin-jdsl/README.md) - * [Statements](jpql-with-kotlin-jdsl/statements.md) - * [Entities](jpql-with-kotlin-jdsl/entities.md) - * [Paths](jpql-with-kotlin-jdsl/paths.md) - * [Expressions](jpql-with-kotlin-jdsl/expressions.md) - * [Predicates](jpql-with-kotlin-jdsl/predicates.md) - * [Sorts](jpql-with-kotlin-jdsl/sorts.md) - * [Subqueries](jpql-with-kotlin-jdsl/subqueries.md) - * [Custom DSL](jpql-with-kotlin-jdsl/custom-dsl.md) - * [Spring supports](jpql-with-kotlin-jdsl/spring-supports.md) - * [Migration 2.X to 3.X](jpql-with-kotlin-jdsl/migration-2.x-to-3.x.md) + * [Statements](jpql-with-kotlin-jdsl/statements.md) + * [Entities](jpql-with-kotlin-jdsl/entities.md) + * [Paths](jpql-with-kotlin-jdsl/paths.md) + * [Expressions](jpql-with-kotlin-jdsl/expressions.md) + * [Predicates](jpql-with-kotlin-jdsl/predicates.md) + * [Sorts](jpql-with-kotlin-jdsl/sorts.md) + * [Subqueries](jpql-with-kotlin-jdsl/subqueries.md) + * [Custom DSL](jpql-with-kotlin-jdsl/custom-dsl.md) + * [Spring supports](jpql-with-kotlin-jdsl/spring-supports.md) + * [Migration 2.X to 3.X](jpql-with-kotlin-jdsl/migration-2.x-to-3.x.md) * [Kotlin JDSL Roadmap](kotlin-jdsl-roadmap.md) ## FAQ diff --git a/docs/ko/SUMMARY.md b/docs/ko/SUMMARY.md index 087c0c4e2..105b3b8ae 100644 --- a/docs/ko/SUMMARY.md +++ b/docs/ko/SUMMARY.md @@ -2,16 +2,16 @@ * [Kotlin JDSL](README.md) * [JPQL with Kotlin JDSL](jpql-with-kotlin-jdsl/README.md) - * [Statements](jpql-with-kotlin-jdsl/statements.md) - * [Entities](jpql-with-kotlin-jdsl/entities.md) - * [Paths](jpql-with-kotlin-jdsl/paths.md) - * [Expressions](jpql-with-kotlin-jdsl/expressions.md) - * [Predicates](jpql-with-kotlin-jdsl/predicates.md) - * [Sorts](jpql-with-kotlin-jdsl/sorts.md) - * [Subqueries](jpql-with-kotlin-jdsl/subqueries.md) - * [Custom DSL](jpql-with-kotlin-jdsl/custom-dsl.md) - * [Spring supports](jpql-with-kotlin-jdsl/spring-supports.md) - * [Migration 2.X to 3.X](jpql-with-kotlin-jdsl/migration-2.x-to-3.x.md) + * [Statements](jpql-with-kotlin-jdsl/statements.md) + * [Entities](jpql-with-kotlin-jdsl/entities.md) + * [Paths](jpql-with-kotlin-jdsl/paths.md) + * [Expressions](jpql-with-kotlin-jdsl/expressions.md) + * [Predicates](jpql-with-kotlin-jdsl/predicates.md) + * [Sorts](jpql-with-kotlin-jdsl/sorts.md) + * [Subqueries](jpql-with-kotlin-jdsl/subqueries.md) + * [Custom DSL](jpql-with-kotlin-jdsl/custom-dsl.md) + * [Spring supports](jpql-with-kotlin-jdsl/spring-supports.md) + * [Migration 2.X to 3.X](jpql-with-kotlin-jdsl/migration-2.x-to-3.x.md) * [Kotlin JDSL Roadmap](kotlin-jdsl-roadmap.md) ## FAQ From 7556c844062faa719184005b2663e52f952bb44b Mon Sep 17 00:00:00 2001 From: Jonghyon Seo <31586979+shouwn@users.noreply.github.com> Date: Tue, 17 Dec 2024 18:56:27 +0900 Subject: [PATCH 5/7] feat: support stream in spring data repository (#803) * feat: support stream in spring data repository * docs: support stream in spring data repository --- .../jpql-with-kotlin-jdsl/spring-supports.md | 30 ++- .../jpql-with-kotlin-jdsl/spring-supports.md | 30 ++- .../jpa/javax/jpql/select/SelectExample.kt | 58 +++++ .../data/jpa/jpql/select/SelectExample.kt | 51 ++++ .../repository/KotlinJdslJpqlExecutor.kt | 62 +++++ .../repository/KotlinJdslJpqlExecutorImpl.kt | 72 +++++- .../KotlinJdslJpqlExecutorImplTest.kt | 224 ++++++++++++++++++ .../jpa/repository/KotlinJdslJpqlExecutor.kt | 62 +++++ .../repository/KotlinJdslJpqlExecutorImpl.kt | 72 +++++- .../KotlinJdslJpqlExecutorImplTest.kt | 224 ++++++++++++++++++ 10 files changed, 849 insertions(+), 36 deletions(-) diff --git a/docs/en/jpql-with-kotlin-jdsl/spring-supports.md b/docs/en/jpql-with-kotlin-jdsl/spring-supports.md index bb6b60c43..3e63b848c 100644 --- a/docs/en/jpql-with-kotlin-jdsl/spring-supports.md +++ b/docs/en/jpql-with-kotlin-jdsl/spring-supports.md @@ -12,23 +12,33 @@ If you declare your `JpqlSerializer` or `JpqlIntrospector` as a bean, it will be If your `JpaRepository` extends `KotlinJdslJpqlExecutor`, you can use the extension provided by Kotlin JDSL. ```kotlin -interface AuthorRepository : JpaRepository, KotlinJdslJpqlExecutor interface BookRepository : JpaRepository, KotlinJdslJpqlExecutor -authorRepository.findAll { +val result: List = bookRepository.findAll { select( - path(Author::authorId), + path(Book::isbn), + ).from( + entity(Book::class), + ) +} + +val result: Page = bookRepository.findPage(pageable) { + select( + path(Book::isbn), ).from( - entity(Author::class), - join(BookAuthor::class).on(path(Author::authorId).equal(path(BookAuthor::authorId))), - ).groupBy( - path(Author::authorId), - ).orderBy( - count(Author::authorId).desc(), + entity(Book::class), + ) +} + +val result: Slice = bookRepository.findSlice(pageable) { + select( + path(Book::isbn), + ).from( + entity(Book::class), ) } -bookRepository.findPage(pageable) { +val result: Stream = bookRepository.findStream { select( path(Book::isbn), ).from( diff --git a/docs/ko/jpql-with-kotlin-jdsl/spring-supports.md b/docs/ko/jpql-with-kotlin-jdsl/spring-supports.md index b33b4930d..b32228d16 100644 --- a/docs/ko/jpql-with-kotlin-jdsl/spring-supports.md +++ b/docs/ko/jpql-with-kotlin-jdsl/spring-supports.md @@ -12,23 +12,33 @@ Kotlin JDSL은 Spring Boot AutoConfigure를 지원합니다. 만약 사용하고 있는 `JpaRepository`가 `KotlinJdslJpqlExecutor`를 상속하면, Kotlin JDSL이 제공하는 확장 기능을 사용할 수 있습니다. ```kotlin -interface AuthorRepository : JpaRepository, KotlinJdslJpqlExecutor interface BookRepository : JpaRepository, KotlinJdslJpqlExecutor -authorRepository.findAll { +val result: List = bookRepository.findAll { select( - path(Author::authorId), + path(Book::isbn), + ).from( + entity(Book::class), + ) +} + +val result: Page = bookRepository.findPage(pageable) { + select( + path(Book::isbn), ).from( - entity(Author::class), - join(BookAuthor::class).on(path(Author::authorId).equal(path(BookAuthor::authorId))), - ).groupBy( - path(Author::authorId), - ).orderBy( - count(Author::authorId).desc(), + entity(Book::class), + ) +} + +val result: Slice = bookRepository.findSlice(pageable) { + select( + path(Book::isbn), + ).from( + entity(Book::class), ) } -bookRepository.findPage(pageable) { +val result: Stream = bookRepository.findStream { select( path(Book::isbn), ).from( diff --git a/example/spring-data-jpa-javax/src/test/kotlin/com/linecorp/kotlinjdsl/example/spring/data/jpa/javax/jpql/select/SelectExample.kt b/example/spring-data-jpa-javax/src/test/kotlin/com/linecorp/kotlinjdsl/example/spring/data/jpa/javax/jpql/select/SelectExample.kt index 5f30e16c0..4f494d8d9 100644 --- a/example/spring-data-jpa-javax/src/test/kotlin/com/linecorp/kotlinjdsl/example/spring/data/jpa/javax/jpql/select/SelectExample.kt +++ b/example/spring-data-jpa-javax/src/test/kotlin/com/linecorp/kotlinjdsl/example/spring/data/jpa/javax/jpql/select/SelectExample.kt @@ -18,6 +18,7 @@ import org.springframework.data.domain.PageRequest import org.springframework.data.domain.Sort import org.springframework.transaction.annotation.Transactional import java.time.OffsetDateTime +import java.util.stream.Collectors @Transactional @SpringBootTest @@ -89,6 +90,30 @@ class SelectExample : WithAssertions { assertThat(actual).isEqualTo(listOf(Isbn("04"), Isbn("05"), Isbn("06"))) } + @Test + fun `the stream of books`() { + // given + val pageable = PageRequest.of(1, 3, Sort.by(Sort.Direction.ASC, "isbn")) + + // when + val actual = bookRepository.findStream(pageable) { + select( + path(Book::isbn), + ).from( + entity(Book::class), + ) + } + + // then + assertThat(actual.collect(Collectors.toList())).isEqualTo( + listOf( + Isbn("04"), + Isbn("05"), + Isbn("06"), + ), + ) + } + @Test fun `the page of books`() { // given @@ -302,6 +327,39 @@ class SelectExample : WithAssertions { ) } + @Test + fun the_number_of_employees_per_department_stream() { + // given + data class Row( + val departmentId: Long, + val count: Long, + ) + + // when + val actual = employeeRepository.findStream { + selectNew( + path(EmployeeDepartment::departmentId), + count(Employee::employeeId), + ).from( + entity(Employee::class), + join(Employee::departments), + ).groupBy( + path(EmployeeDepartment::departmentId), + ).orderBy( + path(EmployeeDepartment::departmentId).asc(), + ) + } + + // then + assertThat(actual.collect(Collectors.toList())).isEqualTo( + listOf( + Row(1, 6), + Row(2, 15), + Row(3, 18), + ), + ) + } + @Test fun `the number of employees who belong to more than one department`() { // when diff --git a/example/spring-data-jpa/src/test/kotlin/com/linecorp/kotlinjdsl/example/spring/data/jpa/jpql/select/SelectExample.kt b/example/spring-data-jpa/src/test/kotlin/com/linecorp/kotlinjdsl/example/spring/data/jpa/jpql/select/SelectExample.kt index aaa6de0ed..61de2f806 100644 --- a/example/spring-data-jpa/src/test/kotlin/com/linecorp/kotlinjdsl/example/spring/data/jpa/jpql/select/SelectExample.kt +++ b/example/spring-data-jpa/src/test/kotlin/com/linecorp/kotlinjdsl/example/spring/data/jpa/jpql/select/SelectExample.kt @@ -147,6 +147,24 @@ class SelectExample : WithAssertions { assertThat(actual.hasNext()).isTrue } + @Test + fun `the stream of books`() { + // given + val pageable = PageRequest.of(1, 3, Sort.by(Sort.Direction.ASC, "isbn")) + + // when + val actual = bookRepository.findStream(pageable) { + select( + path(Book::isbn), + ).from( + entity(Book::class), + ) + } + + // then + assertThat(actual.toList()).isEqualTo(listOf(Isbn("04"), Isbn("05"), Isbn("06"))) + } + @Test fun `the book with the most authors`() { // when @@ -321,6 +339,39 @@ class SelectExample : WithAssertions { ) } + @Test + fun the_number_of_employees_per_department_stream() { + // given + data class Row( + val departmentId: Long, + val count: Long, + ) + + // when + val actual = employeeRepository.findStream { + selectNew( + path(EmployeeDepartment::departmentId), + count(Employee::employeeId), + ).from( + entity(Employee::class), + join(Employee::departments), + ).groupBy( + path(EmployeeDepartment::departmentId), + ).orderBy( + path(EmployeeDepartment::departmentId).asc(), + ) + } + + // then + assertThat(actual.toList()).isEqualTo( + listOf( + Row(1, 6), + Row(2, 15), + Row(3, 18), + ), + ) + } + @Test fun `the number of employees who belong to more than one department`() { // given diff --git a/support/spring-data-jpa-javax/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/javax/repository/KotlinJdslJpqlExecutor.kt b/support/spring-data-jpa-javax/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/javax/repository/KotlinJdslJpqlExecutor.kt index 56a500830..91623f02d 100644 --- a/support/spring-data-jpa-javax/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/javax/repository/KotlinJdslJpqlExecutor.kt +++ b/support/spring-data-jpa-javax/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/javax/repository/KotlinJdslJpqlExecutor.kt @@ -11,6 +11,7 @@ import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.data.domain.Slice import org.springframework.data.repository.NoRepositoryBean +import java.util.stream.Stream @NoRepositoryBean @SinceJdsl("3.0.0") @@ -134,6 +135,67 @@ interface KotlinJdslJpqlExecutor { init: DSL.() -> JpqlQueryable>, ): Slice + /** + * Returns all results of the select query. + */ + @SinceJdsl("3.5.4") + fun findStream( + offset: Int? = null, + limit: Int? = null, + init: Jpql.() -> JpqlQueryable>, + ): Stream + + /** + * Returns all results of the select query. + */ + @SinceJdsl("3.5.4") + fun findStream( + dsl: JpqlDsl.Constructor, + offset: Int? = null, + limit: Int? = null, + init: DSL.() -> JpqlQueryable>, + ): Stream + + /** + * Returns all results of the select query. + */ + @SinceJdsl("3.5.4") + fun findStream( + dsl: DSL, + offset: Int? = null, + limit: Int? = null, + init: DSL.() -> JpqlQueryable>, + ): Stream + + /** + * Returns all results of the select query. + */ + @SinceJdsl("3.5.4") + fun findStream( + pageable: Pageable, + init: Jpql.() -> JpqlQueryable>, + ): Stream + + /** + * Returns all results of the select query. + */ + @SinceJdsl("3.5.4") + fun findStream( + dsl: JpqlDsl.Constructor, + pageable: Pageable, + init: DSL.() -> JpqlQueryable>, + ): Stream + + /** + * Returns all results of the select query. + */ + @SinceJdsl("3.5.4") + fun findStream( + dsl: DSL, + pageable: Pageable, + init: DSL.() -> JpqlQueryable>, + ): Stream + /** * Execute the update query. * diff --git a/support/spring-data-jpa-javax/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/javax/repository/KotlinJdslJpqlExecutorImpl.kt b/support/spring-data-jpa-javax/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/javax/repository/KotlinJdslJpqlExecutorImpl.kt index 59ddd4e00..93eb22f6e 100644 --- a/support/spring-data-jpa-javax/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/javax/repository/KotlinJdslJpqlExecutorImpl.kt +++ b/support/spring-data-jpa-javax/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/javax/repository/KotlinJdslJpqlExecutorImpl.kt @@ -22,6 +22,7 @@ import org.springframework.data.jpa.repository.support.QueryHints import org.springframework.data.repository.NoRepositoryBean import org.springframework.data.support.PageableExecutionUtilsAdaptor import org.springframework.transaction.annotation.Transactional +import java.util.stream.Stream import javax.persistence.EntityManager import javax.persistence.LockModeType import javax.persistence.Query @@ -60,10 +61,7 @@ open class KotlinJdslJpqlExecutorImpl( init: DSL.() -> JpqlQueryable>, ): List { val query: SelectQuery = jpql(dsl, init) - val jpaQuery = createJpaQuery(query, query.returnType).apply { - offset?.let { setFirstResult(it) } - limit?.let { setMaxResults(it) } - } + val jpaQuery = createJpaQuery(query, query.returnType, offset, limit) return jpaQuery.resultList } @@ -90,7 +88,7 @@ open class KotlinJdslJpqlExecutorImpl( ): List { val query: SelectQuery = jpql(dsl, init) - return createList(query, query.returnType, pageable) + return createSortedQuery(query, query.returnType, pageable).resultList } override fun findPage( @@ -143,6 +141,60 @@ open class KotlinJdslJpqlExecutorImpl( return createSlice(query, query.returnType, pageable) } + override fun findStream( + offset: Int?, + limit: Int?, + init: Jpql.() -> JpqlQueryable>, + ): Stream { + return findStream(Jpql, offset = offset, limit = limit, init) + } + + override fun findStream( + dsl: JpqlDsl.Constructor, + offset: Int?, + limit: Int?, + init: DSL.() -> JpqlQueryable>, + ): Stream { + return findStream(dsl.newInstance(), offset = offset, limit = limit, init) + } + + override fun findStream( + dsl: DSL, + offset: Int?, + limit: Int?, + init: DSL.() -> JpqlQueryable>, + ): Stream { + val query: SelectQuery = jpql(dsl, init) + val jpaQuery = createJpaQuery(query, query.returnType, offset, limit) + + return jpaQuery.resultStream + } + + override fun findStream( + pageable: Pageable, + init: Jpql.() -> JpqlQueryable>, + ): Stream { + return findStream(Jpql, pageable, init) + } + + override fun findStream( + dsl: JpqlDsl.Constructor, + pageable: Pageable, + init: DSL.() -> JpqlQueryable>, + ): Stream { + return findStream(dsl.newInstance(), pageable, init) + } + + override fun findStream( + dsl: DSL, + pageable: Pageable, + init: DSL.() -> JpqlQueryable>, + ): Stream { + val query: SelectQuery = jpql(dsl, init) + + return this.createSortedQuery(query, query.returnType, pageable).resultStream + } + @Transactional override fun update( init: Jpql.() -> JpqlQueryable>, @@ -198,9 +250,13 @@ open class KotlinJdslJpqlExecutorImpl( private fun createJpaQuery( query: JpqlQuery<*>, returnType: KClass, + offset: Int?, + limit: Int?, ): TypedQuery { return JpqlEntityManagerUtils.createQuery(entityManager, query, returnType, renderContext).apply { setMetadata(this, metadata) + offset?.let { setFirstResult(it) } + limit?.let { setMaxResults(it) } } } @@ -258,11 +314,11 @@ open class KotlinJdslJpqlExecutorImpl( } } - private fun createList( + private fun createSortedQuery( query: JpqlQuery<*>, returnType: KClass, pageable: Pageable, - ): List { + ): TypedQuery { val enhancedQuery = createJpaEnhancedQuery(query, returnType, pageable.sort) val sortedQuery = enhancedQuery.sortedQuery @@ -272,7 +328,7 @@ open class KotlinJdslJpqlExecutorImpl( sortedQuery.maxResults = pageable.pageSize } - return sortedQuery.resultList + return sortedQuery } private fun createPage( diff --git a/support/spring-data-jpa-javax/src/test/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/javax/repository/KotlinJdslJpqlExecutorImplTest.kt b/support/spring-data-jpa-javax/src/test/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/javax/repository/KotlinJdslJpqlExecutorImplTest.kt index 18f10efb5..96e040aff 100644 --- a/support/spring-data-jpa-javax/src/test/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/javax/repository/KotlinJdslJpqlExecutorImplTest.kt +++ b/support/spring-data-jpa-javax/src/test/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/javax/repository/KotlinJdslJpqlExecutorImplTest.kt @@ -29,6 +29,7 @@ import org.springframework.data.jpa.repository.support.QueryHints import org.springframework.data.support.PageableExecutionUtilsAdaptor import java.util.function.BiConsumer import java.util.function.LongSupplier +import java.util.stream.Stream import javax.persistence.EntityManager import javax.persistence.LockModeType import javax.persistence.Query @@ -181,6 +182,7 @@ class KotlinJdslJpqlExecutorImplTest : WithAssertions { private val pageable1 = PageRequest.of(1, 10, sort1) private val list1 = listOf("1", "2", "3") + private val stream1 = Stream.of("1", "2", "3") private val counts1 = listOf(1L, 1L, 1L) private val enhancedTypedQuery1: EnhancedTypedQuery by lazy(LazyThreadSafetyMode.NONE) { @@ -767,6 +769,228 @@ class KotlinJdslJpqlExecutorImplTest : WithAssertions { } } + @Test + fun findStream() { + // given + every { selectQuery1.returnType } returns String::class + every { + JpqlEntityManagerUtils.createQuery(any(), any>(), any>(), any()) + } returns stringTypedQuery1 + every { stringTypedQuery1.setLockMode(any()) } returns stringTypedQuery1 + every { stringTypedQuery1.setHint(any(), any()) } returns stringTypedQuery1 + every { stringTypedQuery1.setFirstResult(any()) } returns stringTypedQuery1 + every { stringTypedQuery1.setMaxResults(any()) } returns stringTypedQuery1 + every { stringTypedQuery1.resultStream } returns stream1 + every { metadata.lockModeType } returns lockModeType1 + every { metadata.queryHints } returns queryHints1 + + // when + val actual = sut.findStream(offset1, limit1, createSelectQuery1) + + // then + assertThat(actual).isEqualTo(stream1) + + verifySequence { + selectQuery1.returnType + JpqlEntityManagerUtils.createQuery(entityManager, selectQuery1, String::class, renderContext) + metadata.lockModeType + stringTypedQuery1.setLockMode(lockModeType1) + metadata.queryHints + stringTypedQuery1.setHint(queryHint1.first, queryHint1.second) + stringTypedQuery1.setHint(queryHint2.first, queryHint2.second) + stringTypedQuery1.setHint(queryHint3.first, queryHint3.second) + stringTypedQuery1.setFirstResult(offset1) + stringTypedQuery1.setMaxResults(limit1) + stringTypedQuery1.resultStream + } + } + + @Test + fun `findStream() with a dsl`() { + // given + every { selectQuery2.returnType } returns String::class + every { + JpqlEntityManagerUtils.createQuery(any(), any>(), any>(), any()) + } returns stringTypedQuery2 + every { stringTypedQuery2.setLockMode(any()) } returns stringTypedQuery2 + every { stringTypedQuery2.setHint(any(), any()) } returns stringTypedQuery2 + every { stringTypedQuery2.setFirstResult(any()) } returns stringTypedQuery2 + every { stringTypedQuery2.setMaxResults(any()) } returns stringTypedQuery2 + every { stringTypedQuery2.resultStream } returns stream1 + every { metadata.lockModeType } returns lockModeType1 + every { metadata.queryHints } returns queryHints1 + + // when + val actual = sut.findStream(MyJpql, offset1, limit1, createSelectQuery2) + + // then + assertThat(actual).isEqualTo(stream1) + + verifySequence { + selectQuery2.returnType + JpqlEntityManagerUtils.createQuery(entityManager, selectQuery2, String::class, renderContext) + metadata.lockModeType + stringTypedQuery2.setLockMode(lockModeType1) + metadata.queryHints + stringTypedQuery2.setHint(queryHint1.first, queryHint1.second) + stringTypedQuery2.setHint(queryHint2.first, queryHint2.second) + stringTypedQuery2.setHint(queryHint3.first, queryHint3.second) + stringTypedQuery2.setFirstResult(offset1) + stringTypedQuery2.setMaxResults(limit1) + stringTypedQuery2.resultStream + } + } + + @Test + fun `findStream() with a dsl object`() { + // given + every { selectQuery3.returnType } returns String::class + every { + JpqlEntityManagerUtils.createQuery(any(), any>(), any>(), any()) + } returns stringTypedQuery3 + every { stringTypedQuery3.setLockMode(any()) } returns stringTypedQuery3 + every { stringTypedQuery3.setHint(any(), any()) } returns stringTypedQuery3 + every { stringTypedQuery3.setFirstResult(any()) } returns stringTypedQuery3 + every { stringTypedQuery3.setMaxResults(any()) } returns stringTypedQuery3 + every { stringTypedQuery3.resultStream } returns stream1 + every { metadata.lockModeType } returns lockModeType1 + every { metadata.queryHints } returns queryHints1 + + // when + val actual = sut.findStream(MyJpqlObject, offset1, limit1, createSelectQuery3) + + // then + assertThat(actual).isEqualTo(stream1) + + verifySequence { + selectQuery3.returnType + JpqlEntityManagerUtils.createQuery(entityManager, selectQuery3, String::class, renderContext) + metadata.lockModeType + stringTypedQuery3.setLockMode(lockModeType1) + metadata.queryHints + stringTypedQuery3.setHint(queryHint1.first, queryHint1.second) + stringTypedQuery3.setHint(queryHint2.first, queryHint2.second) + stringTypedQuery3.setHint(queryHint3.first, queryHint3.second) + stringTypedQuery3.setFirstResult(offset1) + stringTypedQuery3.setMaxResults(limit1) + stringTypedQuery3.resultStream + } + } + + @Test + fun `findStream() with a pageable`() { + // given + every { selectQuery1.returnType } returns String::class + every { + JpqlEntityManagerUtils.createEnhancedQuery( + any(), any>(), any>(), any(), any(), + ) + } returns enhancedTypedQuery1 + every { stringTypedQuery1.setLockMode(any()) } returns stringTypedQuery1 + every { stringTypedQuery1.setHint(any(), any()) } returns stringTypedQuery1 + every { stringTypedQuery1.setFirstResult(any()) } returns stringTypedQuery1 + every { stringTypedQuery1.setMaxResults(any()) } returns stringTypedQuery1 + every { stringTypedQuery1.resultStream } returns stream1 + every { metadata.lockModeType } returns lockModeType1 + every { metadata.queryHints } returns queryHints1 + + // when + val actual = sut.findStream(pageable1, createSelectQuery1) + + // then + assertThat(actual).isEqualTo(stream1) + + verifySequence { + selectQuery1.returnType + JpqlEntityManagerUtils.createEnhancedQuery(entityManager, selectQuery1, String::class, sort1, renderContext) + metadata.lockModeType + stringTypedQuery1.setLockMode(lockModeType1) + metadata.queryHints + stringTypedQuery1.setHint(queryHint1.first, queryHint1.second) + stringTypedQuery1.setHint(queryHint2.first, queryHint2.second) + stringTypedQuery1.setHint(queryHint3.first, queryHint3.second) + stringTypedQuery1.firstResult = pageable1.offset.toInt() + stringTypedQuery1.maxResults = pageable1.pageSize + stringTypedQuery1.resultStream + } + } + + @Test + fun `findStream() with a dsl and a pageable`() { + // given + every { selectQuery2.returnType } returns String::class + every { + JpqlEntityManagerUtils.createEnhancedQuery( + any(), any>(), any>(), any(), any(), + ) + } returns enhancedTypedQuery2 + every { stringTypedQuery2.setLockMode(any()) } returns stringTypedQuery2 + every { stringTypedQuery2.setHint(any(), any()) } returns stringTypedQuery2 + every { stringTypedQuery2.setFirstResult(any()) } returns stringTypedQuery2 + every { stringTypedQuery2.setMaxResults(any()) } returns stringTypedQuery2 + every { stringTypedQuery2.resultStream } returns stream1 + every { metadata.lockModeType } returns lockModeType1 + every { metadata.queryHints } returns queryHints1 + + // when + val actual = sut.findStream(MyJpql, pageable1, createSelectQuery2) + + // then + assertThat(actual).isEqualTo(stream1) + + verifySequence { + selectQuery2.returnType + JpqlEntityManagerUtils.createEnhancedQuery(entityManager, selectQuery2, String::class, sort1, renderContext) + metadata.lockModeType + stringTypedQuery2.setLockMode(lockModeType1) + metadata.queryHints + stringTypedQuery2.setHint(queryHint1.first, queryHint1.second) + stringTypedQuery2.setHint(queryHint2.first, queryHint2.second) + stringTypedQuery2.setHint(queryHint3.first, queryHint3.second) + stringTypedQuery2.firstResult = pageable1.offset.toInt() + stringTypedQuery2.maxResults = pageable1.pageSize + stringTypedQuery2.resultStream + } + } + + @Test + fun `findStream() with a dsl object and a pageable`() { + // given + every { selectQuery3.returnType } returns String::class + every { + JpqlEntityManagerUtils.createEnhancedQuery( + any(), any>(), any>(), any(), any(), + ) + } returns enhancedTypedQuery3 + every { stringTypedQuery3.setLockMode(any()) } returns stringTypedQuery3 + every { stringTypedQuery3.setHint(any(), any()) } returns stringTypedQuery3 + every { stringTypedQuery3.setFirstResult(any()) } returns stringTypedQuery3 + every { stringTypedQuery3.setMaxResults(any()) } returns stringTypedQuery3 + every { stringTypedQuery3.resultStream } returns stream1 + every { metadata.lockModeType } returns lockModeType1 + every { metadata.queryHints } returns queryHints1 + + // when + val actual = sut.findStream(MyJpqlObject, pageable1, createSelectQuery3) + + // then + assertThat(actual).isEqualTo(stream1) + + verifySequence { + selectQuery3.returnType + JpqlEntityManagerUtils.createEnhancedQuery(entityManager, selectQuery3, String::class, sort1, renderContext) + metadata.lockModeType + stringTypedQuery3.setLockMode(lockModeType1) + metadata.queryHints + stringTypedQuery3.setHint(queryHint1.first, queryHint1.second) + stringTypedQuery3.setHint(queryHint2.first, queryHint2.second) + stringTypedQuery3.setHint(queryHint3.first, queryHint3.second) + stringTypedQuery3.firstResult = pageable1.offset.toInt() + stringTypedQuery3.maxResults = pageable1.pageSize + stringTypedQuery3.resultStream + } + } + @Test fun update() { // given diff --git a/support/spring-data-jpa/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/repository/KotlinJdslJpqlExecutor.kt b/support/spring-data-jpa/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/repository/KotlinJdslJpqlExecutor.kt index c3e28613c..a7002f28d 100644 --- a/support/spring-data-jpa/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/repository/KotlinJdslJpqlExecutor.kt +++ b/support/spring-data-jpa/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/repository/KotlinJdslJpqlExecutor.kt @@ -11,6 +11,7 @@ import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.data.domain.Slice import org.springframework.data.repository.NoRepositoryBean +import java.util.stream.Stream @NoRepositoryBean @SinceJdsl("3.0.0") @@ -134,6 +135,67 @@ interface KotlinJdslJpqlExecutor { init: DSL.() -> JpqlQueryable>, ): Slice + /** + * Returns all results of the select query. + */ + @SinceJdsl("3.5.4") + fun findStream( + offset: Int? = null, + limit: Int? = null, + init: Jpql.() -> JpqlQueryable>, + ): Stream + + /** + * Returns all results of the select query. + */ + @SinceJdsl("3.5.4") + fun findStream( + dsl: JpqlDsl.Constructor, + offset: Int? = null, + limit: Int? = null, + init: DSL.() -> JpqlQueryable>, + ): Stream + + /** + * Returns all results of the select query. + */ + @SinceJdsl("3.5.4") + fun findStream( + dsl: DSL, + offset: Int? = null, + limit: Int? = null, + init: DSL.() -> JpqlQueryable>, + ): Stream + + /** + * Returns all results of the select query. + */ + @SinceJdsl("3.5.4") + fun findStream( + pageable: Pageable, + init: Jpql.() -> JpqlQueryable>, + ): Stream + + /** + * Returns all results of the select query. + */ + @SinceJdsl("3.5.4") + fun findStream( + dsl: JpqlDsl.Constructor, + pageable: Pageable, + init: DSL.() -> JpqlQueryable>, + ): Stream + + /** + * Returns all results of the select query. + */ + @SinceJdsl("3.5.4") + fun findStream( + dsl: DSL, + pageable: Pageable, + init: DSL.() -> JpqlQueryable>, + ): Stream + /** * Execute the update query. * diff --git a/support/spring-data-jpa/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/repository/KotlinJdslJpqlExecutorImpl.kt b/support/spring-data-jpa/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/repository/KotlinJdslJpqlExecutorImpl.kt index db74f6857..3d22667ff 100644 --- a/support/spring-data-jpa/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/repository/KotlinJdslJpqlExecutorImpl.kt +++ b/support/spring-data-jpa/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/repository/KotlinJdslJpqlExecutorImpl.kt @@ -28,6 +28,7 @@ import org.springframework.data.jpa.repository.support.QueryHints import org.springframework.data.repository.NoRepositoryBean import org.springframework.data.support.PageableExecutionUtilsAdaptor import org.springframework.transaction.annotation.Transactional +import java.util.stream.Stream import kotlin.reflect.KClass @NoRepositoryBean @@ -62,10 +63,7 @@ open class KotlinJdslJpqlExecutorImpl( init: DSL.() -> JpqlQueryable>, ): List { val query: SelectQuery = jpql(dsl, init) - val jpaQuery = createJpaQuery(query, query.returnType).apply { - offset?.let { setFirstResult(it) } - limit?.let { setMaxResults(it) } - } + val jpaQuery = createJpaQuery(query, query.returnType, offset, limit) return jpaQuery.resultList } @@ -92,7 +90,7 @@ open class KotlinJdslJpqlExecutorImpl( ): List { val query: SelectQuery = jpql(dsl, init) - return createList(query, query.returnType, pageable) + return this.createSortedQuery(query, query.returnType, pageable).resultList } override fun findPage( @@ -145,6 +143,60 @@ open class KotlinJdslJpqlExecutorImpl( return createSlice(query, query.returnType, pageable) } + override fun findStream( + offset: Int?, + limit: Int?, + init: Jpql.() -> JpqlQueryable>, + ): Stream { + return findStream(Jpql, offset = offset, limit = limit, init) + } + + override fun findStream( + dsl: JpqlDsl.Constructor, + offset: Int?, + limit: Int?, + init: DSL.() -> JpqlQueryable>, + ): Stream { + return findStream(dsl.newInstance(), offset = offset, limit = limit, init) + } + + override fun findStream( + dsl: DSL, + offset: Int?, + limit: Int?, + init: DSL.() -> JpqlQueryable>, + ): Stream { + val query: SelectQuery = jpql(dsl, init) + val jpaQuery = createJpaQuery(query, query.returnType, offset, limit) + + return jpaQuery.resultStream + } + + override fun findStream( + pageable: Pageable, + init: Jpql.() -> JpqlQueryable>, + ): Stream { + return findStream(Jpql, pageable, init) + } + + override fun findStream( + dsl: JpqlDsl.Constructor, + pageable: Pageable, + init: DSL.() -> JpqlQueryable>, + ): Stream { + return findStream(dsl.newInstance(), pageable, init) + } + + override fun findStream( + dsl: DSL, + pageable: Pageable, + init: DSL.() -> JpqlQueryable>, + ): Stream { + val query: SelectQuery = jpql(dsl, init) + + return this.createSortedQuery(query, query.returnType, pageable).resultStream + } + @Transactional override fun update( init: Jpql.() -> JpqlQueryable>, @@ -200,9 +252,13 @@ open class KotlinJdslJpqlExecutorImpl( private fun createJpaQuery( query: JpqlQuery<*>, returnType: KClass, + offset: Int?, + limit: Int?, ): TypedQuery { return JpqlEntityManagerUtils.createQuery(entityManager, query, returnType, renderContext).apply { setMetadata(this, metadata) + offset?.let { setFirstResult(it) } + limit?.let { setMaxResults(it) } } } @@ -260,11 +316,11 @@ open class KotlinJdslJpqlExecutorImpl( } } - private fun createList( + private fun createSortedQuery( query: JpqlQuery<*>, returnType: KClass, pageable: Pageable, - ): List { + ): TypedQuery { val enhancedQuery = createJpaEnhancedQuery(query, returnType, pageable.sort) val sortedQuery = enhancedQuery.sortedQuery @@ -274,7 +330,7 @@ open class KotlinJdslJpqlExecutorImpl( sortedQuery.maxResults = pageable.pageSize } - return sortedQuery.resultList + return sortedQuery } private fun createPage( diff --git a/support/spring-data-jpa/src/test/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/repository/KotlinJdslJpqlExecutorImplTest.kt b/support/spring-data-jpa/src/test/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/repository/KotlinJdslJpqlExecutorImplTest.kt index 654d7e27b..52a4ae793 100644 --- a/support/spring-data-jpa/src/test/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/repository/KotlinJdslJpqlExecutorImplTest.kt +++ b/support/spring-data-jpa/src/test/kotlin/com/linecorp/kotlinjdsl/support/spring/data/jpa/repository/KotlinJdslJpqlExecutorImplTest.kt @@ -33,6 +33,7 @@ import org.springframework.data.jpa.repository.support.QueryHints import org.springframework.data.support.PageableExecutionUtilsAdaptor import java.util.function.BiConsumer import java.util.function.LongSupplier +import java.util.stream.Stream import kotlin.reflect.KClass @ExtendWith(MockKExtension::class) @@ -181,6 +182,7 @@ class KotlinJdslJpqlExecutorImplTest : WithAssertions { private val pageable1 = PageRequest.of(1, 10, sort1) private val list1 = listOf("1", "2", "3") + private val stream1 = Stream.of("1", "2", "3") private val counts1 = listOf(1L, 1L, 1L) private val enhancedTypedQuery1: EnhancedTypedQuery by lazy(LazyThreadSafetyMode.NONE) { @@ -767,6 +769,228 @@ class KotlinJdslJpqlExecutorImplTest : WithAssertions { } } + @Test + fun findStream() { + // given + every { selectQuery1.returnType } returns String::class + every { + JpqlEntityManagerUtils.createQuery(any(), any>(), any>(), any()) + } returns stringTypedQuery1 + every { stringTypedQuery1.setLockMode(any()) } returns stringTypedQuery1 + every { stringTypedQuery1.setHint(any(), any()) } returns stringTypedQuery1 + every { stringTypedQuery1.setFirstResult(any()) } returns stringTypedQuery1 + every { stringTypedQuery1.setMaxResults(any()) } returns stringTypedQuery1 + every { stringTypedQuery1.resultStream } returns stream1 + every { metadata.lockModeType } returns lockModeType1 + every { metadata.queryHints } returns queryHints1 + + // when + val actual = sut.findStream(offset1, limit1, createSelectQuery1) + + // then + assertThat(actual).isEqualTo(stream1) + + verifySequence { + selectQuery1.returnType + JpqlEntityManagerUtils.createQuery(entityManager, selectQuery1, String::class, renderContext) + metadata.lockModeType + stringTypedQuery1.setLockMode(lockModeType1) + metadata.queryHints + stringTypedQuery1.setHint(queryHint1.first, queryHint1.second) + stringTypedQuery1.setHint(queryHint2.first, queryHint2.second) + stringTypedQuery1.setHint(queryHint3.first, queryHint3.second) + stringTypedQuery1.setFirstResult(offset1) + stringTypedQuery1.setMaxResults(limit1) + stringTypedQuery1.resultStream + } + } + + @Test + fun `findStream() with a dsl`() { + // given + every { selectQuery2.returnType } returns String::class + every { + JpqlEntityManagerUtils.createQuery(any(), any>(), any>(), any()) + } returns stringTypedQuery2 + every { stringTypedQuery2.setLockMode(any()) } returns stringTypedQuery2 + every { stringTypedQuery2.setHint(any(), any()) } returns stringTypedQuery2 + every { stringTypedQuery2.setFirstResult(any()) } returns stringTypedQuery2 + every { stringTypedQuery2.setMaxResults(any()) } returns stringTypedQuery2 + every { stringTypedQuery2.resultStream } returns stream1 + every { metadata.lockModeType } returns lockModeType1 + every { metadata.queryHints } returns queryHints1 + + // when + val actual = sut.findStream(MyJpql, offset1, limit1, createSelectQuery2) + + // then + assertThat(actual).isEqualTo(stream1) + + verifySequence { + selectQuery2.returnType + JpqlEntityManagerUtils.createQuery(entityManager, selectQuery2, String::class, renderContext) + metadata.lockModeType + stringTypedQuery2.setLockMode(lockModeType1) + metadata.queryHints + stringTypedQuery2.setHint(queryHint1.first, queryHint1.second) + stringTypedQuery2.setHint(queryHint2.first, queryHint2.second) + stringTypedQuery2.setHint(queryHint3.first, queryHint3.second) + stringTypedQuery2.setFirstResult(offset1) + stringTypedQuery2.setMaxResults(limit1) + stringTypedQuery2.resultStream + } + } + + @Test + fun `findStream() with a dsl object`() { + // given + every { selectQuery3.returnType } returns String::class + every { + JpqlEntityManagerUtils.createQuery(any(), any>(), any>(), any()) + } returns stringTypedQuery3 + every { stringTypedQuery3.setLockMode(any()) } returns stringTypedQuery3 + every { stringTypedQuery3.setHint(any(), any()) } returns stringTypedQuery3 + every { stringTypedQuery3.setFirstResult(any()) } returns stringTypedQuery3 + every { stringTypedQuery3.setMaxResults(any()) } returns stringTypedQuery3 + every { stringTypedQuery3.resultStream } returns stream1 + every { metadata.lockModeType } returns lockModeType1 + every { metadata.queryHints } returns queryHints1 + + // when + val actual = sut.findStream(MyJpqlObject, offset1, limit1, createSelectQuery3) + + // then + assertThat(actual).isEqualTo(stream1) + + verifySequence { + selectQuery3.returnType + JpqlEntityManagerUtils.createQuery(entityManager, selectQuery3, String::class, renderContext) + metadata.lockModeType + stringTypedQuery3.setLockMode(lockModeType1) + metadata.queryHints + stringTypedQuery3.setHint(queryHint1.first, queryHint1.second) + stringTypedQuery3.setHint(queryHint2.first, queryHint2.second) + stringTypedQuery3.setHint(queryHint3.first, queryHint3.second) + stringTypedQuery3.setFirstResult(offset1) + stringTypedQuery3.setMaxResults(limit1) + stringTypedQuery3.resultStream + } + } + + @Test + fun `findStream() with a pageable`() { + // given + every { selectQuery1.returnType } returns String::class + every { + JpqlEntityManagerUtils.createEnhancedQuery( + any(), any>(), any>(), any(), any(), + ) + } returns enhancedTypedQuery1 + every { stringTypedQuery1.setLockMode(any()) } returns stringTypedQuery1 + every { stringTypedQuery1.setHint(any(), any()) } returns stringTypedQuery1 + every { stringTypedQuery1.setFirstResult(any()) } returns stringTypedQuery1 + every { stringTypedQuery1.setMaxResults(any()) } returns stringTypedQuery1 + every { stringTypedQuery1.resultStream } returns stream1 + every { metadata.lockModeType } returns lockModeType1 + every { metadata.queryHints } returns queryHints1 + + // when + val actual = sut.findStream(pageable1, createSelectQuery1) + + // then + assertThat(actual).isEqualTo(stream1) + + verifySequence { + selectQuery1.returnType + JpqlEntityManagerUtils.createEnhancedQuery(entityManager, selectQuery1, String::class, sort1, renderContext) + metadata.lockModeType + stringTypedQuery1.setLockMode(lockModeType1) + metadata.queryHints + stringTypedQuery1.setHint(queryHint1.first, queryHint1.second) + stringTypedQuery1.setHint(queryHint2.first, queryHint2.second) + stringTypedQuery1.setHint(queryHint3.first, queryHint3.second) + stringTypedQuery1.firstResult = pageable1.offset.toInt() + stringTypedQuery1.maxResults = pageable1.pageSize + stringTypedQuery1.resultStream + } + } + + @Test + fun `findStream() with a dsl and a pageable`() { + // given + every { selectQuery2.returnType } returns String::class + every { + JpqlEntityManagerUtils.createEnhancedQuery( + any(), any>(), any>(), any(), any(), + ) + } returns enhancedTypedQuery2 + every { stringTypedQuery2.setLockMode(any()) } returns stringTypedQuery2 + every { stringTypedQuery2.setHint(any(), any()) } returns stringTypedQuery2 + every { stringTypedQuery2.setFirstResult(any()) } returns stringTypedQuery2 + every { stringTypedQuery2.setMaxResults(any()) } returns stringTypedQuery2 + every { stringTypedQuery2.resultStream } returns stream1 + every { metadata.lockModeType } returns lockModeType1 + every { metadata.queryHints } returns queryHints1 + + // when + val actual = sut.findStream(MyJpql, pageable1, createSelectQuery2) + + // then + assertThat(actual).isEqualTo(stream1) + + verifySequence { + selectQuery2.returnType + JpqlEntityManagerUtils.createEnhancedQuery(entityManager, selectQuery2, String::class, sort1, renderContext) + metadata.lockModeType + stringTypedQuery2.setLockMode(lockModeType1) + metadata.queryHints + stringTypedQuery2.setHint(queryHint1.first, queryHint1.second) + stringTypedQuery2.setHint(queryHint2.first, queryHint2.second) + stringTypedQuery2.setHint(queryHint3.first, queryHint3.second) + stringTypedQuery2.firstResult = pageable1.offset.toInt() + stringTypedQuery2.maxResults = pageable1.pageSize + stringTypedQuery2.resultStream + } + } + + @Test + fun `findStream() with a dsl object and a pageable`() { + // given + every { selectQuery3.returnType } returns String::class + every { + JpqlEntityManagerUtils.createEnhancedQuery( + any(), any>(), any>(), any(), any(), + ) + } returns enhancedTypedQuery3 + every { stringTypedQuery3.setLockMode(any()) } returns stringTypedQuery3 + every { stringTypedQuery3.setHint(any(), any()) } returns stringTypedQuery3 + every { stringTypedQuery3.setFirstResult(any()) } returns stringTypedQuery3 + every { stringTypedQuery3.setMaxResults(any()) } returns stringTypedQuery3 + every { stringTypedQuery3.resultStream } returns stream1 + every { metadata.lockModeType } returns lockModeType1 + every { metadata.queryHints } returns queryHints1 + + // when + val actual = sut.findStream(MyJpqlObject, pageable1, createSelectQuery3) + + // then + assertThat(actual).isEqualTo(stream1) + + verifySequence { + selectQuery3.returnType + JpqlEntityManagerUtils.createEnhancedQuery(entityManager, selectQuery3, String::class, sort1, renderContext) + metadata.lockModeType + stringTypedQuery3.setLockMode(lockModeType1) + metadata.queryHints + stringTypedQuery3.setHint(queryHint1.first, queryHint1.second) + stringTypedQuery3.setHint(queryHint2.first, queryHint2.second) + stringTypedQuery3.setHint(queryHint3.first, queryHint3.second) + stringTypedQuery3.firstResult = pageable1.offset.toInt() + stringTypedQuery3.maxResults = pageable1.pageSize + stringTypedQuery3.resultStream + } + } + @Test fun update() { // given From 5974ea7995d3b2e343dbbd3772321d5bb7f6a2b7 Mon Sep 17 00:00:00 2001 From: Jonghyon Seo <31586979+shouwn@users.noreply.github.com> Date: Tue, 17 Dec 2024 18:57:48 +0900 Subject: [PATCH 6/7] feat: handle mixed values and expressions in parameters (#806) --- .../com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt | 18 ++++++++++----- .../expression/CustomExpressionDslTest.kt | 22 +++++++++++++++++++ .../dsl/jpql/expression/FunctionDslTest.kt | 22 +++++++++++++++++++ .../dsl/jpql/expression/NewDslTest.kt | 21 ++++++++++++++++++ .../jpql/predicate/CustomPredicateDslTest.kt | 21 ++++++++++++++++++ .../dsl/jpql/predicate/FunctionDslTest.kt | 21 ++++++++++++++++++ 6 files changed, 120 insertions(+), 5 deletions(-) diff --git a/dsl/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt b/dsl/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt index 13d54c210..ccfc4924a 100644 --- a/dsl/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt +++ b/dsl/jpql/src/main/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt @@ -1287,7 +1287,7 @@ open class Jpql : JpqlDsl { */ @SinceJdsl("3.0.0") fun new(type: KClass, vararg args: Any): Expression { - return Expressions.new(type, args.map { Expressions.value(it) }) + return Expressions.new(type, args.map { valueOrExpression(it) }) } /** @@ -1708,7 +1708,7 @@ open class Jpql : JpqlDsl { @LowPriorityInOverloadResolution @SinceJdsl("3.0.0") fun function(type: KClass, name: String, vararg args: Any): Expression { - return Expressions.function(type, name, args.map { Expressions.value(it) }) + return Expressions.function(type, name, args.map { valueOrExpression(it) }) } /** @@ -1737,7 +1737,7 @@ open class Jpql : JpqlDsl { @LowPriorityInOverloadResolution @SinceJdsl("3.0.0") fun customExpression(type: KClass, template: String, vararg args: Any): Expression { - return Expressions.customExpression(type, template, args.map { Expressions.value(it) }) + return Expressions.customExpression(type, template, args.map { valueOrExpression(it) }) } /** @@ -3036,7 +3036,7 @@ open class Jpql : JpqlDsl { @LowPriorityInOverloadResolution @SinceJdsl("3.0.0") fun function(type: KClass, name: String, vararg args: Any): Predicate { - return Predicates.function(name, args.map { Expressions.value(it) }) + return Predicates.function(name, args.map { valueOrExpression(it) }) } /** @@ -3066,7 +3066,7 @@ open class Jpql : JpqlDsl { @LowPriorityInOverloadResolution @SinceJdsl("3.3.0") fun customPredicate(template: String, vararg args: Any): Predicate { - return Predicates.customPredicate(template, args.map { Expressions.value(it) }) + return Predicates.customPredicate(template, args.map { valueOrExpression(it) }) } /** @@ -3218,4 +3218,12 @@ open class Jpql : JpqlDsl { fun deleteFrom(entity: Entityable): DeleteQueryWhereStep { return DeleteQueryDsl(entity.toEntity()) } + + private fun valueOrExpression(value: Any): Expression<*> { + return if (value is Expression<*>) { + value + } else { + Expressions.value(value) + } + } } diff --git a/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/expression/CustomExpressionDslTest.kt b/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/expression/CustomExpressionDslTest.kt index 40c640bba..a32847970 100644 --- a/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/expression/CustomExpressionDslTest.kt +++ b/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/expression/CustomExpressionDslTest.kt @@ -58,4 +58,26 @@ class CustomExpressionDslTest : WithAssertions { assertThat(actual).isEqualTo(expected) } + + @Test + fun `customExpression() with a string and a string expression`() { + // when + val expression = queryPart { + customExpression(Int::class, template1, string1, stringExpression2) + }.toExpression() + + val actual: Expression = expression // for type check + + // then + val expected = Expressions.customExpression( + Int::class, + template1, + listOf( + Expressions.value(string1), + stringExpression2, + ), + ) + + assertThat(actual).isEqualTo(expected) + } } diff --git a/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/expression/FunctionDslTest.kt b/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/expression/FunctionDslTest.kt index 76a6166b8..2f60e0d85 100644 --- a/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/expression/FunctionDslTest.kt +++ b/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/expression/FunctionDslTest.kt @@ -58,4 +58,26 @@ class FunctionDslTest : WithAssertions { assertThat(actual).isEqualTo(expected) } + + @Test + fun `function() with a string and a string expression`() { + // when + val expression = queryPart { + function(Int::class, name1, string1, stringExpression2) + }.toExpression() + + val actual: Expression = expression // for type check + + // then + val expected = Expressions.function( + Int::class, + name1, + listOf( + Expressions.value(string1), + stringExpression2, + ), + ) + + assertThat(actual).isEqualTo(expected) + } } diff --git a/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/expression/NewDslTest.kt b/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/expression/NewDslTest.kt index 88e3f163d..6e209bc47 100644 --- a/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/expression/NewDslTest.kt +++ b/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/expression/NewDslTest.kt @@ -56,4 +56,25 @@ class NewDslTest : WithAssertions { assertThat(actual).isEqualTo(expected) } + + @Test + fun `new() with a string and a string expression`() { + // when + val expression = queryPart { + new(Row::class, string1, stringExpression2) + }.toExpression() + + val actual: Expression = expression // for type check + + // then + val expected = Expressions.new( + type = Row::class, + args = listOf( + Expressions.value(string1), + stringExpression2, + ), + ) + + assertThat(actual).isEqualTo(expected) + } } diff --git a/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/predicate/CustomPredicateDslTest.kt b/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/predicate/CustomPredicateDslTest.kt index 2800a89a8..bfe172ec1 100644 --- a/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/predicate/CustomPredicateDslTest.kt +++ b/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/predicate/CustomPredicateDslTest.kt @@ -57,4 +57,25 @@ class CustomPredicateDslTest : WithAssertions { assertThat(actual).isEqualTo(expected) } + + @Test + fun `customPredicate() with a string and a string expression`() { + // when + val predicate = queryPart { + customPredicate(template1, string1, stringExpression2) + } + + val actual: Predicate = predicate // for type check + + // then + val expected = Predicates.customPredicate( + template1, + listOf( + Expressions.value(string1), + stringExpression2, + ), + ) + + assertThat(actual).isEqualTo(expected) + } } diff --git a/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/predicate/FunctionDslTest.kt b/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/predicate/FunctionDslTest.kt index 9a6f29188..11fab4a7b 100644 --- a/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/predicate/FunctionDslTest.kt +++ b/dsl/jpql/src/test/kotlin/com/linecorp/kotlinjdsl/dsl/jpql/predicate/FunctionDslTest.kt @@ -57,4 +57,25 @@ class FunctionDslTest : WithAssertions { assertThat(actual).isEqualTo(expected) } + + @Test + fun `function() with a string and a string expression`() { + // when + val predicate = queryPart { + function(Boolean::class, name1, string1, stringExpression2) + } + + val actual: Predicate = predicate // for type check + + // then + val expected = Predicates.function( + name1, + listOf( + Expressions.value(string1), + stringExpression2, + ), + ) + + assertThat(actual).isEqualTo(expected) + } } From 5f0dfba8ac709f491765eb6a285e064d2123de33 Mon Sep 17 00:00:00 2001 From: "jonghyon.s" Date: Wed, 18 Dec 2024 14:45:05 +0900 Subject: [PATCH 7/7] chore: release 3.5.4 --- build.gradle.kts | 2 +- docs/en/README.md | 2 +- docs/en/jpql-with-kotlin-jdsl/README.md | 12 ++++++------ docs/ko/README.md | 2 +- docs/ko/jpql-with-kotlin-jdsl/README.md | 12 ++++++------ 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 8f07c9088..c87168889 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -25,7 +25,7 @@ allprojects { apply(plugin = "signing") group = "com.linecorp.kotlin-jdsl" - version = "3.5.3" + version = "3.5.4" repositories { mavenCentral() diff --git a/docs/en/README.md b/docs/en/README.md index 02e965006..051ec320d 100644 --- a/docs/en/README.md +++ b/docs/en/README.md @@ -1,5 +1,5 @@ --- -description: 'Latest stable version: 3.5.3' +description: 'Latest stable version: 3.5.4' --- # Kotlin JDSL diff --git a/docs/en/jpql-with-kotlin-jdsl/README.md b/docs/en/jpql-with-kotlin-jdsl/README.md index 83e6a8a1a..7373f9863 100644 --- a/docs/en/jpql-with-kotlin-jdsl/README.md +++ b/docs/en/jpql-with-kotlin-jdsl/README.md @@ -99,8 +99,8 @@ The following dependencies are the minimum requirement for all Kotlin JDSL appli ```kotlin dependencies { - implementation("com.linecorp.kotlin-jdsl:jpql-dsl:3.5.3") - implementation("com.linecorp.kotlin-jdsl:jpql-render:3.5.3") + implementation("com.linecorp.kotlin-jdsl:jpql-dsl:3.5.4") + implementation("com.linecorp.kotlin-jdsl:jpql-render:3.5.4") } ``` @@ -110,8 +110,8 @@ dependencies { ```groovy dependencies { - implementation 'com.linecorp.kotlin-jdsl:jpql-dsl:3.5.3' - implementation 'com.linecorp.kotlin-jdsl:jpql-render:3.5.3' + implementation 'com.linecorp.kotlin-jdsl:jpql-dsl:3.5.4' + implementation 'com.linecorp.kotlin-jdsl:jpql-render:3.5.4' } ``` @@ -125,12 +125,12 @@ dependencies { com.linecorp.kotlin-jdsl jpql-dsl - 3.5.3 + 3.5.4 com.linecorp.kotlin-jdsl jpql-render - 3.5.3 + 3.5.4 ``` diff --git a/docs/ko/README.md b/docs/ko/README.md index efaa1879c..d79708937 100644 --- a/docs/ko/README.md +++ b/docs/ko/README.md @@ -1,5 +1,5 @@ --- -description: 'Latest stable version: 3.5.3' +description: 'Latest stable version: 3.5.4' --- # Kotlin JDSL diff --git a/docs/ko/jpql-with-kotlin-jdsl/README.md b/docs/ko/jpql-with-kotlin-jdsl/README.md index 74d4f7c94..f5bf94c10 100644 --- a/docs/ko/jpql-with-kotlin-jdsl/README.md +++ b/docs/ko/jpql-with-kotlin-jdsl/README.md @@ -100,8 +100,8 @@ Kotlin JDSL을 실행시키기 위해서는 다음 dependency들이 필수로 ```kotlin dependencies { - implementation("com.linecorp.kotlin-jdsl:jpql-dsl:3.5.3") - implementation("com.linecorp.kotlin-jdsl:jpql-render:3.5.3") + implementation("com.linecorp.kotlin-jdsl:jpql-dsl:3.5.4") + implementation("com.linecorp.kotlin-jdsl:jpql-render:3.5.4") } ``` @@ -111,8 +111,8 @@ dependencies { ```groovy dependencies { - implementation 'com.linecorp.kotlin-jdsl:jpql-dsl:3.5.3' - implementation 'com.linecorp.kotlin-jdsl:jpql-render:3.5.3' + implementation 'com.linecorp.kotlin-jdsl:jpql-dsl:3.5.4' + implementation 'com.linecorp.kotlin-jdsl:jpql-render:3.5.4' } ``` @@ -126,12 +126,12 @@ dependencies { com.linecorp.kotlin-jdsl jpql-dsl - 3.5.3 + 3.5.4 com.linecorp.kotlin-jdsl jpql-render - 3.5.3 + 3.5.4 ```