Skip to content

Commit 55cb324

Browse files
Merge pull request #525 from HC-224/changed-properties
Track and Provide Access to Changed Properties
2 parents 0a03a29 + a5b1be8 commit 55cb324

File tree

4 files changed

+65
-5
lines changed

4 files changed

+65
-5
lines changed

buildSrc/src/main/kotlin/ktorm.publish.gradle.kts

+5
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,11 @@ publishing {
155155
id.set("brohacz")
156156
name.set("Michal Brosig")
157157
}
158+
developer {
159+
id.set("hc224")
160+
name.set("hc224")
161+
email.set("[email protected]")
162+
}
158163
}
159164
}
160165
}

ktorm-core/src/main/kotlin/org/ktorm/entity/Entity.kt

+8
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,14 @@ public interface Entity<E : Entity<E>> : Serializable {
143143
*/
144144
public val properties: Map<String, Any?>
145145

146+
/**
147+
* Return the immutable view of the original values for this entity's changed properties.
148+
*
149+
* Properties are set as changed when constructing an entity with non-default values or when setting a property.
150+
* If the property is set to the same value it is still marked as changed.
151+
*/
152+
public val changedProperties: Map<String, Any?>
153+
146154
/**
147155
* Update the property changes of this entity into the database and return the affected record number.
148156
*

ktorm-core/src/main/kotlin/org/ktorm/entity/EntityImplementation.kt

+7-4
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ internal class EntityImplementation(
3838
@Transient var fromDatabase: Database? = fromDatabase
3939
@Transient var fromTable: Table<*>? = fromTable
4040
@Transient var parent: EntityImplementation? = parent
41-
@Transient var changedProperties = LinkedHashSet<String>()
41+
@Transient var changedProperties = LinkedHashMap<String, Any?>()
4242

4343
override fun invoke(proxy: Any, method: Method, args: Array<out Any>?): Any? {
4444
return when (method.declaringClass.kotlin) {
@@ -54,6 +54,7 @@ internal class EntityImplementation(
5454
when (method.name) {
5555
"getEntityClass" -> this.entityClass
5656
"getProperties" -> Collections.unmodifiableMap(this.values)
57+
"getChangedProperties" -> Collections.unmodifiableMap(this.changedProperties)
5758
"flushChanges" -> this.doFlushChanges()
5859
"discardChanges" -> this.doDiscardChanges()
5960
"delete" -> this.doDelete()
@@ -150,13 +151,15 @@ internal class EntityImplementation(
150151
throw UnsupportedOperationException(msg)
151152
}
152153

154+
// Map#putIfAbsent treats null as absent, but null is a valid entity value
155+
if (!changedProperties.containsKey(name))
156+
changedProperties[name] = this.values.getOrDefault(name, null)
153157
values[name] = value
154-
changedProperties.add(name)
155158
}
156159

157160
private fun copy(): Entity<*> {
158161
val entity = Entity.create(entityClass, parent, fromDatabase, fromTable)
159-
entity.implementation.changedProperties.addAll(changedProperties)
162+
entity.implementation.changedProperties.putAll(changedProperties)
160163

161164
for ((name, value) in values) {
162165
if (value is Entity<*>) {
@@ -204,7 +207,7 @@ internal class EntityImplementation(
204207
val javaClass = Class.forName(input.readUTF(), true, Thread.currentThread().contextClassLoader)
205208
entityClass = javaClass.kotlin
206209
values = input.readObject() as LinkedHashMap<String, Any?>
207-
changedProperties = LinkedHashSet()
210+
changedProperties = LinkedHashMap()
208211
}
209212

210213
override fun equals(other: Any?): Boolean {

ktorm-core/src/test/kotlin/org/ktorm/entity/EntityTest.kt

+45-1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,50 @@ class EntityTest : BaseTest() {
4747
assert(employee.job == "")
4848
}
4949

50+
@Test
51+
fun testEntityChangedPropertiesInitializer() {
52+
val employee = Employee { name = "walter" }
53+
println(employee)
54+
55+
assert(employee.changedProperties.size == 1)
56+
assert(employee.changedProperties.contains("name"))
57+
assert(employee.changedProperties["name"] == null)
58+
}
59+
60+
@Test
61+
fun testEntityChangedPropertiesSetter() {
62+
val employee = Employee { }
63+
println(employee)
64+
65+
val oldChangedPropertiesIsEmpty = employee.changedProperties.isEmpty()
66+
67+
employee.name = "walter"
68+
println(employee)
69+
70+
assert(oldChangedPropertiesIsEmpty)
71+
assert(employee.changedProperties.size == 1)
72+
assert(employee.changedProperties.contains("name"))
73+
assert(employee.changedProperties["name"] == null)
74+
}
75+
76+
@Test
77+
fun testEntityChangedPropertiesMultiple() {
78+
val employee = Employee { }.copy()
79+
println(employee)
80+
81+
val oldChangedPropertiesIsEmpty = employee.changedProperties.isEmpty()
82+
83+
employee.name = "walter"
84+
println(employee)
85+
employee.name = "vince"
86+
println(employee)
87+
88+
assert(oldChangedPropertiesIsEmpty)
89+
assert(employee.changedProperties.size == 1)
90+
assert(employee.changedProperties.containsKey("name"))
91+
assert(employee.changedProperties["name"] == null)
92+
}
93+
5094
@Test
5195
fun testDefaultMethod() {
5296
for (method in Employee::class.java.methods) {
@@ -783,4 +827,4 @@ class EntityTest : BaseTest() {
783827
assert(departmentTransient !== departmentAttached)
784828
assert(departmentTransient.hashCode() == departmentAttached.hashCode())
785829
}
786-
}
830+
}

0 commit comments

Comments
 (0)