All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.exposed.dao.InnerTableLink.kt Maven / Gradle / Ivy

There is a newer version: 0.57.0
Show newest version
package org.jetbrains.exposed.dao

import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.TransactionManager
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

@Suppress("UNCHECKED_CAST")
class InnerTableLink, Source : Entity, ID : Comparable, Target : Entity>(
    val table: Table,
    val target: EntityClass,
    val sourceColumn: Column>? = null,
    targetColumn: Column>? = null
) : ReadWriteProperty> {
    init {
        targetColumn?.let {
            requireNotNull(sourceColumn) { "Both source and target columns should be specified" }
            require(targetColumn.referee?.table == target.table) {
                "Column $targetColumn point to wrong table, expected ${target.table.tableName}"
            }
            require(targetColumn.table == sourceColumn.table) {
                "Both source and target columns should be from the same table"
            }
        }
        sourceColumn?.let {
            requireNotNull(targetColumn) { "Both source and target columns should be specified" }
        }
    }

    private val targetColumn = targetColumn
        ?: table.columns.singleOrNull { it.referee == target.table.id } as? Column>
        ?: error("Table does not reference target")

    private fun getSourceRefColumn(o: Source): Column> {
        return sourceColumn
            ?: table.columns.singleOrNull { it.referee == o.klass.table.id } as? Column>
            ?: error("Table does not reference source")
    }

    override operator fun getValue(o: Source, unused: KProperty<*>): SizedIterable {
        if (o.id._value == null) return emptySized()
        val sourceRefColumn = getSourceRefColumn(o)
        val transaction = TransactionManager.currentOrNull()
            ?: return o.getReferenceFromCache(sourceRefColumn)
        val alreadyInJoin = (target.dependsOnTables as? Join)?.alreadyInJoin(table) ?: false
        val entityTables =
            if (alreadyInJoin) target.dependsOnTables else target.dependsOnTables.join(table, JoinType.INNER, target.table.id, targetColumn)

        val columns = (
            target.dependsOnColumns + (if (!alreadyInJoin) table.columns else emptyList()) -
                sourceRefColumn
            ).distinct() + sourceRefColumn

        val query = { target.wrapRows(entityTables.slice(columns).select { sourceRefColumn eq o.id }) }
        return transaction.entityCache.getOrPutReferrers(o.id, sourceRefColumn, query).also {
            o.storeReferenceInCache(sourceRefColumn, it)
        }
    }

    override fun setValue(o: Source, unused: KProperty<*>, value: SizedIterable) {
        val sourceRefColumn = getSourceRefColumn(o)

        val tx = TransactionManager.current()
        val entityCache = tx.entityCache
        entityCache.flush()
        val oldValue = getValue(o, unused)
        val existingIds = oldValue.map { it.id }.toSet()
        entityCache.referrers[sourceRefColumn]?.remove(o.id)

        val targetIds = value.map { it.id }
        executeAsPartOfEntityLifecycle {
            table.deleteWhere { (sourceRefColumn eq o.id) and (targetColumn notInList targetIds) }
            table.batchInsert(targetIds.filter { !existingIds.contains(it) }, shouldReturnGeneratedValues = false) { targetId ->
                this[sourceRefColumn] = o.id
                this[targetColumn] = targetId
            }
        }

        // current entity updated
        tx.registerChange(o.klass, o.id, EntityChangeType.Updated)

        // linked entities updated
        val targetClass = (value.firstOrNull() ?: oldValue.firstOrNull())?.klass
        if (targetClass != null) {
            existingIds.plus(targetIds).forEach {
                tx.registerChange(targetClass, it, EntityChangeType.Updated)
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy