AuftragsCache.ktPerformance-critical timed cache that preloads and maintains all order (Auftrag) information for the order book. Extends AbstractCache with an 8-hour refresh cycle. Loads all non-deleted orders, their positions, and payment schedules into in-memory maps to avoid repeated database queries. Calculates invoiced sums, fully-invoiced status, "to be invoiced" flags, and tracks counts of orders needing invoicing. Listens for changes via BaseDOModifiedListener hooks on both AuftragDO and RechnungDO to invalidate cached data. Handles a cyclic dependency with AuftragDao via post-construction wiring.
@Service class AuftragsCache : AbstractCache(8 * TICKS_PER_HOUR)
| Dependency | Type | Notes |
|---|---|---|
auftragsCacheService | @Autowired AuftragsCacheService | Provides bulk SELECT queries for orders, positions, and payment schedules |
rechnungDao | @Autowired RechnungDao | Listened for invoice changes that invalidate order data |
auftragDao | @Autowired AuftragDao | Wired back to AuftragDao.auftragsCache via init(); listened for order changes |
| Map | Key | Value | Description |
|---|---|---|---|
orderInfoMap | Long (order ID) | OrderInfo | All cached order info by order ID |
orderPositionMapByPosId | Long (position ID) | OrderPositionInfo | All cached position info by position ID |
toBeInvoicedCounter | — | Int? | Cached count of orders with toBeInvoiced == true (lazily calculated) |
@PostConstruct init())Sets the singleton instance, wires this into auftragDao.auftragsCache, and registers two change listeners:
RechnungDO calls setExpired() to invalidate the entire cache (since invoices affect order position invoiced sums)AuftragDO calls setExpired()| Method | Returns | Description |
|---|---|---|
getOrderInfo(orderId) | OrderInfo? | Get cached info by order ID |
getOrderInfo(order) | OrderInfo | Get cached info for an order object (falls back to empty OrderInfo if not found) |
findOrderInfoByNumber(orderNumber) | OrderInfo? | Lookup by AuftragDO.nummer |
getOrderInfoByPositionId(positionInfoId) | OrderInfo? | Get the parent order info for a given position ID |
getOrderPositionInfo(positionId) | OrderPositionInfo? | Get cached position info by position ID |
getOrderPositionInfosByAuftragId(auftragId) | Collection<OrderPositionInfo>? | Get all position infos for an order |
setOrderInfo(order) | Unit | Fills order.info with cached calculated data |
getFakturiertSum(order) | BigDecimal | Convenience for getOrderInfo(order).invoicedSum |
isVollstaendigFakturiert(order) | Boolean | Convenience for getOrderInfo(order).isVollstaendigFakturiert |
isPositionAbgeschlossenUndNichtVollstaendigFakturiert(order) | Boolean | Convenience lookup |
isPaymentSchedulesReached(order) | Boolean | Convenience lookup |
getToBeInvoicedCounter() | Int | Lazily counts orders with toBeInvoiced == true |
setExpired() — Overrides AbstractCache.setExpired() to also reset toBeInvoicedCounter to null, forcing recalculation on next accesssetExpired(order) — Same as above (formerly updated individual entries; now expires the full cache)auftragListener, rechnungListener) call setExpired() on any changerefresh())Called when the cache TTL expires (every 8 hours) or when manually expired:
LogDuration timerauftragsCacheService.selectNonDeletedAuftragsPositions(), groups by auftrag.idauftragsCacheService.selectAuftragsList()auftragsCacheService.selectNonDeletedPaymentSchedules(), groups by auftrag.idOrderInfo objects for each order (calling updateFields(order) to copy basic data)OrderPositionInfo objects for each position (note: constructor uses AuftragsRechnungCache, which in turn uses AuftragsCache — the cyclic dependency is resolved by running caches twice on initialization)order.info.calculateAll(order, positions, paymentSchedules)toBeInvoicedCounter to nullMaps (orderInfoMap, orderPositionMapByPosId) are replaced atomically with new immutable maps during refresh. The comment "No sync, immutable map" on all read accesses indicates a read-only snapshot pattern: on each checkRefresh() call, the cache may rebuild a fresh map and swap the reference.
checkRefresh parameter on getOrderInfo(order, checkRefresh) exists to avoid deadlocks during cache initialization — when the cache is already being refreshed, recursive calls skip refreshAuftragsCache, AuftragsRechnungCache, and OrderPositionInfo is documented in commit 51d352133 — resolved by running caches twice on initializationtoBeInvoicedCounter uses a lazy calculation pattern with debug logging of which order numbers are countedcheckRefresh() before access to ensure fresh data868d6abb7 2025 -> 2026 51d352133 AuftragsCache / AuftragsRechnungsCache handling refactored (cyclic usage resolved by running caches twice on initialization) 0043cbdb5 WIP Forecast 63081666f Source file headers: 2024-> 2025. ae2c04ee0 Migration stuff in progress... (all tests of all packages: OK). 9aff90908 Migration stuff in progress... (all tests of all packages: OK). 89ea9a532 Migration stuff in progress... (all tests of all packages: OK). d02c8a770 Migration stuff in progress... 3159cfa6c Migration stuff in progress... (all tests of all packages: OK). ff2cc4cfa Migration stuff in progress... (all tests of all packages: OK). 714452019 Migration stuff in progress... (all tests of all packages: OK). 567ca70cd Migration in progress... spring.datasource.hikari.auto-commit=false (40+ commits spanning migration, refactoring, java.time conversion, Jakarta migration, etc.)