diff --git a/README.md b/README.md index b129745..ea5c10b 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ project.afterEvaluate { - Supports `like` and `notLike` expressions in a null-safe way for String columns - Supports `and` and `or` expressions for building up a query - Adds the `PanacheSingleResult` sealed class and `singleResultSafe()` extension function to return a single result without throwing exceptions. - - Allows you to handle no/multiple results with a `when (result) {...}` block instead of try-catching + - Allows you to handle no/multiple results with a `when (result) { ... }` block instead of try-catching ### Code Generation - Generate `Column`s for non-transient and non-mapped fields in Panache entities - Generate query entry point extension functions for entities with Panache companion objects diff --git a/src/main/kotlin/ch/icken/processor/ColumnType.kt b/src/main/kotlin/ch/icken/processor/ColumnType.kt index db65fbc..e704485 100644 --- a/src/main/kotlin/ch/icken/processor/ColumnType.kt +++ b/src/main/kotlin/ch/icken/processor/ColumnType.kt @@ -26,9 +26,9 @@ import kotlin.reflect.KClass * The intended use case for this annotation is when the column's Kotlin type is different from your database. * When using JPA's [@Convert][jakarta.persistence.Convert] annotation, Hibernate converts to and from the type as * specified by the [@Converter][jakarta.persistence.Converter]. Panache uses this other type in its queries, - * so it needs to be known during [Column][ch.icken.query.Column] generation. + * so it needs to be known during `Column` generation. * - * @property type the generic type to be used by the generated [Column][ch.icken.query.Column] + * @property type the generic type to be used by the generated `Column` */ @Retention(SOURCE) @Target(PROPERTY) diff --git a/src/main/kotlin/ch/icken/processor/PanacheCompanionBaseProcessor.kt b/src/main/kotlin/ch/icken/processor/PanacheCompanionBaseProcessor.kt index 666c2b6..f568786 100644 --- a/src/main/kotlin/ch/icken/processor/PanacheCompanionBaseProcessor.kt +++ b/src/main/kotlin/ch/icken/processor/PanacheCompanionBaseProcessor.kt @@ -73,7 +73,7 @@ class PanacheCompanionBaseProcessor( val setterExpressionParameterLambdaType = LambdaTypeName.get( receiver = columnsObjectClassName, - returnType = SetterClassName + returnType = SetterExpressionClassName ) val initialUpdateComponentType = InitialUpdateComponentClassName .plusParameter(className) diff --git a/src/main/kotlin/ch/icken/processor/ProcessorCommon.kt b/src/main/kotlin/ch/icken/processor/ProcessorCommon.kt index 7bad316..9f45d7f 100644 --- a/src/main/kotlin/ch/icken/processor/ProcessorCommon.kt +++ b/src/main/kotlin/ch/icken/processor/ProcessorCommon.kt @@ -18,7 +18,9 @@ package ch.icken.processor import ch.icken.query.Column import ch.icken.query.Component.QueryComponent -import ch.icken.query.Component.UpdateComponent +import ch.icken.query.Component.UpdateComponent.InitialUpdateComponent +import ch.icken.query.Component.UpdateComponent.InitialUpdateComponent.SetterExpression +import ch.icken.query.Component.UpdateComponent.LogicalUpdateComponent import ch.icken.query.Expression import ch.icken.query.PanacheSingleResult import com.squareup.kotlinpoet.AnnotationSpec @@ -59,16 +61,16 @@ abstract class ProcessorCommon(options: Map) { internal val ColumnClassName = Column::class.asClassName() internal val ExpressionClassName = Expression::class.asClassName() internal val GeneratedClassName = Generated::class.asClassName() - internal val InitialUpdateComponentClassName = UpdateComponent.InitialUpdateComponent::class.asClassName() + internal val InitialUpdateComponentClassName = InitialUpdateComponent::class.asClassName() internal val IntClassName = Int::class.asClassName() internal val JvmNameClassName = JvmName::class.asClassName() internal val ListClassName = List::class.asClassName() - internal val LogicalUpdateComponentClassName = UpdateComponent.LogicalUpdateComponent::class.asClassName() + internal val LogicalUpdateComponentClassName = LogicalUpdateComponent::class.asClassName() internal val LongClassName = Long::class.asClassName() internal val PanacheQueryClassName = PanacheQuery::class.asClassName() internal val PanacheSingleResultClassName = PanacheSingleResult::class.asClassName() internal val QueryComponentClassName = QueryComponent::class.asClassName() - internal val SetterClassName = UpdateComponent.InitialUpdateComponent.Setter::class.asClassName() + internal val SetterExpressionClassName = SetterExpression::class.asClassName() internal val SortClassName = Sort::class.asClassName() internal val StreamClassName = Stream::class.asClassName() internal val StringClassName = String::class.asClassName() diff --git a/src/main/kotlin/ch/icken/query/Column.kt b/src/main/kotlin/ch/icken/query/Column.kt index 4889c24..fc77de2 100644 --- a/src/main/kotlin/ch/icken/query/Column.kt +++ b/src/main/kotlin/ch/icken/query/Column.kt @@ -26,11 +26,13 @@ import ch.icken.query.Expression.BooleanExpression.IsExpression.IsNull class Column(internal val name: String) { /** - * Adds a setter expression to this update query + * Creates a [SetterExpression][ch.icken.query.Component.UpdateComponent.InitialUpdateComponent.SetterExpression] + * for this column * - * @param value the new value for this column + * @param value the new value for this column + * @return a new `SetterExpression` instance */ - infix fun set(value: Type) = Component.UpdateComponent.InitialUpdateComponent.Setter(name, value) + infix fun set(value: Type) = Component.UpdateComponent.InitialUpdateComponent.SetterExpression(name, value) } //region eq diff --git a/src/main/kotlin/ch/icken/query/Component.kt b/src/main/kotlin/ch/icken/query/Component.kt index 7c9c8ab..94e0841 100644 --- a/src/main/kotlin/ch/icken/query/Component.kt +++ b/src/main/kotlin/ch/icken/query/Component.kt @@ -19,7 +19,7 @@ package ch.icken.query import ch.icken.query.Component.QueryComponent import ch.icken.query.Component.QueryComponent.InitialQueryComponent import ch.icken.query.Component.UpdateComponent.InitialUpdateComponent -import ch.icken.query.Component.UpdateComponent.InitialUpdateComponent.Setter +import ch.icken.query.Component.UpdateComponent.InitialUpdateComponent.SetterExpression import io.quarkus.hibernate.orm.panache.kotlin.PanacheCompanionBase import io.quarkus.hibernate.orm.panache.kotlin.PanacheEntityBase import io.quarkus.panache.common.Sort @@ -64,44 +64,115 @@ sealed class Component private co //region Terminal operations /** - * TODO + * Counts the number of entities matching the preceding query. + * + * @return the number of entities counted + * @see io.quarkus.hibernate.orm.panache.kotlin.PanacheCompanionBase.count */ fun count() = withCompiled { companion.count(component, parameters) } /** - * TODO + * Deletes all entities matching the preceding query. + * + * WARNING: the default Panache implementation behind this function uses a bulk delete query + * and ignores cascading rules from the JPA model. + * + * @return the number of entities deleted + * @see io.quarkus.hibernate.orm.panache.kotlin.PanacheCompanionBase.delete */ fun delete() = withCompiled { companion.delete(component, parameters) } /** - * TODO + * Finds entities matching the preceding query. + * + * May be used to chain functionality not (yet) abstracted by this library, like + * [page][io.quarkus.hibernate.orm.panache.kotlin.PanacheQuery.page] and + * [project][io.quarkus.hibernate.orm.panache.kotlin.PanacheQuery.project]. + * + * @return a new [PanacheQuery][io.quarkus.hibernate.orm.panache.kotlin.PanacheQuery] instance + * @see io.quarkus.hibernate.orm.panache.kotlin.PanacheCompanionBase.find */ fun find() = withCompiled { companion.find(component, parameters) } /** - * TODO + * Finds entities matching the preceding query and the given sort options. + * + * May be used to chain functionality not (yet) abstracted by this library, like + * [page][io.quarkus.hibernate.orm.panache.kotlin.PanacheQuery.page] and + * [project][io.quarkus.hibernate.orm.panache.kotlin.PanacheQuery.project]. + * + * @param sort the sort strategy to use + * @return a new [PanacheQuery][io.quarkus.hibernate.orm.panache.kotlin.PanacheQuery] instance + * @see io.quarkus.hibernate.orm.panache.kotlin.PanacheCompanionBase.find */ fun find(sort: Sort) = withCompiled { companion.find(component, sort, parameters) } /** - * TODO + * Streams all entities matching the preceding query. This function is a shortcut for `find().stream()`. + * + * WARNING: this function requires a transaction to be active, + * otherwise the underlying cursor may be closed before the end of the stream. + * + * @return a new [Stream][java.util.stream.Stream] instance containing all results, without paging + * @see io.quarkus.hibernate.orm.panache.kotlin.PanacheCompanionBase.stream */ fun stream() = withCompiled { companion.stream(component, parameters) } /** - * TODO + * Streams all entities matching the preceding query and the given sort options. + * This function is a shortcut for `find(sort).stream()`. + * + * WARNING: this function requires a transaction to be active, + * otherwise the underlying cursor may be closed before the end of the stream. + * + * @param sort the sort strategy to use + * @return a new [Stream][java.util.stream.Stream] instance containing all results, without paging + * @see io.quarkus.hibernate.orm.panache.kotlin.PanacheCompanionBase.stream */ fun stream(sort: Sort) = withCompiled { companion.stream(component, sort, parameters) } /** - * TODO + * Finds a single result matching the preceding query, or throws if there is not exactly one. + * This function is a shortcut for `find().singleResult()`. + * + * @return the single result + * @throws jakarta.persistence.NoResultException when there is no result + * @throws jakarta.persistence.NonUniqueResultException when there are multiple results + * @see io.quarkus.hibernate.orm.panache.kotlin.PanacheQuery.singleResult */ fun single() = find().singleResult() /** - * TODO + * Finds a single result matching the preceding query, but does not throw if there is not exactly one. + * This function is a shortcut for `find().singleResultSafe()`. + * + * This function will always return one of the following: + * - [NoResult][ch.icken.query.PanacheSingleResult.NoResult] when there is no result + * - A new instance of [Result][ch.icken.query.PanacheSingleResult.Result] when there is exactly one result + * - [NotUnique][ch.icken.query.PanacheSingleResult.NotUnique] when there are multiple results + * + * Below is an example of how this result could be used: + * ``` + * val result = User.where { ... } + * val user = when (result) { + * NoResult -> null //or maybe return + * is Result -> result.value + * NotUnique -> throw IllegalStateException() + * } + * ``` + * + * @return a [PanacheSingleResult][ch.icken.query.PanacheSingleResult] instance + * @see ch.icken.query.singleResultSafe */ fun singleSafe() = find().singleResultSafe() /** - * TODO + * Finds all entities matching the preceding query. This function is a shortcut for `find().list()`. + * + * @return a new [List][kotlin.collections.List] instance containing all results, without paging + * @see io.quarkus.hibernate.orm.panache.kotlin.PanacheQuery.list */ fun multiple() = find().list() /** - * TODO + * Finds all entities matching the preceding query and the given sort options. + * This function is a shortcut for `find(sort).list()`. + * + * @param sort the sort strategy to use + * @return a new [List][kotlin.collections.List] instance containing all results, without paging + * @see io.quarkus.hibernate.orm.panache.kotlin.PanacheQuery.list */ fun multiple(sort: Sort) = find(sort).list() //endregion @@ -149,7 +220,7 @@ sealed class Component private co class InitialUpdateComponent internal constructor( companion: PanacheCompanionBase, private val columns: Columns, - private val setters: Array Setter> + private val setters: Array SetterExpression> ) : UpdateComponent(companion) { //region Chaining operations /** @@ -174,7 +245,7 @@ sealed class Component private co ) } - class Setter internal constructor(private val columnName: String, private val value: Any?) { + class SetterExpression internal constructor(private val columnName: String, private val value: Any?) { private val parameterName: String = generateParameterName() internal fun compile(): Compiled = when (value) { @@ -240,10 +311,10 @@ sealed class Component private co } fun - PanacheCompanionBase.update(columns: Columns, setter: Columns.() -> Setter): + PanacheCompanionBase.update(columns: Columns, setter: Columns.() -> SetterExpression): InitialUpdateComponent = InitialUpdateComponent(this, columns, arrayOf(setter)) fun - PanacheCompanionBase.update(columns: Columns, setters: Array Setter>): + PanacheCompanionBase.update(columns: Columns, setters: Array SetterExpression>): InitialUpdateComponent = InitialUpdateComponent(this, columns, setters) fun