From 110b1111754f337a9f905630131e84aa2689ba46 Mon Sep 17 00:00:00 2001 From: Jake Son Date: Sun, 18 Feb 2024 10:01:53 +0900 Subject: [PATCH 1/3] feat: add support for accepting jdsl object as argument from spring data jpa --- .../com/linecorp/kotlinjdsl/dsl/jpql/Jpql.kt | 8 + .../repository/KotlinJdslJpqlExecutor.kt | 61 +++++++ .../repository/KotlinJdslJpqlExecutorImpl.kt | 56 +++++++ .../KotlinJdslJpqlExecutorImplTest.kt | 155 ++++++++++++++++++ .../jpa/repository/KotlinJdslJpqlExecutor.kt | 61 +++++++ .../repository/KotlinJdslJpqlExecutorImpl.kt | 56 +++++++ .../KotlinJdslJpqlExecutorImplTest.kt | 155 ++++++++++++++++++ 7 files changed, 552 insertions(+) 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 8de25ce1b..6ec1815ea 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 @@ -72,6 +72,14 @@ inline fun > jpql(dsl: JpqlDsl.Constructor, return dsl.newInstance().init().toQuery() } +/** + * Builds new JPQL query using provided JpqlDsl instance. + */ +@SinceJdsl("3.4.0") +inline fun > jpql(jpql: DSL, init: DSL.() -> JpqlQueryable): Q { + return jpql.init().toQuery() +} + /** * Default implementation of DSL for building a JPQL query. */ 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 5d4390f5d..28c8bce40 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 @@ -32,6 +32,15 @@ interface KotlinJdslJpqlExecutor { init: DSL.() -> JpqlQueryable>, ): List + /** + * Returns all results of the select query. + */ + @SinceJdsl("3.4.0") + fun findAll( + dsl: DSL, + init: DSL.() -> JpqlQueryable>, + ): List + /** * Returns all results of the select query. */ @@ -51,6 +60,16 @@ interface KotlinJdslJpqlExecutor { init: DSL.() -> JpqlQueryable>, ): List + /** + * Returns all results of the select query. + */ + @SinceJdsl("3.4.0") + fun findAll( + dsl: DSL, + pageable: Pageable, + init: DSL.() -> JpqlQueryable>, + ): List + /** * Returns the page of the select query. */ @@ -70,6 +89,16 @@ interface KotlinJdslJpqlExecutor { init: DSL.() -> JpqlQueryable>, ): Page + /** + * Returns the page of the select query. + */ + @SinceJdsl("3.4.0") + fun findPage( + dsl: DSL, + pageable: Pageable, + init: DSL.() -> JpqlQueryable>, + ): Page + /** * Returns the slice of the select query. */ @@ -89,6 +118,16 @@ interface KotlinJdslJpqlExecutor { init: DSL.() -> JpqlQueryable>, ): Slice + /** + * Returns the slice of the select query. + */ + @SinceJdsl("3.4.0") + fun findSlice( + dsl: DSL, + pageable: Pageable, + init: DSL.() -> JpqlQueryable>, + ): Slice + /** * Execute the update query. * @@ -110,6 +149,17 @@ interface KotlinJdslJpqlExecutor { init: DSL.() -> JpqlQueryable>, ): Int + /** + * Execute the update query. + * + * @return the number of entities updated + */ + @SinceJdsl("3.4.0") + fun update( + dsl: DSL, + init: DSL.() -> JpqlQueryable>, + ): Int + /** * Execute the delete query. * @@ -130,4 +180,15 @@ interface KotlinJdslJpqlExecutor { dsl: JpqlDsl.Constructor, init: DSL.() -> JpqlQueryable>, ): Int + + /** + * Execute the delete query. + * + * @return the number of entities deleted + */ + @SinceJdsl("3.4.0") + fun delete( + dsl: DSL, + init: DSL.() -> JpqlQueryable>, + ): Int } 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 e5420271a..e39cf6035 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 @@ -40,6 +40,13 @@ open class KotlinJdslJpqlExecutorImpl( return jpaQuery.resultList } + override fun findAll(dsl: DSL, init: DSL.() -> JpqlQueryable>): List { + val query: SelectQuery = jpql(dsl, init) + val jpaQuery = JpqlEntityManagerUtils.createQuery(entityManager, query, renderContext) + + return jpaQuery.resultList + } + override fun findAll( pageable: Pageable, init: Jpql.() -> JpqlQueryable>, @@ -57,6 +64,16 @@ open class KotlinJdslJpqlExecutorImpl( return JpqlEntityManagerUtils.queryForList(entityManager, query, pageable, renderContext) } + override fun findAll( + dsl: DSL, + pageable: Pageable, + init: DSL.() -> JpqlQueryable>, + ): List { + val query: SelectQuery = jpql(dsl, init) + + return JpqlEntityManagerUtils.queryForList(entityManager, query, pageable, renderContext) + } + override fun findPage( pageable: Pageable, init: Jpql.() -> JpqlQueryable>, @@ -74,6 +91,16 @@ open class KotlinJdslJpqlExecutorImpl( return JpqlEntityManagerUtils.queryForPage(entityManager, query, pageable, renderContext) } + override fun findPage( + dsl: DSL, + pageable: Pageable, + init: DSL.() -> JpqlQueryable>, + ): Page { + val query: SelectQuery = jpql(dsl, init) + + return JpqlEntityManagerUtils.queryForPage(entityManager, query, pageable, renderContext) + } + override fun findSlice( pageable: Pageable, init: Jpql.() -> JpqlQueryable>, @@ -91,6 +118,15 @@ open class KotlinJdslJpqlExecutorImpl( return JpqlEntityManagerUtils.queryForSlice(entityManager, query, pageable, renderContext) } + override fun findSlice( + dsl: DSL, + pageable: Pageable, + init: DSL.() -> JpqlQueryable>, + ): Slice { + val query: SelectQuery = jpql(dsl, init) + + return JpqlEntityManagerUtils.queryForSlice(entityManager, query, pageable, renderContext) + } override fun update( init: Jpql.() -> JpqlQueryable>, ): Int { @@ -107,6 +143,16 @@ open class KotlinJdslJpqlExecutorImpl( return jpaQuery.executeUpdate() } + override fun update( + dsl: DSL, + init: DSL.() -> JpqlQueryable>, + ): Int { + val query: UpdateQuery = jpql(dsl, init) + val jpaQuery = JpqlEntityManagerUtils.createQuery(entityManager, query, renderContext) + + return jpaQuery.executeUpdate() + } + override fun delete( init: Jpql.() -> JpqlQueryable>, ): Int { @@ -122,4 +168,14 @@ open class KotlinJdslJpqlExecutorImpl( return jpaQuery.executeUpdate() } + + override fun delete( + dsl: DSL, + init: DSL.() -> JpqlQueryable>, + ): Int { + val query: DeleteQuery = jpql(dsl, init) + val jpaQuery = JpqlEntityManagerUtils.createQuery(entityManager, query, renderContext) + + return jpaQuery.executeUpdate() + } } 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 38ad6ed93..32af9fb96 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 @@ -44,42 +44,63 @@ class KotlinJdslJpqlExecutorImplTest : WithAssertions { @MockK private lateinit var createSelectQuery2: MyJpql.() -> JpqlQueryable> + @MockK + private lateinit var createSelectQuery3: MyJpqlObject.() -> JpqlQueryable> + @MockK private lateinit var createUpdateQuery1: Jpql.() -> JpqlQueryable> @MockK private lateinit var createUpdateQuery2: MyJpql.() -> JpqlQueryable> + @MockK + private lateinit var createUpdateQuery3: MyJpqlObject.() -> JpqlQueryable> + @MockK private lateinit var createDeleteQuery1: Jpql.() -> JpqlQueryable> @MockK private lateinit var createDeleteQuery2: MyJpql.() -> JpqlQueryable> + @MockK + private lateinit var createDeleteQuery3: MyJpqlObject.() -> JpqlQueryable> + @MockK private lateinit var selectQuery1: SelectQuery @MockK private lateinit var selectQuery2: SelectQuery + @MockK + private lateinit var selectQuery3: SelectQuery + @MockK private lateinit var updateQuery1: UpdateQuery @MockK private lateinit var updateQuery2: UpdateQuery + @MockK + private lateinit var updateQuery3: UpdateQuery + @MockK private lateinit var deleteQuery1: DeleteQuery @MockK private lateinit var deleteQuery2: DeleteQuery + @MockK + private lateinit var deleteQuery3: DeleteQuery + @MockK private lateinit var typedQuery1: TypedQuery @MockK private lateinit var typedQuery2: TypedQuery + @MockK + private lateinit var typedQuery3: TypedQuery + @MockK private lateinit var query1: Query @@ -98,35 +119,58 @@ class KotlinJdslJpqlExecutorImplTest : WithAssertions { } } + private object MyJpqlObject : Jpql() { + override fun equals(other: Any?): Boolean { + if (this === other) return true + return javaClass == other?.javaClass + } + + override fun hashCode(): Int { + return javaClass.hashCode() + } + } + @BeforeEach fun setUp() { mockkObject(JpqlEntityManagerUtils) every { createSelectQuery1.invoke(any()) } returns selectQuery1 every { createSelectQuery2.invoke(any()) } returns selectQuery2 + every { createSelectQuery3.invoke(any()) } returns selectQuery3 every { createUpdateQuery1.invoke(any()) } returns updateQuery1 every { createUpdateQuery2.invoke(any()) } returns updateQuery2 + every { createUpdateQuery3.invoke(any()) } returns updateQuery3 every { createDeleteQuery1.invoke(any()) } returns deleteQuery1 every { createDeleteQuery2.invoke(any()) } returns deleteQuery2 + every { createDeleteQuery3.invoke(any()) } returns deleteQuery3 every { selectQuery1.toQuery() } returns selectQuery1 every { selectQuery2.toQuery() } returns selectQuery2 + every { selectQuery3.toQuery() } returns selectQuery3 every { updateQuery1.toQuery() } returns updateQuery1 every { updateQuery2.toQuery() } returns updateQuery2 + every { updateQuery3.toQuery() } returns updateQuery3 every { deleteQuery1.toQuery() } returns deleteQuery1 every { deleteQuery2.toQuery() } returns deleteQuery2 + every { deleteQuery3.toQuery() } returns deleteQuery3 excludeRecords { createSelectQuery1.invoke(any()) } excludeRecords { createSelectQuery2.invoke(any()) } + excludeRecords { createSelectQuery3.invoke(any()) } excludeRecords { createUpdateQuery1.invoke(any()) } excludeRecords { createUpdateQuery2.invoke(any()) } + excludeRecords { createUpdateQuery3.invoke(any()) } excludeRecords { createDeleteQuery1.invoke(any()) } excludeRecords { createDeleteQuery2.invoke(any()) } + excludeRecords { createDeleteQuery3.invoke(any()) } excludeRecords { selectQuery1.toQuery() } excludeRecords { selectQuery2.toQuery() } + excludeRecords { selectQuery3.toQuery() } excludeRecords { updateQuery1.toQuery() } excludeRecords { updateQuery2.toQuery() } + excludeRecords { updateQuery3.toQuery() } excludeRecords { deleteQuery1.toQuery() } excludeRecords { deleteQuery2.toQuery() } + excludeRecords { deleteQuery3.toQuery() } } @Test @@ -169,6 +213,26 @@ class KotlinJdslJpqlExecutorImplTest : WithAssertions { } } + @Test + fun `findAll() with a dsl object`() { + // given + val list1 = listOf("1", "2", "3") + + every { JpqlEntityManagerUtils.createQuery(any(), any>(), any()) } returns typedQuery3 + every { typedQuery3.resultList } returns list1 + + // when + val actual = sut.findAll(MyJpqlObject, createSelectQuery3) + + // then + assertThat(actual).isEqualTo(list1) + + verifySequence { + JpqlEntityManagerUtils.createQuery(entityManager, selectQuery3, renderContext) + typedQuery3.resultList + } + } + @Test fun `findAll() with a pageable`() { // given @@ -207,6 +271,25 @@ class KotlinJdslJpqlExecutorImplTest : WithAssertions { } } + @Test + fun `findAll() with a dsl object and a pageable`() { + // given + val pageable1 = PageRequest.of(0, 10) + val list1 = listOf("1", "2", "3") + + every { JpqlEntityManagerUtils.queryForList(any(), any>(), any(), any()) } returns list1 + + // when + val actual = sut.findAll(MyJpqlObject, pageable1, createSelectQuery3) + + // then + assertThat(actual).isEqualTo(list1) + + verifySequence { + JpqlEntityManagerUtils.queryForList(entityManager, selectQuery3, pageable1, renderContext) + } + } + @Test fun findPage() { // given @@ -245,6 +328,25 @@ class KotlinJdslJpqlExecutorImplTest : WithAssertions { } } + @Test + fun `findPage() with a dsl object`() { + // given + val pageable1 = PageRequest.of(0, 10) + val page1: Page = mockk() + + every { JpqlEntityManagerUtils.queryForPage(any(), any>(), any(), any()) } returns page1 + + // when + val actual = sut.findPage(MyJpqlObject, pageable1, createSelectQuery3) + + // then + assertThat(actual).isEqualTo(page1) + + verifySequence { + JpqlEntityManagerUtils.queryForPage(entityManager, selectQuery3, pageable1, renderContext) + } + } + @Test fun findSlice() { // given @@ -283,6 +385,25 @@ class KotlinJdslJpqlExecutorImplTest : WithAssertions { } } + @Test + fun `findSlice() with a dsl object`() { + // given + val pageable1 = PageRequest.of(0, 10) + val slice1: Slice = mockk() + + every { JpqlEntityManagerUtils.queryForSlice(any(), any>(), any(), any()) } returns slice1 + + // when + val actual = sut.findSlice(MyJpqlObject, pageable1, createSelectQuery3) + + // then + assertThat(actual).isEqualTo(slice1) + + verifySequence { + JpqlEntityManagerUtils.queryForSlice(entityManager, selectQuery3, pageable1, renderContext) + } + } + @Test fun update() { // given @@ -317,6 +438,23 @@ class KotlinJdslJpqlExecutorImplTest : WithAssertions { } } + @Test + fun `update() with a dsl object`() { + // given + every { JpqlEntityManagerUtils.createQuery(any(), any>(), any()) } returns query1 + every { query1.executeUpdate() } returns 1 + + // when + val actual = sut.update(MyJpqlObject, createUpdateQuery3) + + // then + assertThat(actual).isEqualTo(1) + + verifySequence { + JpqlEntityManagerUtils.createQuery(entityManager, updateQuery3, renderContext) + } + } + @Test fun delete() { // given @@ -350,4 +488,21 @@ class KotlinJdslJpqlExecutorImplTest : WithAssertions { JpqlEntityManagerUtils.createQuery(entityManager, deleteQuery2, renderContext) } } + + @Test + fun `delete() with a dsl object`() { + // given + every { JpqlEntityManagerUtils.createQuery(any(), any>(), any()) } returns query1 + every { query1.executeUpdate() } returns 1 + + // when + val actual = sut.delete(MyJpqlObject, createDeleteQuery3) + + // then + assertThat(actual).isEqualTo(1) + + verifySequence { + JpqlEntityManagerUtils.createQuery(entityManager, deleteQuery3, renderContext) + } + } } 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 af0e18c49..dabc8f07b 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 @@ -32,6 +32,15 @@ interface KotlinJdslJpqlExecutor { init: DSL.() -> JpqlQueryable>, ): List + /** + * Returns all results of the select query. + */ + @SinceJdsl("3.4.0") + fun findAll( + dsl: DSL, + init: DSL.() -> JpqlQueryable>, + ): List + /** * Returns all results of the select query. */ @@ -51,6 +60,16 @@ interface KotlinJdslJpqlExecutor { init: DSL.() -> JpqlQueryable>, ): List + /** + * Returns all results of the select query. + */ + @SinceJdsl("3.4.0") + fun findAll( + dsl: DSL, + pageable: Pageable, + init: DSL.() -> JpqlQueryable>, + ): List + /** * Returns the page of the select query. */ @@ -70,6 +89,16 @@ interface KotlinJdslJpqlExecutor { init: DSL.() -> JpqlQueryable>, ): Page + /** + * Returns the page of the select query. + */ + @SinceJdsl("3.4.0") + fun findPage( + dsl: DSL, + pageable: Pageable, + init: DSL.() -> JpqlQueryable>, + ): Page + /** * Returns the slice of the select query. */ @@ -89,6 +118,16 @@ interface KotlinJdslJpqlExecutor { init: DSL.() -> JpqlQueryable>, ): Slice + /** + * Returns the slice of the select query. + */ + @SinceJdsl("3.4.0") + fun findSlice( + dsl: DSL, + pageable: Pageable, + init: DSL.() -> JpqlQueryable>, + ): Slice + /** * Execute the update query. * @@ -110,6 +149,17 @@ interface KotlinJdslJpqlExecutor { init: DSL.() -> JpqlQueryable>, ): Int + /** + * Execute the update query. + * + * @return the number of entities updated + */ + @SinceJdsl("3.4.0") + fun update( + dsl: DSL, + init: DSL.() -> JpqlQueryable>, + ): Int + /** * Execute the delete query. * @@ -130,4 +180,15 @@ interface KotlinJdslJpqlExecutor { dsl: JpqlDsl.Constructor, init: DSL.() -> JpqlQueryable>, ): Int + + /** + * Execute the delete query. + * + * @return the number of entities deleted + */ + @SinceJdsl("3.4.0") + fun delete( + dsl: DSL, + init: DSL.() -> JpqlQueryable>, + ): Int } 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 49129c1cb..8c54f36dc 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 @@ -40,6 +40,13 @@ open class KotlinJdslJpqlExecutorImpl( return jpaQuery.resultList } + override fun findAll(dsl: DSL, init: DSL.() -> JpqlQueryable>): List { + val query: SelectQuery = jpql(dsl, init) + val jpaQuery = JpqlEntityManagerUtils.createQuery(entityManager, query, renderContext) + + return jpaQuery.resultList + } + override fun findAll( pageable: Pageable, init: Jpql.() -> JpqlQueryable>, @@ -57,6 +64,16 @@ open class KotlinJdslJpqlExecutorImpl( return JpqlEntityManagerUtils.queryForList(entityManager, query, pageable, renderContext) } + override fun findAll( + dsl: DSL, + pageable: Pageable, + init: DSL.() -> JpqlQueryable>, + ): List { + val query: SelectQuery = jpql(dsl, init) + + return JpqlEntityManagerUtils.queryForList(entityManager, query, pageable, renderContext) + } + override fun findPage( pageable: Pageable, init: Jpql.() -> JpqlQueryable>, @@ -74,6 +91,16 @@ open class KotlinJdslJpqlExecutorImpl( return JpqlEntityManagerUtils.queryForPage(entityManager, query, pageable, renderContext) } + override fun findPage( + dsl: DSL, + pageable: Pageable, + init: DSL.() -> JpqlQueryable>, + ): Page { + val query: SelectQuery = jpql(dsl, init) + + return JpqlEntityManagerUtils.queryForPage(entityManager, query, pageable, renderContext) + } + override fun findSlice( pageable: Pageable, init: Jpql.() -> JpqlQueryable>, @@ -91,6 +118,15 @@ open class KotlinJdslJpqlExecutorImpl( return JpqlEntityManagerUtils.queryForSlice(entityManager, query, pageable, renderContext) } + override fun findSlice( + dsl: DSL, + pageable: Pageable, + init: DSL.() -> JpqlQueryable>, + ): Slice { + val query: SelectQuery = jpql(dsl, init) + + return JpqlEntityManagerUtils.queryForSlice(entityManager, query, pageable, renderContext) + } override fun update( init: Jpql.() -> JpqlQueryable>, ): Int { @@ -107,6 +143,16 @@ open class KotlinJdslJpqlExecutorImpl( return jpaQuery.executeUpdate() } + override fun update( + dsl: DSL, + init: DSL.() -> JpqlQueryable>, + ): Int { + val query: UpdateQuery = jpql(dsl, init) + val jpaQuery = JpqlEntityManagerUtils.createQuery(entityManager, query, renderContext) + + return jpaQuery.executeUpdate() + } + override fun delete( init: Jpql.() -> JpqlQueryable>, ): Int { @@ -122,4 +168,14 @@ open class KotlinJdslJpqlExecutorImpl( return jpaQuery.executeUpdate() } + + override fun delete( + dsl: DSL, + init: DSL.() -> JpqlQueryable>, + ): Int { + val query: DeleteQuery = jpql(dsl, init) + val jpaQuery = JpqlEntityManagerUtils.createQuery(entityManager, query, renderContext) + + return jpaQuery.executeUpdate() + } } 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 a71e8b4fd..aaa8c7fc3 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 @@ -44,42 +44,63 @@ class KotlinJdslJpqlExecutorImplTest : WithAssertions { @MockK private lateinit var createSelectQuery2: MyJpql.() -> JpqlQueryable> + @MockK + private lateinit var createSelectQuery3: MyJpqlObject.() -> JpqlQueryable> + @MockK private lateinit var createUpdateQuery1: Jpql.() -> JpqlQueryable> @MockK private lateinit var createUpdateQuery2: MyJpql.() -> JpqlQueryable> + @MockK + private lateinit var createUpdateQuery3: MyJpqlObject.() -> JpqlQueryable> + @MockK private lateinit var createDeleteQuery1: Jpql.() -> JpqlQueryable> @MockK private lateinit var createDeleteQuery2: MyJpql.() -> JpqlQueryable> + @MockK + private lateinit var createDeleteQuery3: MyJpqlObject.() -> JpqlQueryable> + @MockK private lateinit var selectQuery1: SelectQuery @MockK private lateinit var selectQuery2: SelectQuery + @MockK + private lateinit var selectQuery3: SelectQuery + @MockK private lateinit var updateQuery1: UpdateQuery @MockK private lateinit var updateQuery2: UpdateQuery + @MockK + private lateinit var updateQuery3: UpdateQuery + @MockK private lateinit var deleteQuery1: DeleteQuery @MockK private lateinit var deleteQuery2: DeleteQuery + @MockK + private lateinit var deleteQuery3: DeleteQuery + @MockK private lateinit var typedQuery1: TypedQuery @MockK private lateinit var typedQuery2: TypedQuery + @MockK + private lateinit var typedQuery3: TypedQuery + @MockK private lateinit var query1: Query @@ -98,35 +119,58 @@ class KotlinJdslJpqlExecutorImplTest : WithAssertions { } } + private object MyJpqlObject : Jpql() { + override fun equals(other: Any?): Boolean { + if (this === other) return true + return javaClass == other?.javaClass + } + + override fun hashCode(): Int { + return javaClass.hashCode() + } + } + @BeforeEach fun setUp() { mockkObject(JpqlEntityManagerUtils) every { createSelectQuery1.invoke(any()) } returns selectQuery1 every { createSelectQuery2.invoke(any()) } returns selectQuery2 + every { createSelectQuery3.invoke(any()) } returns selectQuery3 every { createUpdateQuery1.invoke(any()) } returns updateQuery1 every { createUpdateQuery2.invoke(any()) } returns updateQuery2 + every { createUpdateQuery3.invoke(any()) } returns updateQuery3 every { createDeleteQuery1.invoke(any()) } returns deleteQuery1 every { createDeleteQuery2.invoke(any()) } returns deleteQuery2 + every { createDeleteQuery3.invoke(any()) } returns deleteQuery3 every { selectQuery1.toQuery() } returns selectQuery1 every { selectQuery2.toQuery() } returns selectQuery2 + every { selectQuery3.toQuery() } returns selectQuery3 every { updateQuery1.toQuery() } returns updateQuery1 every { updateQuery2.toQuery() } returns updateQuery2 + every { updateQuery3.toQuery() } returns updateQuery3 every { deleteQuery1.toQuery() } returns deleteQuery1 every { deleteQuery2.toQuery() } returns deleteQuery2 + every { deleteQuery3.toQuery() } returns deleteQuery3 excludeRecords { createSelectQuery1.invoke(any()) } excludeRecords { createSelectQuery2.invoke(any()) } + excludeRecords { createSelectQuery3.invoke(any()) } excludeRecords { createUpdateQuery1.invoke(any()) } excludeRecords { createUpdateQuery2.invoke(any()) } + excludeRecords { createUpdateQuery3.invoke(any()) } excludeRecords { createDeleteQuery1.invoke(any()) } excludeRecords { createDeleteQuery2.invoke(any()) } + excludeRecords { createDeleteQuery3.invoke(any()) } excludeRecords { selectQuery1.toQuery() } excludeRecords { selectQuery2.toQuery() } + excludeRecords { selectQuery3.toQuery() } excludeRecords { updateQuery1.toQuery() } excludeRecords { updateQuery2.toQuery() } + excludeRecords { updateQuery3.toQuery() } excludeRecords { deleteQuery1.toQuery() } excludeRecords { deleteQuery2.toQuery() } + excludeRecords { deleteQuery3.toQuery() } } @Test @@ -169,6 +213,26 @@ class KotlinJdslJpqlExecutorImplTest : WithAssertions { } } + @Test + fun `findAll() with a dsl object`() { + // given + val list1 = listOf("1", "2", "3") + + every { JpqlEntityManagerUtils.createQuery(any(), any>(), any()) } returns typedQuery3 + every { typedQuery3.resultList } returns list1 + + // when + val actual = sut.findAll(MyJpqlObject, createSelectQuery3) + + // then + assertThat(actual).isEqualTo(list1) + + verifySequence { + JpqlEntityManagerUtils.createQuery(entityManager, selectQuery3, renderContext) + typedQuery3.resultList + } + } + @Test fun `findAll() with a pageable`() { // given @@ -207,6 +271,25 @@ class KotlinJdslJpqlExecutorImplTest : WithAssertions { } } + @Test + fun `findAll() with a dsl object and a pageable`() { + // given + val pageable1 = PageRequest.of(0, 10) + val list1 = listOf("1", "2", "3") + + every { JpqlEntityManagerUtils.queryForList(any(), any>(), any(), any()) } returns list1 + + // when + val actual = sut.findAll(MyJpqlObject, pageable1, createSelectQuery3) + + // then + assertThat(actual).isEqualTo(list1) + + verifySequence { + JpqlEntityManagerUtils.queryForList(entityManager, selectQuery3, pageable1, renderContext) + } + } + @Test fun findPage() { // given @@ -245,6 +328,25 @@ class KotlinJdslJpqlExecutorImplTest : WithAssertions { } } + @Test + fun `findPage() with a dsl object`() { + // given + val pageable1 = PageRequest.of(0, 10) + val page1: Page = mockk() + + every { JpqlEntityManagerUtils.queryForPage(any(), any>(), any(), any()) } returns page1 + + // when + val actual = sut.findPage(MyJpqlObject, pageable1, createSelectQuery3) + + // then + assertThat(actual).isEqualTo(page1) + + verifySequence { + JpqlEntityManagerUtils.queryForPage(entityManager, selectQuery3, pageable1, renderContext) + } + } + @Test fun findSlice() { // given @@ -283,6 +385,25 @@ class KotlinJdslJpqlExecutorImplTest : WithAssertions { } } + @Test + fun `findSlice() with a dsl object`() { + // given + val pageable1 = PageRequest.of(0, 10) + val slice1: Slice = mockk() + + every { JpqlEntityManagerUtils.queryForSlice(any(), any>(), any(), any()) } returns slice1 + + // when + val actual = sut.findSlice(MyJpqlObject, pageable1, createSelectQuery3) + + // then + assertThat(actual).isEqualTo(slice1) + + verifySequence { + JpqlEntityManagerUtils.queryForSlice(entityManager, selectQuery3, pageable1, renderContext) + } + } + @Test fun update() { // given @@ -317,6 +438,23 @@ class KotlinJdslJpqlExecutorImplTest : WithAssertions { } } + @Test + fun `update() with a dsl object`() { + // given + every { JpqlEntityManagerUtils.createQuery(any(), any>(), any()) } returns query1 + every { query1.executeUpdate() } returns 1 + + // when + val actual = sut.update(MyJpqlObject, createUpdateQuery3) + + // then + assertThat(actual).isEqualTo(1) + + verifySequence { + JpqlEntityManagerUtils.createQuery(entityManager, updateQuery3, renderContext) + } + } + @Test fun delete() { // given @@ -350,4 +488,21 @@ class KotlinJdslJpqlExecutorImplTest : WithAssertions { JpqlEntityManagerUtils.createQuery(entityManager, deleteQuery2, renderContext) } } + + @Test + fun `delete() with a dsl object`() { + // given + every { JpqlEntityManagerUtils.createQuery(any(), any>(), any()) } returns query1 + every { query1.executeUpdate() } returns 1 + + // when + val actual = sut.delete(MyJpqlObject, createDeleteQuery3) + + // then + assertThat(actual).isEqualTo(1) + + verifySequence { + JpqlEntityManagerUtils.createQuery(entityManager, deleteQuery3, renderContext) + } + } } From b4c89703ec676f60dd6c500fb98b315b2885d6ae Mon Sep 17 00:00:00 2001 From: Jake Son Date: Sun, 18 Feb 2024 10:13:01 +0900 Subject: [PATCH 2/3] feat: add support for accepting jdsl object as argument from spring data batch --- .../orm/KotlinJdslQueryProviderFactory.kt | 25 +++++++++ .../orm/KotlinJdslQueryProviderFactoryTest.kt | 51 +++++++++++++++++++ .../orm/KotlinJdslQueryProviderFactory.kt | 25 +++++++++ .../orm/KotlinJdslQueryProviderFactoryTest.kt | 51 +++++++++++++++++++ 4 files changed, 152 insertions(+) diff --git a/support/spring-batch-javax/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/batch/javax/item/database/orm/KotlinJdslQueryProviderFactory.kt b/support/spring-batch-javax/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/batch/javax/item/database/orm/KotlinJdslQueryProviderFactory.kt index 64a7fc8d4..876581c50 100644 --- a/support/spring-batch-javax/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/batch/javax/item/database/orm/KotlinJdslQueryProviderFactory.kt +++ b/support/spring-batch-javax/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/batch/javax/item/database/orm/KotlinJdslQueryProviderFactory.kt @@ -61,6 +61,31 @@ class KotlinJdslQueryProviderFactory( return KotlinJdslQueryProvider(query, queryParams, context) } + /** + * Creates a [KotlinJdslQueryProvider] from a select query. + */ + fun create( + dsl: DSL, + init: DSL.() -> JpqlQueryable>, + ): KotlinJdslQueryProvider { + val query = jpql(dsl, init) + + return KotlinJdslQueryProvider(query, emptyMap(), context) + } + + /** + * Creates a [KotlinJdslQueryProvider] from a select query. + */ + fun create( + dsl: DSL, + queryParams: Map, + init: DSL.() -> JpqlQueryable>, + ): KotlinJdslQueryProvider { + val query = jpql(dsl, init) + + return KotlinJdslQueryProvider(query, queryParams, context) + } + /** * Creates a [KotlinJdslQueryProvider] from a select query. */ diff --git a/support/spring-batch-javax/src/test/kotlin/com/linecorp/kotlinjdsl/support/spring/batch/javax/item/database/orm/KotlinJdslQueryProviderFactoryTest.kt b/support/spring-batch-javax/src/test/kotlin/com/linecorp/kotlinjdsl/support/spring/batch/javax/item/database/orm/KotlinJdslQueryProviderFactoryTest.kt index 8bbe82163..a62a20431 100644 --- a/support/spring-batch-javax/src/test/kotlin/com/linecorp/kotlinjdsl/support/spring/batch/javax/item/database/orm/KotlinJdslQueryProviderFactoryTest.kt +++ b/support/spring-batch-javax/src/test/kotlin/com/linecorp/kotlinjdsl/support/spring/batch/javax/item/database/orm/KotlinJdslQueryProviderFactoryTest.kt @@ -30,12 +30,18 @@ class KotlinJdslQueryProviderFactoryTest : WithAssertions { @MockK private lateinit var createSelectQuery2: MyJpql.() -> JpqlQueryable> + @MockK + private lateinit var createSelectQuery3: MyJpqlObject.() -> JpqlQueryable> + @MockK private lateinit var selectQuery1: SelectQuery @MockK private lateinit var selectQuery2: SelectQuery + @MockK + private lateinit var selectQuery3: SelectQuery + private val queryParams1 = mapOf("authorId" to 1L) private class MyJpql : Jpql() { @@ -53,18 +59,33 @@ class KotlinJdslQueryProviderFactoryTest : WithAssertions { } } + private object MyJpqlObject : Jpql() { + override fun equals(other: Any?): Boolean { + if (this === other) return true + return javaClass == other?.javaClass + } + + override fun hashCode(): Int { + return javaClass.hashCode() + } + } + @BeforeEach fun setUp() { every { createSelectQuery1.invoke(any()) } returns selectQuery1 every { createSelectQuery2.invoke(any()) } returns selectQuery2 + every { createSelectQuery3.invoke(any()) } returns selectQuery3 every { selectQuery1.toQuery() } returns selectQuery1 every { selectQuery2.toQuery() } returns selectQuery2 + every { selectQuery3.toQuery() } returns selectQuery3 excludeRecords { createSelectQuery1.invoke(any()) } excludeRecords { createSelectQuery2.invoke(any()) } + excludeRecords { createSelectQuery3.invoke(any()) } excludeRecords { selectQuery1.toQuery() } excludeRecords { selectQuery2.toQuery() } + excludeRecords { selectQuery3.toQuery() } } @Test @@ -127,6 +148,36 @@ class KotlinJdslQueryProviderFactoryTest : WithAssertions { assertThat(actual).isEqualTo(expected) } + @Test + fun `create() with an dsl object and a select queryable`() { + // when + val actual = sut.create(MyJpqlObject, createSelectQuery3) + + // then + val expected = KotlinJdslQueryProvider( + query = jpql(MyJpqlObject, createSelectQuery3), + queryParams = emptyMap(), + context = context, + ) + + assertThat(actual).isEqualTo(expected) + } + + @Test + fun `create() with an dsl object, a select queryable, and query params`() { + // when + val actual = sut.create(MyJpqlObject, queryParams1, createSelectQuery3) + + // then + val expected = KotlinJdslQueryProvider( + query = jpql(MyJpqlObject, createSelectQuery3), + queryParams = queryParams1, + context = context, + ) + + assertThat(actual).isEqualTo(expected) + } + @Test fun `create() with a select query`() { // when diff --git a/support/spring-batch/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/batch/item/database/orm/KotlinJdslQueryProviderFactory.kt b/support/spring-batch/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/batch/item/database/orm/KotlinJdslQueryProviderFactory.kt index b28475c81..1b1b37198 100644 --- a/support/spring-batch/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/batch/item/database/orm/KotlinJdslQueryProviderFactory.kt +++ b/support/spring-batch/src/main/kotlin/com/linecorp/kotlinjdsl/support/spring/batch/item/database/orm/KotlinJdslQueryProviderFactory.kt @@ -61,6 +61,31 @@ class KotlinJdslQueryProviderFactory( return KotlinJdslQueryProvider(query, queryParams, context) } + /** + * Creates a [KotlinJdslQueryProvider] from a select query. + */ + fun create( + dsl: DSL, + init: DSL.() -> JpqlQueryable>, + ): KotlinJdslQueryProvider { + val query = jpql(dsl, init) + + return KotlinJdslQueryProvider(query, emptyMap(), context) + } + + /** + * Creates a [KotlinJdslQueryProvider] from a select query. + */ + fun create( + dsl: DSL, + queryParams: Map, + init: DSL.() -> JpqlQueryable>, + ): KotlinJdslQueryProvider { + val query = jpql(dsl, init) + + return KotlinJdslQueryProvider(query, queryParams, context) + } + /** * Creates a [KotlinJdslQueryProvider] from a select query. */ diff --git a/support/spring-batch/src/test/kotlin/com/linecorp/kotlinjdsl/support/spring/batch/item/database/orm/KotlinJdslQueryProviderFactoryTest.kt b/support/spring-batch/src/test/kotlin/com/linecorp/kotlinjdsl/support/spring/batch/item/database/orm/KotlinJdslQueryProviderFactoryTest.kt index edbbb23d6..6aa72f81f 100644 --- a/support/spring-batch/src/test/kotlin/com/linecorp/kotlinjdsl/support/spring/batch/item/database/orm/KotlinJdslQueryProviderFactoryTest.kt +++ b/support/spring-batch/src/test/kotlin/com/linecorp/kotlinjdsl/support/spring/batch/item/database/orm/KotlinJdslQueryProviderFactoryTest.kt @@ -30,12 +30,18 @@ class KotlinJdslQueryProviderFactoryTest : WithAssertions { @MockK private lateinit var createSelectQuery2: MyJpql.() -> JpqlQueryable> + @MockK + private lateinit var createSelectQuery3: MyJpqlObject.() -> JpqlQueryable> + @MockK private lateinit var selectQuery1: SelectQuery @MockK private lateinit var selectQuery2: SelectQuery + @MockK + private lateinit var selectQuery3: SelectQuery + private val queryParams1 = mapOf("authorId" to 1L) private class MyJpql : Jpql() { @@ -53,18 +59,33 @@ class KotlinJdslQueryProviderFactoryTest : WithAssertions { } } + private object MyJpqlObject : Jpql() { + override fun equals(other: Any?): Boolean { + if (this === other) return true + return javaClass == other?.javaClass + } + + override fun hashCode(): Int { + return javaClass.hashCode() + } + } + @BeforeEach fun setUp() { every { createSelectQuery1.invoke(any()) } returns selectQuery1 every { createSelectQuery2.invoke(any()) } returns selectQuery2 + every { createSelectQuery3.invoke(any()) } returns selectQuery3 every { selectQuery1.toQuery() } returns selectQuery1 every { selectQuery2.toQuery() } returns selectQuery2 + every { selectQuery3.toQuery() } returns selectQuery3 excludeRecords { createSelectQuery1.invoke(any()) } excludeRecords { createSelectQuery2.invoke(any()) } + excludeRecords { createSelectQuery3.invoke(any()) } excludeRecords { selectQuery1.toQuery() } excludeRecords { selectQuery2.toQuery() } + excludeRecords { selectQuery3.toQuery() } } @Test @@ -127,6 +148,36 @@ class KotlinJdslQueryProviderFactoryTest : WithAssertions { assertThat(actual).isEqualTo(expected) } + @Test + fun `create() with an dsl object and a select queryable`() { + // when + val actual = sut.create(MyJpqlObject, createSelectQuery3) + + // then + val expected = KotlinJdslQueryProvider( + query = jpql(MyJpqlObject, createSelectQuery3), + queryParams = emptyMap(), + context = context, + ) + + assertThat(actual).isEqualTo(expected) + } + + @Test + fun `create() with an dsl object, a select queryable, and query params`() { + // when + val actual = sut.create(MyJpqlObject, queryParams1, createSelectQuery3) + + // then + val expected = KotlinJdslQueryProvider( + query = jpql(MyJpqlObject, createSelectQuery3), + queryParams = queryParams1, + context = context, + ) + + assertThat(actual).isEqualTo(expected) + } + @Test fun `create() with a select query`() { // when From be449ceaeda2226454e533eacd3070bf5457093e Mon Sep 17 00:00:00 2001 From: Jake Son Date: Sun, 18 Feb 2024 10:59:22 +0900 Subject: [PATCH 3/3] docs: add description for dsl object --- docs/en/jpql-with-kotlin-jdsl/custom-dsl.md | 37 +++++++++++++++++-- docs/ko/jpql-with-kotlin-jdsl/custom-dsl.md | 39 ++++++++++++++++++--- 2 files changed, 69 insertions(+), 7 deletions(-) diff --git a/docs/en/jpql-with-kotlin-jdsl/custom-dsl.md b/docs/en/jpql-with-kotlin-jdsl/custom-dsl.md index 59787d3c7..b67e89a52 100644 --- a/docs/en/jpql-with-kotlin-jdsl/custom-dsl.md +++ b/docs/en/jpql-with-kotlin-jdsl/custom-dsl.md @@ -9,9 +9,12 @@ You can use them to create your own functions. You can also create your own `Model` that implements [`Expression`](expressions.md) or [`Predicate`](predicates.md) and create a function to return this Model. You can implement [`JpqlSerializer`](custom-dsl.md#serializer) to let Kotlin JDSL to render `Model` to String. -{% hint style="info" %} -You need to implement `JpqlDsl.Constructor` as a companion object so that `jpql()` can recognize the DSL. -{% endhint %} +There are two ways to pass your own DSL to `jpql()`. +The first is to implement `JpqlDsl.Constructor` as a companion object to create a DSL object, and the second is to create a DSL instance. + +### JpqlDsl.Constructor + +With this way, you don't need to create an instance and a new instance is automatically created for each query creation. ```kotlin class MyJpql : Jpql() { @@ -39,6 +42,34 @@ val query = jpql(MyJpql) { } ``` +### Jpql Instance + +With this way, you can reuse a single instance for query creation and utilize dependency injection. + +```kotlin +class MyJpql( + private val encryptor: Encryptor, +) : Jpql() { + fun Path.equalToEncrypted(value: String): Predicate { + val encrypted = encryptor.encrypt(value) + return this.eq(encrypted) + } +} + +val encryptor = Encryptor() +val instance = MyJpql(encryptor) + +val query = jpql(instance) { + select( + entity(Book::class) + ).from( + entity(Book::class) + ).where( + path(Book::title).equalToEncrypted("plain") + ) +} +``` + ### Serializer Implement `JpqlSerializer` and add it to `RenderContext` to render your own `Model` to String. diff --git a/docs/ko/jpql-with-kotlin-jdsl/custom-dsl.md b/docs/ko/jpql-with-kotlin-jdsl/custom-dsl.md index 9f6590c67..6e2eac049 100644 --- a/docs/ko/jpql-with-kotlin-jdsl/custom-dsl.md +++ b/docs/ko/jpql-with-kotlin-jdsl/custom-dsl.md @@ -9,9 +9,12 @@ 그리고 [`Expression`](expressions.md) 혹은 [`Predicate`](predicates.md)를 구현한 나만의 `Model` 클래스를 만들고, 이를 반환하는 함수를 만들 수 있습니다. 이 경우 [`JpqlSerializer`](custom-dsl.md#serializer)를 구현하여 `Model`을 String으로 랜더링하는 방법을 Kotlin JDSL에게 알려줄 수 있습니다. -{% hint style="info" %} -`jpql()`이 DSL을 인식하기 위해서 `JpqlDsl.Constructor`를 companion object로 구현해야 합니다. -{% endhint %} +이렇게 만들어진 나만의 DSL을 `jpql()`에 전달하기 위한 두 가지 방법이 있습니다. +첫 번째는 DSL 객체를 생성하는 `JpqlDsl.Constructor`를 companion object로 구현하는 것이고, 두 번째는 DSL 인스턴스를 생성하는 것입니다. + +### JpqlDsl.Constructor + +이 방법을 사용하면 클래스만 선언해 사용할 수 있으며 쿼리 생성 시마다 새로운 인스턴스를 자동으로 생성합니다. ```kotlin class MyJpql : Jpql() { @@ -39,9 +42,37 @@ val query = jpql(MyJpql) { } ``` +### Jpql Instance + +이 방법을 사용하면 쿼리 생성에 하나의 인스턴스를 재활용할 수 있으며 의존성 주입을 활용할 수 있습니다. + +```kotlin +class MyJpql( + private val encryptor: Encryptor, +) : Jpql() { + fun Path.equalToEncrypted(value: String): Predicate { + val encrypted = encryptor.encrypt(value) + return this.eq(encrypted) + } +} + +val encryptor = Encryptor() +val instance = MyJpql(encryptor) + +val query = jpql(instance) { + select( + entity(Book::class) + ).from( + entity(Book::class) + ).where( + path(Book::title).equalToEncrypted("plain") + ) +} +``` + ### Serializer -나만의 `Model`을 String을 랜더링하기 위해 `JpqlSerializer`를 구현하고 이를 `RenderContext`에 추가해야 합니다. +나만의 `Model`을 String으로 랜더링하기 위해 `JpqlSerializer`를 구현하고 이를 `RenderContext`에 추가해야 합니다. `JpqlSerializer`는 랜더링 로직을 구현할 수 있도록 `JpqlWriter`와 `RenderContext`를 제공합니다. `RenderContext`를 통해 `JpqlRenderSerializer`를 얻어 나의 `Model`이 가지고 있는 다른 `Model`을 랜더링할 수 있습니다.