В моем ktor
веб-приложении я использую exposed sql
для хранения объектов в базе данных.
Я также использую ehcache
для выгрузки части работы из базы данных вкеш.
Я хочу иметь возможность обновлять объект в кеше, не обновляя его и в базе данных (потому что это будет происходить довольно часто).
Меня беспокоит то, что при удалении объекта из кэша изменения не будут сохраняться в базе данных
Как сохранить изменения, внесенные в объект, без необходимости каждый раз обращаться к базе данныхвремя?
MyObject.kt
import kotlinx.css.Color
import java.io.Serializable
data class MyObject(val ID: Int, val userID: String, var description: String, var color: Color, var count: Long = 0) : Serializable
MyObjects.kt
import org.jetbrains.exposed.sql.Table
object MyObjects : Table() {
val ID = integer("ID").primaryKey().autoIncrement()
val count = long("Count")
val userID = varchar("UserID", 64).index()
val color = varchar("Color", 16)
val description = varchar("Description", 128)
}
DAOFacade.kt
import kotlinx.css.Color
import kotlinx.io.core.Closeable
interface DAOFacade : Closeable {
fun getMyObject(ID: Int): MyObject?
fun updateMyObject(ID: Int, description: String? = null, color: Color? = null, count: Long? = null)
}
DAOFacadeDatabase.kt
import kotlinx.css.Color
import org.jetbrains.exposed.sql.*
class DAOFacadeDatabase(private val DB: Database = Database.Companion.connect("jdbc:h2:mem:test", "org.h2.Driver")) : DAOFacade {
init {
DB.transaction { create(MyObjects) }
}
private fun ResultRow.toMyObject() = MyObject(this[MyObjects.ID], this[MyObjects.userID], this[MyObjects.description], Color(this[MyObjects.color]), this[MyObjects.count])
override fun getMyObject(ID: Int): MyObject? = DB.transaction {
MyObjects.select { MyObjects.ID eq ID }.singleOrNull()?.toMyObject()
}
override fun updateMyObject(ID: Int, description: String?, color: Color?, count: Long?): Unit = DB.transaction {
MyObjects.update({ MyObjects.ID eq ID }) {
if (description != null) it[MyObjects.description] = description
if (color != null) it[MyObjects.color] = color.value
if (count != null) it[MyObjects.count] = count
}
}
override fun close() = Unit
DAOFacadeCache.kt
import kotlinx.css.Color
import org.ehcache.CacheManagerBuilder
import org.ehcache.config.CacheConfigurationBuilder
import org.ehcache.config.ResourcePoolsBuilder
import org.ehcache.config.persistence.CacheManagerPersistenceConfiguration
import org.ehcache.config.units.EntryUnit
import org.ehcache.config.units.MemoryUnit
import java.io.File
class DAOFacadeCache(private val delegate: DAOFacade, storage: File) : DAOFacade {
private val myObjectCacheAlias = "MyObject Cache"
private val cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.with(CacheManagerPersistenceConfiguration(storage))
.withCache(myObjectCacheAlias, CacheConfigurationBuilder.newCacheConfigurationBuilder<Int, MyObject>()
.withResourcePools(
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(1024, EntryUnit.ENTRIES)
.offheap(16, MemoryUnit.MB)
.disk(128, MemoryUnit.MB, true)
)
.buildConfig(Int::class.javaObjectType, MyObject::class.java)
).build(true)
private val myObjectCache = cacheManager.getCache(myObjectCacheAlias, Int::class.javaObjectType, MyObject::class.java)
override fun getMyObject(ID: Int): MyObject? = myObjectCache[ID].takeUnless { it == null } ?: delegate.getMyObject(ID)?.also {
myObjectCache.put(ID, it)
}
override fun updateMyObject(ID: Int, description: String?, color: Color?, count: Long?) {
getMyObject(ID)?.let {
if (description != null) it.description = description
if (color != null) it.color = color
if (count != null) it.count = count
}
}
override fun close() = try {
delegate.close()
} finally {
cacheManager.close()
}
В моем приложении я создаю DAOFacade
, например:
import com.mchange.v2.c3p0.ComboPooledDataSource
import org.h2.Driver
import org.jetbrains.exposed.sql.Database
import java.io.File
val dir = File("/build/db")
val pool = ComboPooledDataSource().apply {
driverClass = Driver::class.java.name
jdbcUrl = "jdbc:h2:file:${dir.canonicalFile.absolutePath}"
user = ""
password = ""
}
val DAO: DAOFacade = DAOFacadeCache(DAOFacadeDatabase(Database.connect(pool)), File(dir.parentFile, "ehcache"))
Моя идея состояла в том, чтобы отследить ID
s MyObject
s, которые были изменены в поле Set<Int>
в DAOFacadeCache
, и изменить его функцию close
, чтобы сохранить изменения в delegate
база данных.
Однако проблема с этим решением заключается в том, что MyObject
, чей ID
находится в Set
, может быть удален из кэша до его закрытия (возможные изменения будут потеряны)
Можно ли указать кешу что-то сделать с объектом, прежде чем он вытеснит его из памяти?