Skip to content

Commit e9a80ac

Browse files
triple nested binding
1 parent 361e98f commit e9a80ac

File tree

8 files changed

+143
-43
lines changed

8 files changed

+143
-43
lines changed

ktorm-core/src/main/kotlin/me/liuwj/ktorm/entity/EntityDml.kt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ private fun Table<*>.findInsertColumns(entity: Entity<*>): Map<Column<*>, Any?>
6161
val child = entity[binding.property1.name] as Entity<*>?
6262
child?.get(binding.property2.name)?.let { assignments[column] = it }
6363
}
64+
is TripleNestedBinding -> {
65+
val child = entity[binding.property1.name] as Entity<*>?
66+
val grandChild = child?.get(binding.property2.name) as Entity<*>?
67+
grandChild?.get(binding.property3.name)?.let { assignments[column] = it }
68+
}
6469
is ReferenceBinding -> {
6570
val child = entity[binding.onProperty.name] as Entity<*>?
6671
child?.getPrimaryKeyValue(binding.referenceTable)?.let { assignments[column] = it }
@@ -116,12 +121,23 @@ private fun EntityImpl.findChangedColumns(fromTable: Table<*>): Map<Column<*>, A
116121
}
117122
is NestedBinding -> {
118123
val child = this[binding.property1.name] as Entity<*>?
119-
val childChanges = child?.impl?.changedProperties ?: kotlin.collections.emptySet<String>()
124+
val childChanges = child?.impl?.changedProperties ?: emptySet<String>()
120125

121126
if (binding.property1.name in changedProperties || binding.property2.name in childChanges) {
122127
assignments[column] = child?.get(binding.property2.name)
123128
}
124129
}
130+
is TripleNestedBinding -> {
131+
val child = this[binding.property1.name] as Entity<*>?
132+
val childChanges = child?.impl?.changedProperties ?: emptySet<String>()
133+
134+
val grandChild = child?.get(binding.property2.name) as Entity<*>?
135+
val grandChildChanges = grandChild?.impl?.changedProperties ?: emptySet<String>()
136+
137+
if (binding.property1.name in changedProperties || binding.property2.name in childChanges || binding.property3.name in grandChildChanges) {
138+
assignments[column] = grandChild?.get(binding.property3.name)
139+
}
140+
}
125141
is ReferenceBinding -> {
126142
if (binding.onProperty.name in changedProperties) {
127143
val child = this[binding.onProperty.name] as Entity<*>?

ktorm-core/src/main/kotlin/me/liuwj/ktorm/entity/EntityExtensions.kt

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package me.liuwj.ktorm.entity
22

3-
import me.liuwj.ktorm.schema.NestedBinding
4-
import me.liuwj.ktorm.schema.ReferenceBinding
5-
import me.liuwj.ktorm.schema.SimpleBinding
6-
import me.liuwj.ktorm.schema.Table
3+
import me.liuwj.ktorm.schema.*
74
import kotlin.reflect.KClass
85

96
internal fun Entity<*>.getPrimaryKeyValue(fromTable: Table<*>): Any? {
@@ -18,14 +15,19 @@ internal fun Entity<*>.getPrimaryKeyValue(fromTable: Table<*>): Any? {
1815
val child = this[binding.property1.name] as Entity<*>?
1916
return child?.get(binding.property2.name)
2017
}
18+
is TripleNestedBinding -> {
19+
val child = this[binding.property1.name] as Entity<*>?
20+
val grandChild = child?.get(binding.property2.name) as Entity<*>?
21+
return grandChild?.get(binding.property3.name)
22+
}
2123
is ReferenceBinding -> {
2224
val child = this[binding.onProperty.name] as Entity<*>?
2325
return child?.getPrimaryKeyValue(binding.referenceTable)
2426
}
2527
}
2628
}
2729

28-
internal fun Entity<*>.setPrimaryKeyValue(fromTable: Table<*>, value: Any?) {
30+
internal fun Entity<*>.setPrimaryKeyValue(fromTable: Table<*>, value: Any) {
2931
val primaryKey = fromTable.primaryKey ?: kotlin.error("Table ${fromTable.tableName} dosen't have a primary key.")
3032
val binding = primaryKey.binding ?: kotlin.error("Primary key $primaryKey has no bindings to any entity field.")
3133

@@ -42,6 +44,21 @@ internal fun Entity<*>.setPrimaryKeyValue(fromTable: Table<*>, value: Any?) {
4244

4345
child[binding.property2.name] = value
4446
}
47+
is TripleNestedBinding -> {
48+
var child = this[binding.property1.name] as Entity<*>?
49+
if (child == null) {
50+
child = Entity.create(binding.property1.returnType.classifier as KClass<*>, parent = this, fromTable = fromTable)
51+
this[binding.property1.name] = child
52+
}
53+
54+
var grandChild = child[binding.property2.name] as Entity<*>?
55+
if (grandChild == null) {
56+
grandChild = Entity.create(binding.property2.returnType.classifier as KClass<*>, parent = child, fromTable = fromTable)
57+
child[binding.property2.name] = grandChild
58+
}
59+
60+
grandChild[binding.property3.name] = value
61+
}
4562
is ReferenceBinding -> {
4663
var child = this[binding.onProperty.name] as Entity<*>?
4764
if (child == null) {

ktorm-core/src/main/kotlin/me/liuwj/ktorm/entity/EntityFinding.kt

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ fun <E : Entity<E>, K : Any> Table<E>.findMapByIds(ids: Collection<K>): Map<K, E
2222
@Suppress("UNCHECKED_CAST")
2323
fun <E : Entity<E>> Table<E>.findListByIds(ids: Collection<Any>): List<E> {
2424
if (ids.isEmpty()) {
25-
return kotlin.collections.emptyList()
25+
return emptyList()
2626
} else {
2727
val primaryKey = (this.primaryKey as? Column<Any>) ?: kotlin.error("Table $tableName dosen't have a primary key.")
2828
return findList { primaryKey inList ids }
@@ -146,7 +146,24 @@ private fun QueryRowSet.retrieveColumn(column: Column<*>, intoEntity: Entity<*>)
146146
}
147147

148148
child[binding.property2.name] = columnValue
149-
child.discardChanges()
149+
}
150+
}
151+
is TripleNestedBinding -> {
152+
val columnValue = this[column]
153+
if (columnValue != null) {
154+
var child = intoEntity[binding.property1.name] as Entity<*>?
155+
if (child == null) {
156+
child = Entity.create(binding.property1.returnType.classifier as KClass<*>, parent = intoEntity)
157+
intoEntity[binding.property1.name] = child
158+
}
159+
160+
var grandChild = child[binding.property2.name] as Entity<*>?
161+
if (grandChild == null) {
162+
grandChild = Entity.create(binding.property2.returnType.classifier as KClass<*>, parent = child)
163+
child[binding.property2.name] = grandChild
164+
}
165+
166+
grandChild[binding.property3.name] = columnValue
150167
}
151168
}
152169
is ReferenceBinding -> {

ktorm-core/src/main/kotlin/me/liuwj/ktorm/entity/EntityImpl.kt

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package me.liuwj.ktorm.entity
22

3-
import me.liuwj.ktorm.schema.NestedBinding
4-
import me.liuwj.ktorm.schema.ReferenceBinding
5-
import me.liuwj.ktorm.schema.SimpleBinding
6-
import me.liuwj.ktorm.schema.Table
3+
import me.liuwj.ktorm.schema.*
74
import org.slf4j.LoggerFactory
85
import java.io.ObjectInputStream
96
import java.io.ObjectOutputStream
@@ -193,11 +190,24 @@ class EntityImpl(
193190
binding.property.name == name
194191
}
195192
is NestedBinding -> {
196-
val p = parent
197-
if (p == null) {
193+
val parent = this.parent
194+
if (parent == null) {
198195
binding.property1.name == name
199196
} else {
200-
p[binding.property1.name] == this && binding.property2.name == name
197+
parent[binding.property1.name] == this && binding.property2.name == name
198+
}
199+
}
200+
is TripleNestedBinding -> {
201+
val parent = this.parent
202+
if (parent == null) {
203+
binding.property1.name == name
204+
} else {
205+
val grandParent = parent.impl.parent
206+
if (grandParent == null) {
207+
parent[binding.property1.name] == this && binding.property2.name == name
208+
} else {
209+
grandParent[binding.property1.name] == parent && parent[binding.property2.name] == this && binding.property3.name == name
210+
}
201211
}
202212
}
203213
is ReferenceBinding -> {

ktorm-core/src/main/kotlin/me/liuwj/ktorm/schema/Column.kt

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,37 @@ sealed class ColumnBinding
1414
/**
1515
* 将列绑定到一个简单属性
1616
*/
17-
data class SimpleBinding(val property: KProperty1<*, *>) : ColumnBinding()
17+
data class SimpleBinding(
18+
val property: KProperty1<*, *>
19+
) : ColumnBinding()
1820

1921
/**
2022
* 将列绑定到层级嵌套的属性,仅支持两级嵌套
2123
*/
22-
data class NestedBinding(val property1: KProperty1<*, *>, val property2: KProperty1<*, *>) : ColumnBinding()
24+
data class NestedBinding(
25+
val property1: KProperty1<*, *>,
26+
val property2: KProperty1<*, *>
27+
) : ColumnBinding()
28+
29+
/**
30+
* Binding the column to triple nested properties
31+
*/
32+
data class TripleNestedBinding(
33+
val property1: KProperty1<*, *>,
34+
val property2: KProperty1<*, *>,
35+
val property3: KProperty1<*, *>
36+
): ColumnBinding()
2337

2438
/**
2539
* 将列绑定到一个引用表,对应 SQL 中的外键引用,在使用 find* 系列 Entity 扩展函数时,引用表会自动被 left join 联接
2640
*
2741
* @see me.liuwj.ktorm.entity.joinReferencesAndSelect
2842
* @see me.liuwj.ktorm.entity.createEntity
2943
*/
30-
data class ReferenceBinding(val referenceTable: Table<*>, val onProperty: KProperty1<*, *>) : ColumnBinding()
44+
data class ReferenceBinding(
45+
val referenceTable: Table<*>,
46+
val onProperty: KProperty1<*, *>
47+
) : ColumnBinding()
3148

3249
/**
3350
* 列声明

ktorm-core/src/main/kotlin/me/liuwj/ktorm/schema/Table.kt

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -169,10 +169,7 @@ open class Table<E : Entity<E>>(
169169
* @see me.liuwj.ktorm.entity.createEntity
170170
*/
171171
fun <R : Entity<R>> references(referenceTable: Table<R>, onProperty: KProperty1<E, R?>): ColumnRegistration<C> {
172-
if (!onProperty.isAbstract) {
173-
throw IllegalArgumentException("Cannot bind a column to a non-abstract property: $onProperty")
174-
}
175-
172+
checkAbstractProperties(onProperty)
176173
checkCircularReference(referenceTable)
177174
return doBinding(ReferenceBinding(copyReference(referenceTable), onProperty))
178175
}
@@ -201,27 +198,31 @@ open class Table<E : Entity<E>>(
201198
* 将列绑定到一个简单属性
202199
*/
203200
fun bindTo(property: KProperty1<E, C?>): ColumnRegistration<C> {
204-
if (!property.isAbstract) {
205-
throw IllegalArgumentException("Cannot bind a column to a non-abstract property: $property")
206-
}
207-
201+
checkAbstractProperties(property)
208202
return doBinding(SimpleBinding(property))
209203
}
210204

211205
/**
212206
* 将列绑定到层级嵌套的属性,仅支持两级嵌套
213207
*/
214208
fun <R : Entity<R>> bindTo(property1: KProperty1<E, R?>, property2: KProperty1<R, C?>): ColumnRegistration<C> {
215-
if (!property1.isAbstract) {
216-
throw IllegalArgumentException("Cannot bind a column to a non-abstract property: $property1")
217-
}
218-
if (!property2.isAbstract) {
219-
throw IllegalArgumentException("Cannot bind a column to a non-abstract property: $property2")
220-
}
221-
209+
checkAbstractProperties(property1, property2)
222210
return doBinding(NestedBinding(property1, property2))
223211
}
224212

213+
/**
214+
* Binding the column to triple nested properties.
215+
*/
216+
fun <R : Entity<R>, S : Entity<S>> bindTo(
217+
property1: KProperty1<E, R?>,
218+
property2: KProperty1<R, S?>,
219+
property3: KProperty1<S, C?>
220+
): ColumnRegistration<C> {
221+
222+
checkAbstractProperties(property1, property2, property3)
223+
return doBinding(TripleNestedBinding(property1, property2, property3))
224+
}
225+
225226
private fun doBinding(binding: ColumnBinding): ColumnRegistration<C> {
226227
val column = _columns[key] ?: throw NoSuchElementException(key)
227228

@@ -232,6 +233,14 @@ open class Table<E : Entity<E>>(
232233

233234
return this
234235
}
236+
237+
private fun checkAbstractProperties(vararg properties: KProperty1<*, *>) {
238+
for (prop in properties) {
239+
if (!prop.isAbstract) {
240+
throw IllegalArgumentException("Cannot bind a column to a non-abstract property: $prop")
241+
}
242+
}
243+
}
235244
}
236245

237246
/**

ktorm-core/src/test/kotlin/me/liuwj/ktorm/entity/EntityTest.kt

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -206,21 +206,35 @@ class EntityTest : BaseTest() {
206206
assert(employees[2]!!.name == "marry")
207207
}
208208

209+
interface Parent : Entity<Parent> {
210+
var child: Child
211+
}
212+
213+
interface Child : Entity<Child> {
214+
var grandChild: GrandChild
215+
}
216+
217+
interface GrandChild : Entity<GrandChild> {
218+
var id: Int
219+
}
220+
221+
object Parents : Table<Parent>("t_employee") {
222+
val id by int("id").primaryKey().bindTo(Parent::child, Child::grandChild, GrandChild::id)
223+
}
224+
209225
@Test
210226
fun testUpdatePrimaryKey() {
211227
try {
212-
val table = object : Table<Employee>("t_employee") {
213-
val id by int("id").primaryKey().bindTo(Employee::manager, Employee::id)
214-
}
215-
216-
val employee = table.findById(1) ?: throw AssertionError()
217-
assert(employee.manager!!.id == 1)
228+
val parent = Parents.findById(1) ?: throw AssertionError()
229+
assert(parent.getPrimaryKeyValue(Parents) == 1)
230+
assert(parent.child.grandChild.id == 1)
218231

219-
employee.manager = Employee()
232+
parent.child.grandChild.id = 2
220233
throw AssertionError()
221234

222235
} catch (e: UnsupportedOperationException) {
223236
// expected
237+
println(e.message)
224238
}
225239
}
226240
}

ktorm-support-mysql/src/main/kotlin/me/liuwj/ktorm/support/mysql/Functions.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ fun <T : Any> Column<List<T>>.jsonContains(item: T, itemSqlType: SqlType<T>): Fu
1010
// json_contains(column, json_array(item))
1111
return FunctionExpression(
1212
functionName = "json_contains",
13-
arguments = kotlin.collections.listOf(
13+
arguments = listOf(
1414
asExpression(),
1515
FunctionExpression(
1616
functionName = "json_array",
17-
arguments = kotlin.collections.listOf(ArgumentExpression(item, itemSqlType)),
17+
arguments = listOf(ArgumentExpression(item, itemSqlType)),
1818
sqlType = listSqlType
1919
)
2020
),
@@ -47,7 +47,7 @@ fun <T : Any> Column<*>.jsonExtract(path: String, sqlType: SqlType<T>): Function
4747
// json_extract(column, path)
4848
return FunctionExpression(
4949
functionName = "json_extract",
50-
arguments = kotlin.collections.listOf(asExpression(), ArgumentExpression(path, VarcharSqlType)),
50+
arguments = listOf(asExpression(), ArgumentExpression(path, VarcharSqlType)),
5151
sqlType = sqlType
5252
)
5353
}

0 commit comments

Comments
 (0)