#998: KostCache.kt

projectforge-business/src/main/kotlin/org/projectforge/business/fibu/kost/KostCache.kt Type: Cache Component · Purpose: In-memory cache for Kost1, Kost2, and Kost2Art entities · Source: projectforge-business/src/main/kotlin/org/projectforge/business/fibu/kost/KostCache.kt 361 lines · 249 code · 77 comments · 35 blank
Central caching component for all cost center data (Kost1DO, Kost2DO, Kost2ArtDO). Extends AbstractCache to leverage the ProjectForge caching framework. Maintains synchronized hash maps for fast lookup by ID and by numeric components, avoiding repeated JPA queries for frequently accessed cost center data.

Cache Structure

MapKeyValueDescription
kost1MapLong (Kost1DO.id)Kost1DOSynchronized mutable map; updated on insert/modify
kost2MapLong (Kost2DO.id)Kost2DOSynchronized mutable map; updated on insert/modify
kost2ArtMapLong (Kost2ArtDO.id)Kost2ArtDORead-only after refresh, replaced on full refresh
allKost2ArtsList<Kost2Art>Reporting wrapper list (Kost2ArtImpl)

Key Methods

MethodDescription
getKost2(id)Get Kost2 by ID from cache (primary lookup)
getKost2IfNotInitialized(kost2)If lazy proxy, reload from cache to prevent lazy-loading N+1
getKost2(str)Get Kost2 by formatted string (parses "5.123.45.67")
getKost2(nk, b, tb, art)Get Kost2 by individual numeric components
getActiveKost2(nk, b, tb)Filter cached Kost2 by prefix and effective status
getKost1(id) / getKost1(str)Analogous Kost1 lookups
findKost1(obj) / findKost2(obj)Polymorphic find by Kost1DO, String, or Number
getKost2Art(id)Get cost type by 2-digit ID
getKost2ArtsForProjekt(projektId)Cost types used in a project's Kost2 entries
getAllKost2ArtsForProjekt(projektId)All cost types, marked if project already uses them
getKost2ForProjekt(projektId)All Kost2 entries for a given project

Refresh Mechanism

The refresh() method is called by AbstractCache's infrastructure (via checkRefresh() which is invoked at the start of every public method). Refresh loads all Kost1, Kost2, and Kost2Art records in a single read-only isolated transaction:

  1. Loads all Kost1DO and Kost2DO entities into mutable maps
  2. Checks if any non-deleted Kost2 entries exist (kost2EntriesExists flag)
  3. Rebuilds allKost2Arts list wrapped as Kost2ArtImpl for reporting

Cache Invalidation

Hibernate Initialization Guard

The get*IfNotInitialized() methods prevent a critical performance pitfall: when a Hibernate lazy proxy is accessed outside a transaction, it triggers N+1 queries. These methods use HibernateUtils.isFullyInitialized() to detect proxies and reload from the cache instead. This guard is applied to Kost1DO, Kost2DO, and Kost2ArtDO.

Thread Safety

Source Code (abridged)

@Component
class KostCache : AbstractCache() {
    @Autowired private lateinit var persistenceService: PfPersistenceService
    @Autowired private lateinit var kundeDao: KundeDao

    private lateinit var kost1Map: MutableMap<Long, Kost1DO>
    private lateinit var kost2Map: MutableMap<Long, Kost2DO>
    private lateinit var kost2ArtMap: Map<Long, Kost2ArtDO>

    fun getKost2(id: Long?): Kost2DO? {
        id ?: return null
        checkRefresh()
        synchronized(kost2Map) { return kost2Map[id] }
    }

    override fun refresh() {
        persistenceService.runIsolatedReadOnly { context ->
            this.kost1Map = persistenceService
                .executeQuery("from Kost1DO t", Kost1DO::class.java)
                .filter { it.id != null }
                .associateBy { it.id!! }.toMutableMap()
            this.kost2Map = persistenceService
                .executeQuery("from Kost2DO t", Kost2DO::class.java)
                .filter { it.id != null }
                .associateBy { it.id!! }.toMutableMap()
            // ... updateKost2Arts()
        }
    }
    companion object { lateinit var instance: KostCache }
}

Git History

868d6abb7 2025 -> 2026
8239a53ee Kost2: Virtuelle Status-Vererbung von ProjektDO
581988c45 WIP: Parsing of creditor invoices.
6a32032d6 Hibernate.isInitialized -> HibernateUtils.isFullyInitialized
63081666f Source file headers: 2024-> 2025.
db2599ab8 History in orderbook, invoices and projects.
50c3b7b46 Migration (all tests OK).
b3782c8a8 Migration stuff...
4942c854d Migration stuff...
d394f410f Migration (all tests OK).
f928d12b1 Migration (all tests OK).
2e9839891 Migration stuff...
ba2479571 Migration stuff...
b3293f0cc PersistenceService: stats handling improved.
85b4e1175 PfPersistenceService: query -> executeQuery.
b095e6f7d Big change of Transaction handling.
4c04cfd65 MAJOR-CHANGE! Integer id's to Long id's. (file created)