io.micronaut.data.runtime.criteria.KCriteriaBuilderExt.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of micronaut-data-runtime Show documentation
Show all versions of micronaut-data-runtime Show documentation
Data Repository Support for Micronaut
The newest version!
/*
* Copyright 2017-2022 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.data.runtime.criteria
import io.micronaut.core.annotation.Experimental
import io.micronaut.data.repository.jpa.criteria.CriteriaDeleteBuilder
import io.micronaut.data.repository.jpa.criteria.CriteriaQueryBuilder
import io.micronaut.data.repository.jpa.criteria.CriteriaUpdateBuilder
import io.micronaut.data.repository.jpa.criteria.PredicateSpecification
import io.micronaut.data.repository.jpa.criteria.QuerySpecification
import jakarta.persistence.criteria.*
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty1
@Experimental
fun KProperty.asPath(root: Root): Path = root.get(name)
@Experimental
operator fun Path.get(prop: KProperty1): Path {
return get(prop.name)
}
@Experimental
fun ?> From<*, E>.joinMany(prop: KProperty1, joinType: JoinType? = null): Join {
return if (joinType == null) {
this.join(prop.name)
} else {
this.join(prop.name, joinType)
}
}
@Experimental
fun ?> From<*, E>.joinMany(prop: KProperty1, joinType: JoinType? = null): ListJoin {
return if (joinType == null) {
this.joinList(prop.name)
} else {
this.joinList(prop.name, joinType)
}
}
@Experimental
fun ?> From<*, E>.joinMany(prop: KProperty1, joinType: JoinType? = null): SetJoin {
return if (joinType == null) {
this.joinSet(prop.name)
} else {
this.joinSet(prop.name, joinType)
}
}
@Experimental
fun From<*, E>.joinOne(prop: KProperty1, joinType: JoinType? = null): Join {
return if (joinType == null) {
this.join(prop.name)
} else {
this.join(prop.name, joinType)
}
}
@Experimental
inline fun where(noinline dsl: Where.() -> Unit) = WherePredicate(dsl)
@Experimental
inline fun query(noinline dsl: SelectQuery.() -> Unit) = QueryPredicate(dsl)
@Experimental
inline fun query(noinline dsl: SelectQuery.() -> Unit) = QueryBuilder(dsl, E::class.java, R::class.java)
@Experimental
inline fun update(noinline dsl: UpdateQuery.() -> Unit) = UpdateQueryBuilder(dsl, E::class.java)
@Experimental
inline fun delete(noinline dsl: Where.() -> Unit) = DeleteQueryBuilder(dsl, E::class.java)
@Experimental
class QueryBuilder(private var dsl: SelectQuery.() -> Unit, private var entityType: Class, var resultType: Class) : CriteriaQueryBuilder {
override fun build(criteriaBuilder: CriteriaBuilder): CriteriaQuery {
val criteriaQuery = criteriaBuilder.createQuery(resultType)
val root = criteriaQuery.from(entityType)
val selectQuery = SelectQuery(root, criteriaQuery, criteriaBuilder)
dsl.invoke(selectQuery)
return criteriaQuery
}
}
@Experimental
class UpdateQueryBuilder(private var dsl: UpdateQuery.() -> Unit, private var entityType: Class) : CriteriaUpdateBuilder {
override fun build(criteriaBuilder: CriteriaBuilder): CriteriaUpdate {
val criteriaUpdate = criteriaBuilder.createCriteriaUpdate(entityType)
val updateQuery = UpdateQuery(criteriaUpdate.root, criteriaUpdate, criteriaBuilder)
dsl.invoke(updateQuery)
return criteriaUpdate
}
}
@Experimental
class DeleteQueryBuilder(private var dsl: Where.() -> Unit, private var entityType: Class) : CriteriaDeleteBuilder {
override fun build(criteriaBuilder: CriteriaBuilder): CriteriaDelete {
val criteriaDelete = criteriaBuilder.createCriteriaDelete(entityType)
val whereQuery = Where(criteriaDelete.root, criteriaBuilder)
dsl.invoke(whereQuery)
criteriaDelete.where(whereQuery.toPredicate(true))
return criteriaDelete
}
}
@Experimental
class WherePredicate(var where: Where.() -> Unit) : PredicateSpecification {
override fun toPredicate(root: Root, criteriaBuilder: CriteriaBuilder): Predicate {
val query = Where(root, criteriaBuilder)
where.invoke(query)
return query.toPredicate(true)
}
}
@Experimental
class QueryPredicate(var query: SelectQuery.() -> Unit) : QuerySpecification {
override fun toPredicate(root: Root, criteriaQuery: CriteriaQuery<*>, criteriaBuilder: CriteriaBuilder): Predicate {
val selectQuery = SelectQuery(root, criteriaQuery, criteriaBuilder)
query.invoke(selectQuery)
return requireNotNull(selectQuery.predicate)
}
}
@Experimental
class SelectQuery(var root: Root, var query: CriteriaQuery, var criteriaBuilder: CriteriaBuilder) : WhereQuery(root, criteriaBuilder) {
fun select(prop: KProperty) {
select(prop.asPath(root))
}
fun select(expression: Expression) {
query.select(expression)
}
fun multiselect(vararg props: KProperty) {
query.multiselect(props.map { it.asPath(root) })
}
fun multiselect(vararg props: Selection<*>) {
query.multiselect(*props)
}
fun distinct() {
query.distinct(true)
}
override fun where(dsl: Where.() -> Unit) {
super.where(dsl)
query.where(predicate)
}
fun avg(prop: KProperty): Expression {
return criteriaBuilder.avg(prop.asPath(root))
}
fun sum(prop: KProperty): Expression {
return criteriaBuilder.sum(prop.asPath(root))
}
fun sumAsLong(prop: KProperty): Expression {
return criteriaBuilder.sumAsLong(prop.asPath(root))
}
fun sumAsDouble(prop: KProperty): Expression {
return criteriaBuilder.sumAsDouble(prop.asPath(root))
}
fun Selection.alias(prop: KProperty): Selection {
return alias(prop.name)
}
fun max(prop: KProperty): Expression {
return criteriaBuilder.max(prop.asPath(root))
}
fun min(prop: KProperty): Expression {
return criteriaBuilder.min(prop.asPath(root))
}
fun ?> greatest(prop: KProperty): Expression {
return criteriaBuilder.greatest(prop.asPath(root))
}
fun ?> least(prop: KProperty): Expression {
return criteriaBuilder.least(prop.asPath(root))
}
fun count(prop: KProperty): Expression {
return criteriaBuilder.count(prop.asPath(root))
}
fun countDistinct(prop: KProperty): Expression {
return criteriaBuilder.countDistinct(prop.asPath(root))
}
}
@Experimental
class UpdateQuery(var root: Root, var query: CriteriaUpdate, var criteriaBuilder: CriteriaBuilder) : WhereQuery(root, criteriaBuilder) {
fun set(prop: KProperty, value: V) {
query.set(prop.asPath(root), value)
}
override fun where(dsl: Where.() -> Unit) {
super.where(dsl)
query.where(predicate)
}
}
@Experimental
open class WhereQuery(private var root: Root, private var criteriaBuilder: CriteriaBuilder) {
var predicate: Predicate? = null
open fun where(dsl: Where.() -> Unit) {
val w = Where(root, criteriaBuilder)
w.dsl()
predicate = w.toPredicate()
}
}
@Experimental
class Where(var root: Root, var criteriaBuilder: CriteriaBuilder) {
private var predicates = mutableListOf()
fun and(dsl: Where.() -> Unit) {
val w = Where(root, criteriaBuilder)
w.dsl()
addPredicate(w.toPredicate(true))
}
fun or(dsl: Where.() -> Unit) {
val w = Where(root, criteriaBuilder)
w.dsl()
addPredicate(w.toPredicate(false))
}
fun not(dsl: Where.() -> Unit) {
val w = Where(root, criteriaBuilder)
w.dsl()
addPredicate(w.toPredicate(true).not())
}
fun Expression.equalsTrue() = addPredicate(criteriaBuilder.isTrue(this))
fun Expression.equalsFalse() = addPredicate(criteriaBuilder.isFalse(this))
fun Expression<*>.equalsNull() = addPredicate(criteriaBuilder.isNull(this))
fun Expression<*>.notEqualsNull() = addPredicate(criteriaBuilder.isNotNull(this))
infix fun Expression<*>.equal(other: Any?) = addPredicate(criteriaBuilder::equal, other)
infix fun Expression<*>.equal(other: Expression<*>) = addPredicateExp(criteriaBuilder::equal, other)
infix fun Expression<*>.eq(other: Any?) = addPredicate(criteriaBuilder::equal, other)
infix fun Expression<*>.eq(other: Expression<*>) = addPredicateExp(criteriaBuilder::equal, other)
infix fun Expression<*>.notEqual(other: Any?) = addPredicate(criteriaBuilder::notEqual, other)
infix fun Expression<*>.notEqual(other: Expression<*>) = addPredicateExp(criteriaBuilder::notEqual, other)
infix fun Expression<*>.ne(other: Any?) = addPredicate(criteriaBuilder::notEqual, other)
infix fun Expression<*>.ne(other: Expression<*>) = addPredicateExp(criteriaBuilder::notEqual, other)
infix fun > Expression.greaterThan(other: Y) = addComparablePredicate(criteriaBuilder::greaterThan, other)
infix fun > Expression.greaterThan(other: Expression) = addComparablePredicate(criteriaBuilder::greaterThan, other)
infix fun > Expression.greaterThanOrEqualTo(other: Y) = addComparablePredicate(criteriaBuilder::greaterThanOrEqualTo, other)
infix fun > Expression.greaterThanOrEqualTo(other: Expression) = addComparablePredicate(criteriaBuilder::greaterThanOrEqualTo, other)
infix fun > Expression.lessThan(other: Y) = addComparablePredicate(criteriaBuilder::lessThan, other)
infix fun > Expression.lessThan(other: Expression) = addComparablePredicate(criteriaBuilder::lessThan, other)
infix fun > Expression.lessThanOrEqualTo(other: Y) = addComparablePredicate(criteriaBuilder::lessThanOrEqualTo, other)
infix fun > Expression.lessThanOrEqualTo(other: Expression) = addComparablePredicate(criteriaBuilder::lessThanOrEqualTo, other)
fun > Expression.between(x: Y, y: Y) = addPredicate(criteriaBuilder.between(this, x, y))
// Not Supported yet
// fun > Expression.between(x: Expression, y: Expression) = addPredicate(criteriaBuilder.between(this, x, y))
infix fun Expression.gt(other: Y) = addNumberPredicate(criteriaBuilder::gt, other)
infix fun Expression.gt(other: Expression) = addNumberPredicate(criteriaBuilder::gt, other)
infix fun Expression.ge(other: Y) = addNumberPredicate(criteriaBuilder::ge, other)
infix fun Expression.ge(other: Expression) = addNumberPredicate(criteriaBuilder::ge, other)
infix fun Expression.lt(other: Y) = addNumberPredicate(criteriaBuilder::lt, other)
infix fun Expression.lt(other: Expression) = addNumberPredicate(criteriaBuilder::lt, other)
infix fun Expression.le(other: Y) = addNumberPredicate(criteriaBuilder::le, other)
infix fun Expression.le(other: Expression) = addNumberPredicate(criteriaBuilder::le, other)
infix fun Expression.inList(other: Collection) = addInListPredicate(other)
private inline fun Expression.addPredicate(fn: (Expression, Y) -> Predicate, value: Y) {
addPredicate(fn.invoke(this, value))
}
private inline fun Expression.addPredicateExp(fn: (Expression, Expression) -> Predicate, value: Expression) {
addPredicate(fn.invoke(this, value))
}
private inline fun > Expression.addComparablePredicate(fn: (Expression, Y) -> Predicate, value: Y) {
addPredicate(fn.invoke(this, value))
}
private inline fun > Expression.addComparablePredicate(fn: (Expression, Expression) -> Predicate, value: Expression) {
addPredicate(fn.invoke(this, value))
}
private inline fun Expression.addNumberPredicate(fn: (Expression, K) -> Predicate, value: K) {
addPredicate(fn.invoke(this, value))
}
private inline fun Expression.addNumberPredicate(fn: (Expression, Expression) -> Predicate, value: Expression) {
addPredicate(fn.invoke(this, value))
}
private fun Expression.addInListPredicate(values: Collection) {
addPredicate(criteriaBuilder.`in`(this).apply { values.forEach { value(it) } })
}
private fun addPredicate(predicate: Predicate) {
predicates.add(predicate)
}
fun toPredicate(isAnd: Boolean = true): Predicate = if (isAnd) {
criteriaBuilder.and(*predicates.toTypedArray())
} else {
criteriaBuilder.or(*predicates.toTypedArray())
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy