Skip to content

Commit 8b2eabf

Browse files
column binding handler
1 parent e497fb8 commit 8b2eabf

File tree

4 files changed

+106
-70
lines changed

4 files changed

+106
-70
lines changed

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

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

33
import me.liuwj.ktorm.schema.Table
4-
import org.slf4j.LoggerFactory
4+
import me.liuwj.ktorm.schema.defaultValue
5+
import me.liuwj.ktorm.schema.kotlinProperty
56
import java.io.ObjectInputStream
67
import java.io.ObjectOutputStream
78
import java.io.Serializable
@@ -12,13 +13,6 @@ import java.util.*
1213
import kotlin.collections.LinkedHashMap
1314
import kotlin.collections.LinkedHashSet
1415
import kotlin.reflect.KClass
15-
import kotlin.reflect.KMutableProperty
16-
import kotlin.reflect.KProperty
17-
import kotlin.reflect.full.createInstance
18-
import kotlin.reflect.full.isSubclassOf
19-
import kotlin.reflect.full.memberProperties
20-
import kotlin.reflect.jvm.javaGetter
21-
import kotlin.reflect.jvm.javaSetter
2216
import kotlin.reflect.jvm.jvmName
2317
import kotlin.reflect.jvm.kotlinFunction
2418

@@ -36,7 +30,6 @@ internal class EntityImplementation(
3630

3731
companion object {
3832
private const val serialVersionUID = 1L
39-
private val logger = LoggerFactory.getLogger(EntityImplementation::class.java)
4033
private val defaultImplsCache: MutableMap<Method, Method> = Collections.synchronizedMap(WeakHashMap())
4134
}
4235

@@ -116,68 +109,10 @@ internal class EntityImplementation(
116109
throw e.targetException
117110

118111
} catch (e: Throwable) {
119-
logger.error("proxy: ${proxy.javaClass}: $proxy")
120-
logger.error("method: $method")
121-
logger.error("args: ${args?.contentToString()}")
122-
logger.error("arg types: ${args?.map { it.javaClass }}")
123-
logger.error("implementation: $impl")
124-
logger.error("cache: $defaultImplsCache")
125112
throw e
126113
}
127114
}
128115

129-
private val Method.kotlinProperty: Pair<KProperty<*>, Boolean>? get() {
130-
for (prop in entityClass.memberProperties) {
131-
if (prop.javaGetter == this) {
132-
return prop to true
133-
}
134-
if (prop is KMutableProperty<*> && prop.javaSetter == this) {
135-
return prop to false
136-
}
137-
}
138-
return null
139-
}
140-
141-
private val KClass<*>.defaultValue: Any get() {
142-
val value: Any = try {
143-
when {
144-
this == Boolean::class -> false
145-
this == Char::class -> 0.toChar()
146-
this == Byte::class -> 0.toByte()
147-
this == Short::class -> 0.toShort()
148-
this == Int::class -> 0
149-
this == Long::class -> 0L
150-
this == Float::class -> 0.0F
151-
this == Double::class -> 0.0
152-
this == String::class -> ""
153-
this.isSubclassOf(Entity::class) -> Entity.create(this)
154-
this.java.isEnum -> this.java.enumConstants[0]
155-
this.java.isArray -> this.java.componentType.createArray(0)
156-
this == Set::class || this == MutableSet::class -> LinkedHashSet<Any?>()
157-
this == List::class || this == MutableList::class -> ArrayList<Any?>()
158-
this == Collection::class || this == MutableCollection::class -> ArrayList<Any?>()
159-
this == Map::class || this == MutableMap::class -> LinkedHashMap<Any?, Any?>()
160-
this == Queue::class || this == Deque::class -> LinkedList<Any?>()
161-
this == SortedSet::class || this == NavigableSet::class -> TreeSet<Any?>()
162-
this == SortedMap::class || this == NavigableMap::class -> TreeMap<Any?, Any?>()
163-
else -> this.createInstance()
164-
}
165-
} catch (e: Throwable) {
166-
throw IllegalArgumentException("Error creating a default value for non-null type: ${this.jvmName}", e)
167-
}
168-
169-
if (this.isInstance(value)) {
170-
return value
171-
} else {
172-
// never happens...
173-
throw AssertionError("$value must be instance of $this")
174-
}
175-
}
176-
177-
private fun Class<*>.createArray(length: Int): Any {
178-
return java.lang.reflect.Array.newInstance(this, length)
179-
}
180-
181116
fun getProperty(name: String): Any? {
182117
return values[name]
183118
}

ktorm-core/src/main/kotlin/me/liuwj/ktorm/schema/TypeReference.kt renamed to ktorm-core/src/main/kotlin/me/liuwj/ktorm/schema/Reflects.kt

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

3+
import me.liuwj.ktorm.entity.Entity
4+
import java.lang.reflect.Method
35
import java.lang.reflect.ParameterizedType
46
import java.lang.reflect.Type
5-
import kotlin.reflect.KClass
6-
import kotlin.reflect.KType
7+
import java.util.*
8+
import kotlin.reflect.*
9+
import kotlin.reflect.full.createInstance
10+
import kotlin.reflect.full.declaredMemberProperties
11+
import kotlin.reflect.full.isSubclassOf
12+
import kotlin.reflect.jvm.javaGetter
13+
import kotlin.reflect.jvm.javaSetter
14+
import kotlin.reflect.jvm.jvmName
715

816
/**
917
* Base class used for obtaining full generic type information by subclassing.
@@ -80,4 +88,56 @@ inline fun <reified T> typeOf(): Type {
8088
@Deprecated("Do not use this function until the bug KT-28616 fixed", ReplaceWith("typeOf<T>()"))
8189
inline fun <reified T> kotlinTypeOf(): KType {
8290
return typeRef<T>().referencedKotlinType
91+
}
92+
93+
internal val Method.kotlinProperty: Pair<KProperty1<*, *>, Boolean>? get() {
94+
for (prop in declaringClass.kotlin.declaredMemberProperties) {
95+
if (prop.javaGetter == this) {
96+
return prop to true
97+
}
98+
if (prop is KMutableProperty<*> && prop.javaSetter == this) {
99+
return prop to false
100+
}
101+
}
102+
return null
103+
}
104+
105+
internal val KClass<*>.defaultValue: Any get() {
106+
val value: Any = try {
107+
when {
108+
this == Boolean::class -> false
109+
this == Char::class -> 0.toChar()
110+
this == Byte::class -> 0.toByte()
111+
this == Short::class -> 0.toShort()
112+
this == Int::class -> 0
113+
this == Long::class -> 0L
114+
this == Float::class -> 0.0F
115+
this == Double::class -> 0.0
116+
this == String::class -> ""
117+
this.isSubclassOf(Entity::class) -> Entity.create(this)
118+
this.java.isEnum -> this.java.enumConstants[0]
119+
this.java.isArray -> this.java.componentType.createArray(0)
120+
this == Set::class || this == MutableSet::class -> LinkedHashSet<Any?>()
121+
this == List::class || this == MutableList::class -> ArrayList<Any?>()
122+
this == Collection::class || this == MutableCollection::class -> ArrayList<Any?>()
123+
this == Map::class || this == MutableMap::class -> LinkedHashMap<Any?, Any?>()
124+
this == Queue::class || this == Deque::class -> LinkedList<Any?>()
125+
this == SortedSet::class || this == NavigableSet::class -> TreeSet<Any?>()
126+
this == SortedMap::class || this == NavigableMap::class -> TreeMap<Any?, Any?>()
127+
else -> this.createInstance()
128+
}
129+
} catch (e: Throwable) {
130+
throw IllegalArgumentException("Error creating a default value for non-null type: ${this.jvmName}", e)
131+
}
132+
133+
if (this.isInstance(value)) {
134+
return value
135+
} else {
136+
// never happens...
137+
throw AssertionError("$value must be instance of $this")
138+
}
139+
}
140+
141+
private fun Class<*>.createArray(length: Int): Any {
142+
return java.lang.reflect.Array.newInstance(this, length)
83143
}

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@ package me.liuwj.ktorm.schema
22

33
import me.liuwj.ktorm.entity.Entity
44
import me.liuwj.ktorm.expression.TableExpression
5+
import java.lang.reflect.InvocationHandler
6+
import java.lang.reflect.Method
7+
import java.lang.reflect.Proxy
58
import java.util.*
69
import java.util.concurrent.atomic.AtomicInteger
710
import kotlin.properties.ReadOnlyProperty
811
import kotlin.reflect.KClass
912
import kotlin.reflect.KProperty
1013
import kotlin.reflect.KProperty1
14+
import kotlin.reflect.full.isSubclassOf
1115

1216
/**
1317
* SQL 数据表
@@ -257,6 +261,44 @@ open class Table<E : Entity<E>>(
257261
}
258262
}
259263

264+
private class ColumnBindingHandler(val properties: MutableList<KProperty1<*, *>>) : InvocationHandler {
265+
266+
override fun invoke(proxy: Any, method: Method, args: Array<out Any>?): Any? {
267+
when (method.declaringClass.kotlin) {
268+
Any::class, Entity::class -> {
269+
throw UnsupportedOperationException("Unsupported method: $method")
270+
}
271+
else -> {
272+
val (prop, isGetter) = method.kotlinProperty ?: throw UnsupportedOperationException("Unsupported method: $method")
273+
if (!prop.isAbstract) {
274+
throw UnsupportedOperationException("Cannot bind a column to a non-abstract property: $prop")
275+
}
276+
if (!isGetter) {
277+
throw UnsupportedOperationException("Cannot modify a property while we are binding a column to it, property: $prop")
278+
}
279+
280+
properties += prop
281+
282+
val returnType = prop.returnType as KClass<*>
283+
if (returnType.isSubclassOf(Entity::class)) {
284+
return createProxy(returnType, properties)
285+
} else {
286+
return null
287+
}
288+
}
289+
}
290+
}
291+
292+
companion object {
293+
294+
fun createProxy(entityClass: KClass<*>, properties: MutableList<KProperty1<*, *>>): Entity<*> {
295+
val classLoader = Thread.currentThread().contextClassLoader
296+
val handler = ColumnBindingHandler(properties)
297+
return Proxy.newProxyInstance(classLoader, arrayOf(entityClass.java), handler) as Entity<*>
298+
}
299+
}
300+
}
301+
260302
/**
261303
* 获取该表对应的 SQL 表达式
262304
*/

ktorm-jackson/src/main/kotlin/me/liuwj/ktorm/jackson/JacksonExtensions.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package me.liuwj.ktorm.jackson
22

33
import com.fasterxml.jackson.core.JsonGenerator
44
import com.fasterxml.jackson.core.ObjectCodec
5-
import com.fasterxml.jackson.core.type.TypeReference
65
import com.fasterxml.jackson.databind.ObjectMapper
76
import com.fasterxml.jackson.databind.SerializationFeature
87
import com.fasterxml.jackson.databind.cfg.MapperConfig

0 commit comments

Comments
 (0)