Mercurial Hosting > nabble
diff src/fschmidt/db/cache/DbTableImpl.java @ 68:00520880ad02
add fschmidt source
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Sun, 05 Oct 2025 17:24:15 -0600 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/fschmidt/db/cache/DbTableImpl.java Sun Oct 05 17:24:15 2025 -0600 @@ -0,0 +1,261 @@ +/* +Copyright (c) 2008 Franklin Schmidt <fschmidt@gmail.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +package fschmidt.db.cache; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Map; +import java.util.HashMap; +import java.util.Set; +import java.util.HashSet; +import java.util.Collections; +import java.util.Collection; +import java.util.NoSuchElementException; +import fschmidt.db.util.Unique; +import fschmidt.db.util.WeakCacheMap; +import fschmidt.db.DbObject; +import fschmidt.db.DbKey; +import fschmidt.db.DbRecord; +import fschmidt.db.extend.DbRecordExt; +import fschmidt.db.extend.DbTableExt; +import fschmidt.db.extend.DbDatabaseExt; +import fschmidt.db.extend.DbTransaction; +import fschmidt.db.extend.FilterTable; + + +final class DbTableImpl<K extends DbKey,V extends DbObject<K,V>> extends FilterTable<K,V> { + private static final DbObject NULL0 = new DbObject(){ + public DbRecord getDbRecord() { throw new RuntimeException(); } + }; + + @SuppressWarnings("unchecked") + private final V NULL = (V)NULL0; // hack + + private static class CacheKey { + private final DbTableImpl table; + final Object key; + + CacheKey(DbTableImpl table,Object key) { + this.table = table; + this.key = key; + } + + @Override public boolean equals(Object obj) { + if( obj == this ) + return true; + if( !(obj instanceof DbTableImpl.CacheKey) ) + return false; + CacheKey ck = (CacheKey)obj; + return ck.table == table && ck.key.equals(key); + } + + @Override public int hashCode() { + return table.hashCode() + key.hashCode(); + } + + } + + private static final Map<CacheKey,DbObject> cache0 = Collections.synchronizedMap(new WeakCacheMap<CacheKey,DbObject>()); + private final Unique<K> unique = new Unique<K>(); + private final DbDatabaseImpl database; + + DbTableImpl(DbTableExt<K,V> table,DbDatabaseImpl database) { + super(database,table); + this.database = database; + } + + private Map<CacheKey,DbObject> transCache() { + Map<String,Object> transMap = database.transactionMap(); + @SuppressWarnings("unchecked") + Map<CacheKey,DbObject> transCache = (Map<CacheKey,DbObject>)transMap.get("transCache"); + if( transCache == null ) { + transCache = new WeakCacheMap<CacheKey,DbObject>(); + transMap.put( "transCache", transCache ); + } + return transCache; + } + + private final boolean isStale(DbObject obj) { + if( obj==null ) + return true; + if( obj==NULL ) + return false; +/* + if( database.isInTransaction() ) { + return transCache().containsKey(obj.getDbRecord().getPrimaryKey()); + } else { + return obj.getDbRecord().isStale(); + } +*/ + return obj.getDbRecord().isStale(); + } + + public final V findByPrimaryKey(K key) { + CacheKey ck = new CacheKey(this,key); + if( database.isInTransaction() ) { + Map<CacheKey,DbObject> cache = transCache(); + @SuppressWarnings("unchecked") + V obj = (V)cache.get(ck); + if( obj==null ) { + obj = table.findByPrimaryKey(key); + if( obj==null ) + obj = NULL; + cache.put(ck,obj); + } + return obj==NULL ? null : obj; + } + Map<CacheKey,DbObject> cache = cache0; + key = unique.get(key); + try { + synchronized(key) { + @SuppressWarnings("unchecked") + V obj = (V)cache.get(ck); + if( !isStale(obj) ) + return obj==NULL ? null : obj; + obj = table.findByPrimaryKey(key); + if( !isStale( cache.put( ck, obj==null ? NULL : obj ) ) ) + throw new RuntimeException(); + return obj; + } + } finally { + unique.free(key); + } + } + + public final Map<K,V> findByPrimaryKey(Collection<K> keys) { + Map<K,V> map = new HashMap<K,V>(); + Set<K> set = new HashSet<K>(); + Map<CacheKey,DbObject> cache = database.isInTransaction() ? transCache() : cache0; + boolean inTrans = database.isInTransaction(); + for( K key : keys ) { + CacheKey ck = new CacheKey(this,key); + @SuppressWarnings("unchecked") + V obj = (V)cache.get(ck); + if( obj==null || !inTrans && isStale(obj) ) { + set.add(key); + } else { + map.put( key, obj==NULL ? null : obj ); + } + } + if( !set.isEmpty() ) { + Map<K,V> objs = table.findByPrimaryKey(set); + for( K key : set ) { + CacheKey ck = new CacheKey(this,key); + if( inTrans ) { + if( cache.get(ck) != null ) + throw new RuntimeException(); + V obj = table.findByPrimaryKey(key); + if( obj==null ) + obj = NULL; + cache.put(ck,obj); + map.put( key, obj==NULL ? null : obj ); + } else { + key = unique.get(key); + try { + synchronized(key) { + @SuppressWarnings("unchecked") + V obj = (V)cache.get(ck); + if( isStale(obj) ) { + obj = objs.get(key); + if( obj == null ) + obj = NULL; + if( !isStale( cache.put(ck,obj) ) ) + throw new RuntimeException(); + } + map.put( key, obj==NULL ? null : obj ); + } + } finally { + unique.free(key); + } + } + } + } + return map; + } + + public V getDbObject(K key,ResultSet rs,String tableName) + throws SQLException + { + CacheKey ck = new CacheKey(this,key); + if( database.isInTransaction() ) { + Map<CacheKey,DbObject> cache = transCache(); + @SuppressWarnings("unchecked") + V obj = (V)cache.get(ck); + if( obj==null ) { + obj = table.getDbObject(key,rs,tableName); + if( obj==null ) + obj = NULL; + cache.put(ck,obj); + } + return obj==NULL ? null : obj; + } + Map<CacheKey,DbObject> cache = cache0; + key = unique.get(key); + try { + synchronized(key) { + @SuppressWarnings("unchecked") + V obj = (V)cache.get(ck); + if( !isStale(obj) ) + return obj==NULL ? null : obj; + obj = table.getDbObject(key,rs,tableName); + if( !isStale( cache.put( ck, obj==null ? NULL : obj ) ) ) + throw new RuntimeException(); + return obj; + } + } finally { + unique.free(key); + } + } + + public void uncache(final K key) { + if( key == null ) + return; + final CacheKey ck = new CacheKey(this,key); + if( database.isInTransaction() ) { + doUncache(ck,transCache()); + } + database.runJustAfterCommit( new Runnable() { + public void run() { + doUncache(ck,cache0); + } + } ); + } + + private static void doUncache(CacheKey ck,Map<CacheKey,DbObject> cache) { + DbObject obj = cache.remove(ck); + if( obj!=null && obj!=NULL0 ) { + DbRecordExt rec = (DbRecordExt)obj.getDbRecord(); + rec.makeStale(); + } + } + + static void clearCache() { + while( !cache0.isEmpty() ) { + CacheKey[] a = cache0.keySet().toArray(new CacheKey[0]); + for( CacheKey ck : a ) { + doUncache( ck, cache0 ); + } + } + } + +}