All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.micronaut.data.runtime.criteria.KCriteriaBuilderExt.kt Maven / Gradle / Ivy

There is a newer version: 4.10.5
Show 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