Skip to content

Commit 2612fec

Browse files
author
Hyunsik Kang
authored
Support Criteria Delete API (#38)
* Support Criteria Delete API implement missing updateQuery in SpringDataQueryFactory
1 parent 5b67bd4 commit 2612fec

File tree

102 files changed

+2345
-357
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+2345
-357
lines changed

README.md

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,11 @@ val books: List<Row> = queryFactory.listQuery {
9090
}
9191
```
9292

93-
### Update
93+
### Update & Delete
9494

95-
Users can perform bulk update through update query.
96-
* kotlin-jdsl's update does not require from clause. Type T given as generic handles from automatically.
97-
* According to the JPA specification, update does not support join, fetch, group by, order by, limit.
95+
Users can perform bulk update/delete through update/delete query.
96+
* kotlin-jdsl's update/delete does not require from clause. Type T given as generic handles from automatically.
97+
* According to the JPA specification, update/delete does not support join, fetch, group by, order by, limit.
9898
* If you want to use an association mapping as a where condition, you must use [associate](#associate).
9999

100100
```kotlin
@@ -103,9 +103,16 @@ val query: Query = queryFactory.updateQuery<Order> {
103103
setParams(col(Order::purchaserId) to 3000)
104104
}
105105

106-
val udpatedRowsCount: Int = query.executeUpdate()
106+
val updatedRowsCount: Int = query.executeUpdate()
107+
108+
val deleteQuery: Query = queryFactory.deleteQuery<Order> {
109+
where(col(Order::purchaserId).`in`(1000, 2000))
110+
}
111+
112+
val deletedRowsCount: Int = deleteQuery.executeUpdate()
107113
```
108114

115+
109116
### Expression
110117

111118
Kotlin JDSL supports various expressions.
@@ -226,12 +233,17 @@ val query = queryFactory.selectQuery<String> {
226233
associate(OrderAddress::class, Address::class, on(OrderAddress::address))
227234
}
228235

229-
queryFactory.updateQuery<OrderAddress> {
236+
val updatedRowCount = queryFactory.updateQuery<OrderAddress> {
230237
where(col(OrderAddress::id).equal(address1.id))
231238
associate(OrderAddress::class, Address::class, on(OrderAddress::address))
232239
set(col(Address::zipCode), "test")
233240
set(col(Address::baseAddress), "base")
234241
}.executeUpdate()
242+
243+
val deletedRowCount = queryFactory.deleteQuery<OrderAddress> {
244+
where(col(OrderAddress::id).equal(address1.id))
245+
associate(OrderAddress::class, Address::class, on(OrderAddress::address))
246+
}.executeUpdate()
235247
```
236248

237249

core/src/main/kotlin/com/linecorp/kotlinjdsl/QueryFactory.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.linecorp.kotlinjdsl
22

33
import com.linecorp.kotlinjdsl.query.spec.expression.SubqueryExpressionSpec
4+
import com.linecorp.kotlinjdsl.querydsl.CriteriaDeleteQueryDsl
45
import com.linecorp.kotlinjdsl.querydsl.CriteriaQueryDsl
56
import com.linecorp.kotlinjdsl.querydsl.SubqueryDsl
67
import com.linecorp.kotlinjdsl.querydsl.CriteriaUpdateQueryDsl
@@ -13,5 +14,6 @@ interface QueryFactory {
1314
fun <T> typedQuery(returnType: Class<T>, dsl: CriteriaQueryDsl<T>.() -> Unit) = selectQuery(returnType, dsl)
1415
fun <T> selectQuery(returnType: Class<T>, dsl: CriteriaQueryDsl<T>.() -> Unit): TypedQuery<T>
1516
fun <T: Any> updateQuery(target: KClass<T>, dsl: CriteriaUpdateQueryDsl.() -> Unit): Query
17+
fun <T: Any> deleteQuery(target: KClass<T>, dsl: CriteriaDeleteQueryDsl.() -> Unit): Query
1618
fun <T> subquery(returnType: Class<T>, dsl: SubqueryDsl<T>.() -> Unit): SubqueryExpressionSpec<T>
1719
}

core/src/main/kotlin/com/linecorp/kotlinjdsl/QueryFactoryExtensions.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.linecorp.kotlinjdsl
22

3+
import com.linecorp.kotlinjdsl.querydsl.CriteriaDeleteQueryDsl
34
import com.linecorp.kotlinjdsl.querydsl.CriteriaQueryDsl
45
import com.linecorp.kotlinjdsl.querydsl.CriteriaUpdateQueryDsl
56
import com.linecorp.kotlinjdsl.querydsl.SubqueryDsl
@@ -27,5 +28,8 @@ inline fun <reified T> QueryFactory.selectQuery(noinline dsl: CriteriaQueryDsl<T
2728
inline fun <reified T: Any> QueryFactory.updateQuery(noinline dsl: CriteriaUpdateQueryDsl.() -> Unit) =
2829
updateQuery(T::class, dsl)
2930

31+
inline fun <reified T: Any> QueryFactory.deleteQuery(noinline dsl: CriteriaDeleteQueryDsl.() -> Unit) =
32+
deleteQuery(T::class, dsl)
33+
3034
inline fun <reified T> QueryFactory.subquery(noinline dsl: SubqueryDsl<T>.() -> Unit) =
3135
subquery(T::class.java, dsl)

core/src/main/kotlin/com/linecorp/kotlinjdsl/QueryFactoryImpl.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@ package com.linecorp.kotlinjdsl
33
import com.linecorp.kotlinjdsl.query.creator.CriteriaQueryCreator
44
import com.linecorp.kotlinjdsl.query.creator.SubqueryCreator
55
import com.linecorp.kotlinjdsl.query.spec.expression.SubqueryExpressionSpec
6-
import com.linecorp.kotlinjdsl.querydsl.CriteriaQueryDsl
7-
import com.linecorp.kotlinjdsl.querydsl.QueryDslImpl
8-
import com.linecorp.kotlinjdsl.querydsl.SubqueryDsl
9-
import com.linecorp.kotlinjdsl.querydsl.CriteriaUpdateQueryDsl
6+
import com.linecorp.kotlinjdsl.querydsl.*
107
import javax.persistence.Query
118
import javax.persistence.TypedQuery
129
import kotlin.reflect.KClass
@@ -30,6 +27,12 @@ class QueryFactoryImpl(
3027
)
3128
}
3229

30+
override fun <T: Any> deleteQuery(target: KClass<T>, dsl: CriteriaDeleteQueryDsl.() -> Unit): Query {
31+
return criteriaQueryCreator.createQuery(
32+
QueryDslImpl(target.java).apply(dsl).apply { from(target) }.createCriteriaDeleteQuerySpec()
33+
)
34+
}
35+
3336
override fun <T> subquery(
3437
returnType: Class<T>,
3538
dsl: SubqueryDsl<T>.() -> Unit

core/src/main/kotlin/com/linecorp/kotlinjdsl/query/creator/CriteriaQueryCreator.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.linecorp.kotlinjdsl.query.creator
22

3+
import com.linecorp.kotlinjdsl.query.CriteriaDeleteQuerySpec
34
import com.linecorp.kotlinjdsl.query.CriteriaQuerySpec
45
import com.linecorp.kotlinjdsl.query.CriteriaUpdateQuerySpec
56
import javax.persistence.Query
@@ -8,4 +9,5 @@ import javax.persistence.TypedQuery
89
interface CriteriaQueryCreator {
910
fun <T> createQuery(spec: CriteriaQuerySpec<T>): TypedQuery<T>
1011
fun <T> createQuery(spec: CriteriaUpdateQuerySpec<T>): Query
12+
fun <T> createQuery(spec: CriteriaDeleteQuerySpec<T>): Query
1113
}

core/src/main/kotlin/com/linecorp/kotlinjdsl/query/creator/CriteriaQueryCreatorImpl.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package com.linecorp.kotlinjdsl.query.creator
22

3+
import com.linecorp.kotlinjdsl.query.CriteriaDeleteQuerySpec
34
import com.linecorp.kotlinjdsl.query.CriteriaQuerySpec
45
import com.linecorp.kotlinjdsl.query.CriteriaUpdateQuerySpec
56
import javax.persistence.EntityManager
67
import javax.persistence.Query
78
import javax.persistence.TypedQuery
9+
import javax.persistence.criteria.CriteriaDelete
810
import javax.persistence.criteria.CriteriaUpdate
911

1012
class CriteriaQueryCreatorImpl(
@@ -43,4 +45,17 @@ class CriteriaQueryCreatorImpl(
4345
}
4446
}
4547

48+
@Suppress("UNCHECKED_CAST")
49+
override fun <T> createQuery(spec: CriteriaDeleteQuerySpec<T>): Query {
50+
val criteriaBuilder = em.criteriaBuilder
51+
val query = criteriaBuilder.createCriteriaDelete(spec.targetEntity) as CriteriaDelete<Any>
52+
val froms = spec.from.associate(spec.associate, query, spec.targetEntity)
53+
54+
spec.where.apply(froms, query, criteriaBuilder)
55+
56+
return em.createQuery(query).apply {
57+
spec.jpaHint.apply(this)
58+
spec.sqlHint.apply(this)
59+
}
60+
}
4661
}

core/src/test/kotlin/com/linecorp/kotlinjdsl/QueryFactoryExtensionsTest.kt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package com.linecorp.kotlinjdsl
22

33
import com.linecorp.kotlinjdsl.query.spec.expression.SubqueryExpressionSpec
4+
import com.linecorp.kotlinjdsl.querydsl.CriteriaDeleteQueryDsl
45
import com.linecorp.kotlinjdsl.querydsl.CriteriaQueryDsl
6+
import com.linecorp.kotlinjdsl.querydsl.CriteriaUpdateQueryDsl
57
import com.linecorp.kotlinjdsl.querydsl.SubqueryDsl
8+
import com.linecorp.kotlinjdsl.querydsl.expression.col
69
import com.linecorp.kotlinjdsl.test.WithKotlinJdslAssertions
710
import io.mockk.confirmVerified
811
import io.mockk.every
@@ -12,6 +15,7 @@ import io.mockk.verify
1215
import org.junit.jupiter.api.Test
1316
import org.junit.jupiter.api.extension.ExtendWith
1417
import java.util.stream.Stream
18+
import javax.persistence.Query
1519
import javax.persistence.TypedQuery
1620

1721
@ExtendWith(MockKExtension::class)
@@ -125,6 +129,52 @@ internal class QueryFactoryExtensionsTest : WithKotlinJdslAssertions {
125129
confirmVerified(queryFactory)
126130
}
127131

132+
@Test
133+
fun updateQuery() {
134+
// given
135+
every { queryFactory.updateQuery<Data1>(any(), any()) } returns typedQuery
136+
137+
val dsl: CriteriaUpdateQueryDsl.() -> Unit = {
138+
set(col(Data1::id), 1)
139+
where(col(Data1::id).equal(2))
140+
}
141+
142+
// when
143+
val actual: Query = queryFactory.updateQuery<Data1>(dsl)
144+
145+
// then
146+
assertThat(actual).isEqualTo(typedQuery)
147+
148+
verify(exactly = 1) {
149+
queryFactory.updateQuery(Data1::class, dsl)
150+
}
151+
152+
confirmVerified(queryFactory)
153+
}
154+
155+
@Test
156+
fun deleteQuery() {
157+
// given
158+
every { queryFactory.deleteQuery<Data1>(any(), any()) } returns typedQuery
159+
160+
val dsl: CriteriaDeleteQueryDsl.() -> Unit = {
161+
where(col(Data1::id).equal(1))
162+
}
163+
164+
// when
165+
val actual: Query = queryFactory.deleteQuery<Data1>(dsl)
166+
167+
// then
168+
assertThat(actual).isEqualTo(typedQuery)
169+
170+
verify(exactly = 1) {
171+
queryFactory.deleteQuery(Data1::class, dsl)
172+
}
173+
174+
confirmVerified(queryFactory)
175+
}
176+
177+
128178
@Test
129179
fun subquery() {
130180
// given

core/src/test/kotlin/com/linecorp/kotlinjdsl/QueryFactoryImplTest.kt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,38 @@ internal class QueryFactoryImplTest : WithKotlinJdslAssertions {
125125
confirmVerified(criteriaQueryCreator)
126126
}
127127

128+
@Test
129+
fun deleteQuery() {
130+
// given
131+
val query: Query = mockk()
132+
133+
every { criteriaQueryCreator.createQuery(any<QueryDslImpl.CriteriaDeleteQuerySpecImpl<Data1>>()) } returns query
134+
135+
// when
136+
val actual = sut.deleteQuery(Data1::class) {
137+
where(col(Data1::id).equal(1))
138+
}
139+
140+
// then
141+
assertThat(actual).isEqualTo(query)
142+
143+
verify(exactly = 1) {
144+
val columnSpec = ColumnSpec<Int>(EntitySpec(Data1::class.java), Data1::id.name)
145+
criteriaQueryCreator.createQuery(
146+
QueryDslImpl.CriteriaDeleteQuerySpecImpl(
147+
from = FromClause(EntitySpec(Data1::class.java)),
148+
associate = SimpleAssociatedJoinClause(emptyList()),
149+
where = WhereClause(EqualValueSpec(columnSpec, 1)),
150+
jpaHint = JpaQueryHintClauseImpl(emptyMap()),
151+
sqlHint = EmptySqlQueryHintClause,
152+
targetEntity = Data1::class.java
153+
)
154+
)
155+
}
156+
157+
confirmVerified(criteriaQueryCreator)
158+
}
159+
128160
@Test
129161
fun subquery() {
130162
// when

core/src/test/kotlin/com/linecorp/kotlinjdsl/query/creator/CriteriaQueryCreatorImplTest.kt

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.linecorp.kotlinjdsl.query.creator
22

3+
import com.linecorp.kotlinjdsl.query.CriteriaDeleteQuerySpec
34
import com.linecorp.kotlinjdsl.query.CriteriaQuerySpec
45
import com.linecorp.kotlinjdsl.query.CriteriaUpdateQuerySpec
56
import com.linecorp.kotlinjdsl.query.clause.from.FromClause
@@ -25,11 +26,9 @@ import org.junit.jupiter.api.extension.ExtendWith
2526
import javax.persistence.EntityManager
2627
import javax.persistence.Query
2728
import javax.persistence.TypedQuery
28-
import javax.persistence.criteria.CriteriaBuilder
29-
import javax.persistence.criteria.CriteriaQuery
30-
import javax.persistence.criteria.CriteriaUpdate
31-
import javax.persistence.criteria.Path
29+
import javax.persistence.criteria.*
3230

31+
@Suppress("UnusedEquals")
3332
@ExtendWith(MockKExtension::class)
3433
internal class CriteriaQueryCreatorImplTest : WithKotlinJdslAssertions {
3534
@InjectMockKs
@@ -200,4 +199,67 @@ internal class CriteriaQueryCreatorImplTest : WithKotlinJdslAssertions {
200199
em, froms, criteriaBuilder
201200
)
202201
}
202+
203+
@Suppress("UNCHECKED_CAST")
204+
@Test
205+
fun createDeleteQuery() {
206+
data class TestCriteriaDeleteQuerySpec<T>(
207+
override val targetEntity: Class<T>,
208+
override val from: FromClause,
209+
override val associate: SimpleAssociatedJoinClause,
210+
override val where: CriteriaQueryWhereClause,
211+
override val jpaHint: JpaQueryHintClause,
212+
override val sqlHint: SqlQueryHintClause,
213+
) : CriteriaDeleteQuerySpec<T>
214+
// given
215+
val createdQuery: CriteriaDelete<Int> = mockk()
216+
val query: Query = mockk()
217+
218+
val from: FromClause = mockk()
219+
val associate = SimpleAssociatedJoinClause(emptyList())
220+
val where: CriteriaQueryWhereClause = mockk()
221+
val jpaHint: JpaQueryHintClause = mockk()
222+
val sqlHint: SqlQueryHintClause = mockk()
223+
val set: SetClause = mockk()
224+
225+
val spec: CriteriaDeleteQuerySpec<Int> = TestCriteriaDeleteQuerySpec(
226+
from = from,
227+
associate = associate,
228+
where = where,
229+
jpaHint = jpaHint,
230+
sqlHint = sqlHint,
231+
targetEntity = Int::class.java
232+
)
233+
234+
every { em.criteriaBuilder } returns criteriaBuilder
235+
every { em.createQuery(createdQuery) } returns query
236+
every { criteriaBuilder.createCriteriaDelete(Int::class.java) } returns createdQuery
237+
every { from.associate(associate, createdQuery as CriteriaDelete<in Any>, Int::class.java) } returns froms
238+
every { where.apply(froms, createdQuery, criteriaBuilder) } just runs
239+
every { jpaHint.apply(query) } just runs
240+
every { sqlHint.apply(query) } just runs
241+
242+
// when
243+
val actual = sut.createQuery(spec)
244+
245+
// then
246+
assertThat(actual).isEqualTo(query)
247+
248+
verify(exactly = 1) {
249+
em.criteriaBuilder
250+
em.createQuery(createdQuery)
251+
criteriaBuilder.createCriteriaDelete(Int::class.java)
252+
from.associate(associate, createdQuery as CriteriaDelete<in Any>, Int::class.java)
253+
where.apply(froms, createdQuery, criteriaBuilder)
254+
jpaHint.apply(query)
255+
sqlHint.apply(query)
256+
query == query
257+
}
258+
259+
confirmVerified(
260+
from, where, jpaHint, sqlHint, set,
261+
createdQuery, query,
262+
em, froms, criteriaBuilder
263+
)
264+
}
203265
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.linecorp.kotlinjdsl.eclipselink.integration.criteriaquery
2+
3+
import com.linecorp.kotlinjdsl.test.integration.EntityManagerExtension
4+
import com.linecorp.kotlinjdsl.test.integration.criteriaquery.AbstractCriteriaDeleteIntegrationTest
5+
import org.junit.jupiter.api.extension.ExtendWith
6+
import javax.persistence.EntityManager
7+
8+
@ExtendWith(EntityManagerExtension::class)
9+
class EclipselinkCriteriaDeleteIntegrationTest : AbstractCriteriaDeleteIntegrationTest() {
10+
override lateinit var entityManager: EntityManager
11+
}

0 commit comments

Comments
 (0)