Как обновить объект в базе данных до его удаления из кэша? - PullRequest
0 голосов
/ 06 октября 2019

В моем 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, может быть удален из кэша до его закрытия (возможные изменения будут потеряны)

Можно ли указать кешу что-то сделать с объектом, прежде чем он вытеснит его из памяти?

...