+
Skip to content

为Table提供扩展以实现实体类查询api #387

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
2fefd8b
Merge pull request #1 from kotlin-orm/master
ForteScarlet Apr 18, 2022
8d616fb
Conditional table implement
ForteScarlet Apr 18, 2022
87b5edc
Merge pull request #2 from kotlin-orm/v3.5.x
ForteScarlet Apr 18, 2022
17fd7c5
Merge remote-tracking branch 'origin/v3.5.x' into conditional_table
ForteScarlet Apr 18, 2022
20caa48
update developer?
ForteScarlet Apr 18, 2022
132b5cb
实现: filterBy, whereBy, filterByOr, whereByOr, and ConditionalTable.
ForteScarlet Apr 18, 2022
5fcf33d
:bulb: 添加源码注释
ForteScarlet Apr 18, 2022
3c5e585
为函数参数增加变量名称
ForteScarlet Apr 18, 2022
8a66455
Conditional table implement
ForteScarlet Apr 18, 2022
b22905d
update developer?
ForteScarlet Apr 18, 2022
e177474
实现: filterBy, whereBy, filterByOr, whereByOr, and ConditionalTable.
ForteScarlet Apr 18, 2022
073f7c5
:bulb: 添加源码注释
ForteScarlet Apr 18, 2022
c90d7b8
为函数参数增加变量名称
ForteScarlet Apr 18, 2022
d4271e2
Merge remote-tracking branch 'origin/conditional_table' into conditio…
ForteScarlet May 27, 2022
c951804
Merge pull request #3 from kotlin-orm/v3.5.x
ForteScarlet May 27, 2022
4f8ab00
Merge branch 'v3.5.x' into conditional_table
ForteScarlet May 27, 2022
adbca32
将 conditionOn 中的实体类参数移动到receiver的位置
ForteScarlet May 27, 2022
d557e27
Merge pull request #4 from kotlin-orm/v3.5.x
ForteScarlet May 31, 2022
0ab3ed9
Merge branch 'v3.5.x' into conditional_table
ForteScarlet May 31, 2022
8a38328
修改注释中的错误符号
ForteScarlet May 31, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions buildSrc/src/main/kotlin/ktorm.maven-publish.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ publishing {
name.set("ccr")
email.set("2938137849@qq.com")
}
developer {
id.set("forte")
name.set("Forte Scarlet")
email.set("ForteScarlet@163.com")
}
}
}
}
Expand Down
312 changes: 312 additions & 0 deletions ktorm-core/src/main/kotlin/org/ktorm/schema/ConditionalTable.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,312 @@
package org.ktorm.schema

import org.ktorm.dsl.Query
import org.ktorm.dsl.and
import org.ktorm.dsl.where
import org.ktorm.entity.*
import kotlin.reflect.KClass


/**
* An extended type of [Table], implemented to allow default condition generation for fields by entity query.
*
* Provide default fill condition rules for entity queries via [Column.conditionOn] or [Column.conditionNotNullOn],
* e.g.
* ```kotlin
* interface Department : Entity<Department> {
* val id: Int
* var name: String
* var location: String
* }
*
*
* object Departments : ConditionalTable<Department>("t_department") {
* val id = int("id").primaryKey().bindTo { it.id }.conditionOn { column, value -> // this: Department
* if (value != null) column eq value else column eq 1
* }
* val name = varchar("name").bindTo { it.name }.conditionNotNullOn { column, value -> // this: Department
* column like "%$value%"
* }
* val location = varchar("location").bindTo { it.location } // No conditions will be generated for this field(column)
* }
* ```
*
* Then, use [filterBy] or [whereBy] to query by entity objects.
* ```kotlin
* val entity = Department {
* // ...
* }
*
* // by EntitySequence
* database.departments
* .filterBy(entity)
* // Other operations...
* .forEach {
* println(it)
* }
*
* // by Query
* database.from(Departments)
* .select()
* .whereBy(Departments, entity)
* // Other operations...
* .forEach {
* println(it)
* }
*
* ```
*
* @see Table
* @see filterBy
* @see whereBy
*
* @author ForteScarlet
*/
public open class ConditionalTable<E : Entity<E>>(
tableName: String,
alias: String? = null,
catalog: String? = null,
schema: String? = null,
entityClass: KClass<E>? = null
) : Table<E>(tableName, alias, catalog, schema, entityClass) {
private val columnConditions = mutableMapOf<String, (E, EntityImplementation) -> ColumnDeclaring<Boolean>?>()

/**
* Provides a query condition for the current field(column) to be used when querying by entity.
* e.g.
* ```kotlin
* object Departments : ConditionalTable<Department>("t_department") {
* val id = int("id").primaryKey().bindTo { it.id }.conditionOn { column, value -> // this: Department
* if (value != null) column eq value else column eq 1
* }
* }
* ```
* @see conditionNotNullOn
*
* @param condition the query condition.
*/
public inline fun <reified C : Any> Column<C>.conditionOn(crossinline condition: E.(column: Column<C>, value: C?) -> ColumnDeclaring<Boolean>): Column<C> {
return saveColumnCondition { entity, entityImpl ->
val value = entityImpl.getColumnValueOrNull(this)
entity.condition(this, value as C?)
}
}


/**
* Provides a query condition for the current field(column) to be used when querying by entity.
* e.g.
* ```kotlin
* object Departments : ConditionalTable<Department>("t_department") {
* val id = int("id").primaryKey().bindTo { it.id }.conditionNotNullOn { column, value -> // this: Department
* column eq value
* }
* }
* ```
* @see conditionOn
*
* @param condition the query condition.
*/
public inline fun <reified C : Any> Column<C>.conditionNotNullOn(crossinline condition: E.(column: Column<C>, value: C) -> ColumnDeclaring<Boolean>): Column<C> {
return saveColumnCondition { entity, entityImpl ->
val value = entityImpl.getColumnValueOrThrow(this)
if (value != null) {
entity.condition(this, value as C)
} else {
null
}
}
}


@PublishedApi
internal fun EntityImplementation.getColumnValueOrNull(column: Column<*>): Any? {
return column.binding?.let { b -> getColumnValue(b) }
}


@PublishedApi
internal fun EntityImplementation.getColumnValueOrThrow(column: Column<*>): Any? {
val binding = column.binding
if (binding != null) {
return getColumnValue(binding)
}

error("Column $column has no bindings to any entity field.")
}

@PublishedApi
internal fun <C : Any> Column<C>.saveColumnCondition(condition: (E, EntityImplementation) -> ColumnDeclaring<Boolean>?): Column<C> {
// merge by 'and'
columnConditions.merge(name, condition) { old, curr ->
{ entity, entityImplementation ->
val condition1 = old(entity, entityImplementation)
val condition2 = curr(entity, entityImplementation)
when {
condition1 == null && condition2 == null -> null
condition1 == null -> condition2
condition2 == null -> condition1
else -> condition1 and condition2
}
}
}
return this
}


/**
* Translate the provided entity classes into query conditions.
*
* @param entity entity of this table.
* @return Query conditions as [ColumnDeclaring]&lt;Boolean&gt;, May be null if no condition is generated.
*/
public fun asCondition(entity: E): ColumnDeclaring<Boolean>? {
val entityImplementation = entity.implementation
return columnConditions.values.fold<(E, EntityImplementation) -> ColumnDeclaring<Boolean>?, ColumnDeclaring<Boolean>?>(
null
) { left, factory ->
val declaring = factory(entity, entityImplementation)
if (left == null) {
declaring
} else {
if (declaring == null) left else left and declaring
}
}
}


}


/**
* Conditional filtering is performed by the specified entity class [conditionEntity] based
* on the conditions defined by each field in [ConditionalTable].
*
* ```kotlin
* database.departments
* .filterBy(entity)
* // Other operations...
* .forEach { println(it) }
* ```
*
* or
*
* ```kotlin
* database.departments
* .filterBy(entity) { table, condition ->
* condition and (table.location eq LocationWrapper("GuangZhou"))
* }
* // Other operations...
* .forEach { println(it) }
* ```
*
* If you want to handle the case when the condition may be null, you can refer to [filterByOr].
*
* @param andThen When the condition exists, you can operate on it.
*
* @see ConditionalTable
* @see filterByOr
*/
public inline fun <E : Entity<E>, T : ConditionalTable<E>> EntitySequence<E, T>.filterBy(
conditionEntity: E,
andThen: ((table: T, condition: ColumnDeclaring<Boolean>) -> ColumnDeclaring<Boolean>) = { _, condition -> condition }
): EntitySequence<E, T> {
return sourceTable.asCondition(conditionEntity)?.let { condition -> filter { table -> andThen(table, condition) } }
?: this

}

/**
* Conditional filtering is performed by the specified entity class [conditionEntity] based
* on the conditions defined in each field of [table] of type [ConditionalTable].
*
* ```kotlin
* database.from(Departments)
* .select()
* .whereBy(Departments, entity)
* // Other operations...
* .forEach { println(it) }
* ```
*
* or
*
* ```kotlin
* database.from(DepartmentsWithCondition)
* .select()
* .whereBy(DepartmentsWithCondition, entity) {
* it and (Departments.location eq LocationWrapper("GuangZhou"))
* }
* .forEach { println(it) }
* ```
*
* If you want to handle the case when the condition may be null, you can refer to [whereByOr].
*
* @param andThen When the condition exists, you can operate on it.
* @see ConditionalTable
* @see whereByOr
*/
public inline fun <E : Entity<E>, T : ConditionalTable<E>> Query.whereBy(
table: T, conditionEntity: E, andThen: ((condition: ColumnDeclaring<Boolean>) -> ColumnDeclaring<Boolean>) = { it }
): Query {
return table.asCondition(conditionEntity)?.let { this.where(andThen(it)) } ?: this
}


/**
* Conditional filtering is performed by the specified entity class [conditionEntity] based
* on the conditions defined by each field in [ConditionalTable].
*
*
* ```kotlin
* database.departments
* .filterByOr(entity) { table, condition ->
* condition?.and(table.location eq LocationWrapper("GuangZhou")) // nullable.
* }
* // Other operations...
* .forEach { println(it) }
* ```
*
* If you only care about the presence of conditions, you can refer to [filterBy].
*
* @param andThen When the condition exists, you can operate on it.
*
* @see ConditionalTable
* @see filterBy
*/
public inline fun <E : Entity<E>, T : ConditionalTable<E>> EntitySequence<E, T>.filterByOr(
conditionEntity: E,
andThen: ((table: T, condition: ColumnDeclaring<Boolean>?) -> ColumnDeclaring<Boolean>?) = { _, condition -> condition }
): EntitySequence<E, T> {
return andThen(sourceTable, sourceTable.asCondition(conditionEntity))?.let { condition -> filter { condition } }
?: this

}

/**
* Conditional filtering is performed by the specified entity class [conditionEntity] based
* on the conditions defined in each field of [table] of type [ConditionalTable].
*
* ```kotlin
* database.from(DepartmentsWithCondition)
* .select()
* .filterByOr(DepartmentsWithCondition, entity) {
* // nullable.
* it?.and(DepartmentsWithCondition.location eq LocationWrapper("GuangZhou"))
* }
* .forEach { println(it) }
* ```
* If you only care about the presence of conditions, you can refer to [whereBy].
*
* @param andThen When the condition exists, you can operate on it.
*
* @see ConditionalTable
* @see whereBy
*/
public inline fun <E : Entity<E>, T : ConditionalTable<E>> Query.whereByOr(
table: T,
conditionEntity: E,
andThen: ((condition: ColumnDeclaring<Boolean>?) -> ColumnDeclaring<Boolean>?) = { it }
): Query {
return andThen(table.asCondition(conditionEntity))?.let { this.where(it) } ?: this
}

Loading
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载