@@ -11,9 +11,11 @@ import com.beeproduced.bee.persistent.selection.EmptySelection
11
11
import com.linecorp.kotlinjdsl.QueryFactoryImpl
12
12
import com.linecorp.kotlinjdsl.query.creator.CriteriaQueryCreatorImpl
13
13
import com.linecorp.kotlinjdsl.query.creator.SubqueryCreatorImpl
14
+ import com.linecorp.kotlinjdsl.query.spec.expression.ColumnSpec
14
15
import com.linecorp.kotlinjdsl.query.spec.expression.EntitySpec
15
16
import com.linecorp.kotlinjdsl.querydsl.CriteriaDeleteQueryDsl
16
17
import com.linecorp.kotlinjdsl.querydsl.CriteriaQueryDsl
18
+ import com.linecorp.kotlinjdsl.selectQuery
17
19
import jakarta.persistence.EntityManager
18
20
import org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl
19
21
import org.slf4j.Logger
@@ -41,8 +43,9 @@ abstract class BaseDataRepository<T : DataEntity<T>, ID : Any>(
41
43
protected val relations: Relations
42
44
protected val generated: Generated
43
45
44
- protected val selectIdColumns: CriteriaQueryDsl <* >.() -> Unit
45
- protected val selectCount: CriteriaQueryDsl <Long >.() -> Unit
46
+ protected val selectDistinctIdColumns: CriteriaQueryDsl <* >.() -> Unit
47
+ protected val selectDistinctIdAndOrderColumns: CriteriaQueryDsl <* >.(List <ColumnSpec <* >>) -> Unit
48
+ protected val selectCount: CriteriaQueryDsl <Long >.(forceDistinct: Boolean ) -> Unit
46
49
protected val queryFactory = QueryFactoryImpl (
47
50
criteriaQueryCreator = CriteriaQueryCreatorImpl (entityManager),
48
51
subqueryCreator = SubqueryCreatorImpl ()
@@ -78,8 +81,11 @@ abstract class BaseDataRepository<T : DataEntity<T>, ID : Any>(
78
81
// Is needed, as there is a difference between selecting entity with single primary key
79
82
// and with composite key
80
83
@Suppress(" UNCHECKED_CAST" )
81
- selectIdColumns = QueryBuilder .selectIdsFromEntity(this .type, ids)
82
- as CriteriaQueryDsl <* >.() -> Unit
84
+ selectDistinctIdColumns = QueryBuilder .selectDistinctIdsFromEntity(this .type, ids)
85
+ as CriteriaQueryDsl <* >.() -> Unit
86
+ @Suppress(" UNCHECKED_CAST" )
87
+ selectDistinctIdAndOrderColumns = QueryBuilder .selectDistinctIdAndOrderColumnsFromEntity(this .type, ids)
88
+ as CriteriaQueryDsl <* >.(List <ColumnSpec <* >>) -> Unit
83
89
selectCount = QueryBuilder .selectCount(this .type, ids)
84
90
}
85
91
@@ -199,15 +205,15 @@ abstract class BaseDataRepository<T : DataEntity<T>, ID : Any>(
199
205
dsl : CriteriaQueryDsl <* >.() -> Unit = {}
200
206
): List <T > {
201
207
val testDsl = DummyCriteriaQueryDsl ().apply (dsl)
202
- val query = if (testDsl.hasLimitClause) {
208
+ val query = if (testDsl.hasLimitClause && ! testDsl.hasOrderByClause ) {
203
209
// Query ids first, then query with where!
204
210
// Hibernate cannot use `LIMIT` when joining tables!
205
211
// To be precise, it returns a “limited” result but queries EVERYTHING and limits in memory!
206
212
// https://vladmihalcea.com/fix-hibernate-hhh000104-entity-fetch-pagination-warning-message/
207
213
// https://hibernate.atlassian.net/browse/HHH-15964?focusedCommentId=110593
208
214
val resultIds: List <Any > = queryFactory.selectQuery(info.nonPrimitiveIdType) {
209
215
// select(selectIdColumns)
210
- selectIdColumns (this )
216
+ selectDistinctIdColumns (this )
211
217
from(EntitySpec (type))
212
218
dsl(this )
213
219
}.resultList
@@ -218,7 +224,25 @@ abstract class BaseDataRepository<T : DataEntity<T>, ID : Any>(
218
224
select(EntitySpec (type))
219
225
from(EntitySpec (type))
220
226
where(QueryBuilder .whereClauseFromIds(type, info.compositeKeyMapping, resultIds, ids))
227
+ }
228
+ } else if (testDsl.hasLimitClause && testDsl.hasOrderByClause) {
229
+ val orderByColumns = testDsl.orderByColumns()
230
+ val idsAndOrderBy: List <List <Any >> = queryFactory.selectQuery<List <Any >> {
231
+ selectDistinctIdAndOrderColumns(this , orderByColumns)
232
+ from(EntitySpec (type))
221
233
dsl(this )
234
+ }.resultList
235
+
236
+ if (idsAndOrderBy.isEmpty()) return emptyList()
237
+ val resultIds = idsAndOrderBy.map {
238
+ it.dropLast(orderByColumns.count())
239
+ }
240
+
241
+ queryFactory.selectQuery(type) {
242
+ select(EntitySpec (type))
243
+ from(EntitySpec (type))
244
+ where(QueryBuilder .whereClauseFromIds(type, info.compositeKeyMapping, resultIds, ids))
245
+ orderBy(testDsl.orders)
222
246
}
223
247
} else {
224
248
queryFactory.selectQuery(type) {
@@ -242,8 +266,12 @@ abstract class BaseDataRepository<T : DataEntity<T>, ID : Any>(
242
266
}
243
267
244
268
open fun count (dsl : CriteriaQueryDsl <* >.() -> Unit = {}): Long {
269
+ return count(true , dsl)
270
+ }
271
+
272
+ open fun count (distinct : Boolean , dsl : CriteriaQueryDsl <* >.() -> Unit = {}): Long {
245
273
val count = queryFactory.selectQuery(Long ::class .javaObjectType) {
246
- selectCount(this )
274
+ selectCount(this , distinct )
247
275
from(EntitySpec (type))
248
276
dsl(this )
249
277
}
@@ -255,7 +283,7 @@ abstract class BaseDataRepository<T : DataEntity<T>, ID : Any>(
255
283
*/
256
284
open fun exists (id : ID ): Boolean {
257
285
val resultIds: List <Any > = queryFactory.selectQuery(info.nonPrimitiveIdType) {
258
- selectIdColumns (this )
286
+ selectDistinctIdColumns (this )
259
287
from(EntitySpec (type))
260
288
where(QueryBuilder .whereClauseFromId(type, info.compositeKeyMapping, id, ids))
261
289
}.resultList
@@ -270,7 +298,7 @@ abstract class BaseDataRepository<T : DataEntity<T>, ID : Any>(
270
298
open fun existsAll (ids : Collection <ID >): Boolean {
271
299
val uniqueIds = ids.toSet()
272
300
val resultIds: List <Any > = queryFactory.selectQuery(info.nonPrimitiveIdType) {
273
- selectIdColumns (this )
301
+ selectDistinctIdColumns (this )
274
302
from(EntitySpec (type))
275
303
where(
276
304
QueryBuilder .whereClauseFromIds(
0 commit comments