Skip to content

Commit

Permalink
Add QueryComponent terminal operations documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
Thijsiez committed Nov 19, 2024
1 parent ad23e08 commit 9b4191d
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 26 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/ch/icken/processor/ColumnType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class PanacheCompanionBaseProcessor(

val setterExpressionParameterLambdaType = LambdaTypeName.get(
receiver = columnsObjectClassName,
returnType = SetterClassName
returnType = SetterExpressionClassName
)
val initialUpdateComponentType = InitialUpdateComponentClassName
.plusParameter(className)
Expand Down
10 changes: 6 additions & 4 deletions src/main/kotlin/ch/icken/processor/ProcessorCommon.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -59,16 +61,16 @@ abstract class ProcessorCommon(options: Map<String, String>) {
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()
Expand Down
8 changes: 5 additions & 3 deletions src/main/kotlin/ch/icken/query/Column.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@ import ch.icken.query.Expression.BooleanExpression.IsExpression.IsNull

class Column<Columns, Type : Any?>(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
Expand Down
101 changes: 86 additions & 15 deletions src/main/kotlin/ch/icken/query/Component.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -64,44 +64,115 @@ sealed class Component<Entity : PanacheEntityBase, Id : Any, Columns> 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
Expand Down Expand Up @@ -149,7 +220,7 @@ sealed class Component<Entity : PanacheEntityBase, Id : Any, Columns> private co
class InitialUpdateComponent<Entity : PanacheEntityBase, Id : Any, Columns> internal constructor(
companion: PanacheCompanionBase<Entity, Id>,
private val columns: Columns,
private val setters: Array<out Columns.() -> Setter>
private val setters: Array<out Columns.() -> SetterExpression>
) : UpdateComponent<Entity, Id, Columns>(companion) {
//region Chaining operations
/**
Expand All @@ -174,7 +245,7 @@ sealed class Component<Entity : PanacheEntityBase, Id : Any, Columns> 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) {
Expand Down Expand Up @@ -240,10 +311,10 @@ sealed class Component<Entity : PanacheEntityBase, Id : Any, Columns> private co
}

fun <Entity : PanacheEntityBase, Id : Any, Columns>
PanacheCompanionBase<Entity, Id>.update(columns: Columns, setter: Columns.() -> Setter):
PanacheCompanionBase<Entity, Id>.update(columns: Columns, setter: Columns.() -> SetterExpression):
InitialUpdateComponent<Entity, Id, Columns> = InitialUpdateComponent(this, columns, arrayOf(setter))
fun <Entity : PanacheEntityBase, Id : Any, Columns>
PanacheCompanionBase<Entity, Id>.update(columns: Columns, setters: Array<out Columns.() -> Setter>):
PanacheCompanionBase<Entity, Id>.update(columns: Columns, setters: Array<out Columns.() -> SetterExpression>):
InitialUpdateComponent<Entity, Id, Columns> = InitialUpdateComponent(this, columns, setters)

fun <Entity : PanacheEntityBase, Id : Any, Columns>
Expand Down

0 comments on commit 9b4191d

Please sign in to comment.