Skip to content

Commit 406e869

Browse files
refactor entity implemtations
1 parent 14db07d commit 406e869

File tree

7 files changed

+186
-177
lines changed

7 files changed

+186
-177
lines changed

Diff for: ktorm-core/src/main/kotlin/me/liuwj/ktorm/entity/Entity.kt

+20-11
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import kotlin.reflect.full.isSubclassOf
1313
*
1414
* 在框架中,实体类必须声明为 interface(not data class) 并且继承 [Entity]。
1515
* 在从查询结果中创建实体对象时,框架会使用 JDK 动态代理生成实体类接口的实现,并创建一个对象,
16-
* 因此在实体里上进行的任何操作都会代理到框架的 [EntityImpl] 类中,从而使得框架能够检测到实体对象中的任何变化
16+
* 因此在实体里上进行的任何操作都会代理到框架的 [EntityImplementation] 类中,从而使得框架能够检测到实体对象中的任何变化
1717
*
1818
* Created by vince on Jun 18, 2018.
1919
*/
@@ -22,12 +22,12 @@ interface Entity<E : Entity<E>> : Serializable {
2222
/**
2323
* Return this entity's [KClass] instance, which must be an interface.
2424
*/
25-
//val entityClass: KClass<E>
25+
val entityClass: KClass<E>
2626

2727
/**
2828
* Return the immutable view of this entity's all properties.
2929
*/
30-
//val properties: Map<String, Any?>
30+
val properties: Map<String, Any?>
3131

3232
/**
3333
* 将实体对象中变化的字段保存到数据库,返回受影响的记录数
@@ -61,7 +61,7 @@ interface Entity<E : Entity<E>> : Serializable {
6161
/**
6262
* 创建实体类对象,此方法仅限框架内部使用
6363
*/
64-
fun create(entityClass: KClass<*>, parent: Entity<*>? = null, fromTable: Table<*>? = parent?.impl?.fromTable): Entity<*> {
64+
internal fun create(entityClass: KClass<*>, parent: EntityImplementation? = null, fromTable: Table<*>? = parent?.fromTable): Entity<*> {
6565
if (!entityClass.isSubclassOf(Entity::class)) {
6666
throw IllegalArgumentException("An entity class must be subclass of Entity.")
6767
}
@@ -70,22 +70,31 @@ interface Entity<E : Entity<E>> : Serializable {
7070
}
7171

7272
val classLoader = Thread.currentThread().contextClassLoader
73-
val impl = EntityImpl(entityClass, fromTable, parent)
74-
return Proxy.newProxyInstance(classLoader, arrayOf(entityClass.java), impl) as Entity<*>
73+
val handler = EntityImplementation(entityClass, fromTable, parent)
74+
return Proxy.newProxyInstance(classLoader, arrayOf(entityClass.java), handler) as Entity<*>
7575
}
7676

7777
/**
7878
* 创建实体类对象
7979
*/
80-
inline fun <reified E : Entity<E>> create(): E {
81-
return create(E::class, null, null) as E
80+
fun create(entityClass: KClass<*>): Entity<*> {
81+
if (!entityClass.isSubclassOf(Entity::class)) {
82+
throw IllegalArgumentException("An entity class must be subclass of Entity.")
83+
}
84+
if (!entityClass.java.isInterface) {
85+
throw IllegalArgumentException("An entity class must be defined as an interface.")
86+
}
87+
88+
val classLoader = Thread.currentThread().contextClassLoader
89+
val handler = EntityImplementation(entityClass, null, null)
90+
return Proxy.newProxyInstance(classLoader, arrayOf(entityClass.java), handler) as Entity<*>
8291
}
8392

8493
/**
85-
* 创建实体类对象,并执行回调函数进行初始化操作
94+
* 创建实体类对象
8695
*/
87-
inline fun <reified E : Entity<E>> create(init: E.() -> Unit): E {
88-
return create<E>().apply(init)
96+
inline fun <reified E : Entity<E>> create(): E {
97+
return create(E::class) as E
8998
}
9099
}
91100

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

+34-26
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ fun <E : Entity<E>> Table<E>.add(entity: E): Int {
2424
)
2525
)
2626

27-
val useGeneratedKey = primaryKey?.binding != null && entity.getPrimaryKeyValue(this) == null
27+
val useGeneratedKey = primaryKey?.binding != null && entity.implementation.getPrimaryKeyValue(this) == null
2828

2929
expression.prepareStatement(autoGeneratedKeys = useGeneratedKey) { statement, logger ->
3030
val effects = statement.executeUpdate().also { logger.debug("Effects: {}", it) }
@@ -35,24 +35,25 @@ fun <E : Entity<E>> Table<E>.add(entity: E): Int {
3535
val generatedKey = primaryKey?.sqlType?.getResult(rs, 1)
3636
if (generatedKey != null) {
3737
logger.debug("Generated Key: {}", generatedKey)
38-
entity.setPrimaryKeyValue(this, generatedKey)
38+
entity.implementation.setPrimaryKeyValue(this, generatedKey)
3939
}
4040
}
4141
}
4242
}
4343

44-
entity.impl.fromTable = this
45-
entity.discardChanges()
44+
entity.implementation.fromTable = this
45+
entity.implementation.doDiscardChanges()
4646
return effects
4747
}
4848
}
4949

5050
private fun Table<*>.findInsertColumns(entity: Entity<*>): Map<Column<*>, Any?> {
51+
val implementation = entity.implementation
5152
val assignments = LinkedHashMap<Column<*>, Any?>()
5253

5354
for (column in columns) {
5455
if (column is SimpleColumn && column.binding != null) {
55-
val value = entity.getColumnValue(column)
56+
val value = implementation.getColumnValue(column)
5657
if (value != null) {
5758
assignments[column] = value
5859
}
@@ -63,9 +64,9 @@ private fun Table<*>.findInsertColumns(entity: Entity<*>): Map<Column<*>, Any?>
6364
}
6465

6566
@Suppress("UNCHECKED_CAST")
66-
internal fun EntityImpl.doFlushChanges(): Int {
67-
val fromTable = this.fromTable ?: kotlin.error("The entity is not associated with any table yet.")
68-
val primaryKey = fromTable.primaryKey ?: kotlin.error("Table ${fromTable.tableName} doesn't have a primary key.")
67+
internal fun EntityImplementation.doFlushChanges(): Int {
68+
val fromTable = this.fromTable?.takeIf { this.parent == null } ?: error("The entity is not associated with any table yet.")
69+
val primaryKey = fromTable.primaryKey ?: error("Table ${fromTable.tableName} doesn't have a primary key.")
6970
val assignments = findChangedColumns(fromTable).takeIf { it.isNotEmpty() } ?: return 0
7071

7172
val expression = AliasRemover.visit(
@@ -88,12 +89,12 @@ internal fun EntityImpl.doFlushChanges(): Int {
8889

8990
expression.prepareStatement { statement, logger ->
9091
val effects = statement.executeUpdate().also { logger.debug("Effects: {}", it) }
91-
discardChanges()
92+
doDiscardChanges()
9293
return effects
9394
}
9495
}
9596

96-
private fun EntityImpl.findChangedColumns(fromTable: Table<*>): Map<Column<*>, Any?> {
97+
private fun EntityImplementation.findChangedColumns(fromTable: Table<*>): Map<Column<*>, Any?> {
9798
val assignments = LinkedHashMap<Column<*>, Any?>()
9899

99100
for (column in fromTable.columns) {
@@ -102,30 +103,34 @@ private fun EntityImpl.findChangedColumns(fromTable: Table<*>): Map<Column<*>, A
102103
when (binding) {
103104
is ReferenceBinding -> {
104105
if (binding.onProperty.name in changedProperties) {
105-
val child = this[binding.onProperty.name] as Entity<*>?
106-
assignments[column] = child?.getPrimaryKeyValue(binding.referenceTable)
106+
val child = this.getProperty(binding.onProperty.name) as Entity<*>?
107+
assignments[column] = child?.implementation?.getPrimaryKeyValue(binding.referenceTable)
107108
}
108109
}
109110
is NestedBinding -> {
110111
var anyChanged = false
111112
var curr: Any? = this
112113

113114
for ((i, prop) in binding.withIndex()) {
114-
check(curr is Entity<*>?)
115+
check(curr is EntityImplementation?)
115116

116-
val changed = if (curr == null) false else prop.name in curr.impl.changedProperties
117+
val changed = if (curr == null) false else prop.name in curr.changedProperties
117118

118119
if (changed && i > 0) {
119120
check(curr != null)
120121

121-
if (curr.impl.fromTable != null && curr.getRoot() != this) {
122+
if (curr.fromTable != null && curr.getRoot() != this) {
122123
val propPath = binding.subList(0, i + 1).joinToString(separator = ".", prefix = "this.") { it.name }
123124
throw IllegalStateException("$propPath may be unexpectedly discarded after flushChanges, please save it to database first.")
124125
}
125126
}
126127

127128
anyChanged = anyChanged || changed
128-
curr = curr?.get(prop.name)
129+
130+
curr = curr?.getProperty(prop.name)
131+
if (curr is Entity<*>) {
132+
curr = curr.implementation
133+
}
129134
}
130135

131136
if (anyChanged) {
@@ -138,17 +143,17 @@ private fun EntityImpl.findChangedColumns(fromTable: Table<*>): Map<Column<*>, A
138143
return assignments
139144
}
140145

141-
private tailrec fun Entity<*>.getRoot(): Entity<*> {
142-
val parent = this.impl.parent
146+
private tailrec fun EntityImplementation.getRoot(): EntityImplementation {
147+
val parent = this.parent
143148
if (parent == null) {
144149
return this
145150
} else {
146151
return parent.getRoot()
147152
}
148153
}
149154

150-
internal fun EntityImpl.doDiscardChanges() {
151-
val fromTable = this.fromTable ?: kotlin.error("The entity is not associated with any table yet.")
155+
internal fun EntityImplementation.doDiscardChanges() {
156+
val fromTable = this.fromTable?.takeIf { this.parent == null } ?: error("The entity is not associated with any table yet.")
152157

153158
for (column in fromTable.columns) {
154159
val binding = column.binding?.takeIf { column is SimpleColumn } ?: continue
@@ -165,20 +170,23 @@ internal fun EntityImpl.doDiscardChanges() {
165170
break
166171
}
167172

168-
check(curr is Entity<*>)
169-
curr.impl.changedProperties.remove(prop.name)
173+
check(curr is EntityImplementation)
174+
curr.changedProperties.remove(prop.name)
170175

171-
curr = curr[prop.name]
176+
curr = curr.getProperty(prop.name)
177+
if (curr is Entity<*>) {
178+
curr = curr.implementation
179+
}
172180
}
173181
}
174182
}
175183
}
176184
}
177185

178186
@Suppress("UNCHECKED_CAST")
179-
internal fun EntityImpl.doDelete(): Int {
180-
val fromTable = this.fromTable ?: kotlin.error("The entity is not associated with any table yet.")
181-
val primaryKey = fromTable.primaryKey ?: kotlin.error("Table ${fromTable.tableName} doesn't have a primary key.")
187+
internal fun EntityImplementation.doDelete(): Int {
188+
val fromTable = this.fromTable?.takeIf { this.parent == null } ?: error("The entity is not associated with any table yet.")
189+
val primaryKey = fromTable.primaryKey ?: error("Table ${fromTable.tableName} doesn't have a primary key.")
182190

183191
val expression = AliasRemover.visit(
184192
expr = DeleteExpression(

0 commit comments

Comments
 (0)