comparison 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
comparison
equal deleted inserted replaced
67:9d0fefce6985 68:00520880ad02
1 /*
2 Copyright (c) 2008 Franklin Schmidt <fschmidt@gmail.com>
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21 */
22
23 package fschmidt.db.cache;
24
25 import java.sql.ResultSet;
26 import java.sql.SQLException;
27 import java.util.Map;
28 import java.util.HashMap;
29 import java.util.Set;
30 import java.util.HashSet;
31 import java.util.Collections;
32 import java.util.Collection;
33 import java.util.NoSuchElementException;
34 import fschmidt.db.util.Unique;
35 import fschmidt.db.util.WeakCacheMap;
36 import fschmidt.db.DbObject;
37 import fschmidt.db.DbKey;
38 import fschmidt.db.DbRecord;
39 import fschmidt.db.extend.DbRecordExt;
40 import fschmidt.db.extend.DbTableExt;
41 import fschmidt.db.extend.DbDatabaseExt;
42 import fschmidt.db.extend.DbTransaction;
43 import fschmidt.db.extend.FilterTable;
44
45
46 final class DbTableImpl<K extends DbKey,V extends DbObject<K,V>> extends FilterTable<K,V> {
47 private static final DbObject NULL0 = new DbObject(){
48 public DbRecord getDbRecord() { throw new RuntimeException(); }
49 };
50
51 @SuppressWarnings("unchecked")
52 private final V NULL = (V)NULL0; // hack
53
54 private static class CacheKey {
55 private final DbTableImpl table;
56 final Object key;
57
58 CacheKey(DbTableImpl table,Object key) {
59 this.table = table;
60 this.key = key;
61 }
62
63 @Override public boolean equals(Object obj) {
64 if( obj == this )
65 return true;
66 if( !(obj instanceof DbTableImpl.CacheKey) )
67 return false;
68 CacheKey ck = (CacheKey)obj;
69 return ck.table == table && ck.key.equals(key);
70 }
71
72 @Override public int hashCode() {
73 return table.hashCode() + key.hashCode();
74 }
75
76 }
77
78 private static final Map<CacheKey,DbObject> cache0 = Collections.synchronizedMap(new WeakCacheMap<CacheKey,DbObject>());
79 private final Unique<K> unique = new Unique<K>();
80 private final DbDatabaseImpl database;
81
82 DbTableImpl(DbTableExt<K,V> table,DbDatabaseImpl database) {
83 super(database,table);
84 this.database = database;
85 }
86
87 private Map<CacheKey,DbObject> transCache() {
88 Map<String,Object> transMap = database.transactionMap();
89 @SuppressWarnings("unchecked")
90 Map<CacheKey,DbObject> transCache = (Map<CacheKey,DbObject>)transMap.get("transCache");
91 if( transCache == null ) {
92 transCache = new WeakCacheMap<CacheKey,DbObject>();
93 transMap.put( "transCache", transCache );
94 }
95 return transCache;
96 }
97
98 private final boolean isStale(DbObject obj) {
99 if( obj==null )
100 return true;
101 if( obj==NULL )
102 return false;
103 /*
104 if( database.isInTransaction() ) {
105 return transCache().containsKey(obj.getDbRecord().getPrimaryKey());
106 } else {
107 return obj.getDbRecord().isStale();
108 }
109 */
110 return obj.getDbRecord().isStale();
111 }
112
113 public final V findByPrimaryKey(K key) {
114 CacheKey ck = new CacheKey(this,key);
115 if( database.isInTransaction() ) {
116 Map<CacheKey,DbObject> cache = transCache();
117 @SuppressWarnings("unchecked")
118 V obj = (V)cache.get(ck);
119 if( obj==null ) {
120 obj = table.findByPrimaryKey(key);
121 if( obj==null )
122 obj = NULL;
123 cache.put(ck,obj);
124 }
125 return obj==NULL ? null : obj;
126 }
127 Map<CacheKey,DbObject> cache = cache0;
128 key = unique.get(key);
129 try {
130 synchronized(key) {
131 @SuppressWarnings("unchecked")
132 V obj = (V)cache.get(ck);
133 if( !isStale(obj) )
134 return obj==NULL ? null : obj;
135 obj = table.findByPrimaryKey(key);
136 if( !isStale( cache.put( ck, obj==null ? NULL : obj ) ) )
137 throw new RuntimeException();
138 return obj;
139 }
140 } finally {
141 unique.free(key);
142 }
143 }
144
145 public final Map<K,V> findByPrimaryKey(Collection<K> keys) {
146 Map<K,V> map = new HashMap<K,V>();
147 Set<K> set = new HashSet<K>();
148 Map<CacheKey,DbObject> cache = database.isInTransaction() ? transCache() : cache0;
149 boolean inTrans = database.isInTransaction();
150 for( K key : keys ) {
151 CacheKey ck = new CacheKey(this,key);
152 @SuppressWarnings("unchecked")
153 V obj = (V)cache.get(ck);
154 if( obj==null || !inTrans && isStale(obj) ) {
155 set.add(key);
156 } else {
157 map.put( key, obj==NULL ? null : obj );
158 }
159 }
160 if( !set.isEmpty() ) {
161 Map<K,V> objs = table.findByPrimaryKey(set);
162 for( K key : set ) {
163 CacheKey ck = new CacheKey(this,key);
164 if( inTrans ) {
165 if( cache.get(ck) != null )
166 throw new RuntimeException();
167 V obj = table.findByPrimaryKey(key);
168 if( obj==null )
169 obj = NULL;
170 cache.put(ck,obj);
171 map.put( key, obj==NULL ? null : obj );
172 } else {
173 key = unique.get(key);
174 try {
175 synchronized(key) {
176 @SuppressWarnings("unchecked")
177 V obj = (V)cache.get(ck);
178 if( isStale(obj) ) {
179 obj = objs.get(key);
180 if( obj == null )
181 obj = NULL;
182 if( !isStale( cache.put(ck,obj) ) )
183 throw new RuntimeException();
184 }
185 map.put( key, obj==NULL ? null : obj );
186 }
187 } finally {
188 unique.free(key);
189 }
190 }
191 }
192 }
193 return map;
194 }
195
196 public V getDbObject(K key,ResultSet rs,String tableName)
197 throws SQLException
198 {
199 CacheKey ck = new CacheKey(this,key);
200 if( database.isInTransaction() ) {
201 Map<CacheKey,DbObject> cache = transCache();
202 @SuppressWarnings("unchecked")
203 V obj = (V)cache.get(ck);
204 if( obj==null ) {
205 obj = table.getDbObject(key,rs,tableName);
206 if( obj==null )
207 obj = NULL;
208 cache.put(ck,obj);
209 }
210 return obj==NULL ? null : obj;
211 }
212 Map<CacheKey,DbObject> cache = cache0;
213 key = unique.get(key);
214 try {
215 synchronized(key) {
216 @SuppressWarnings("unchecked")
217 V obj = (V)cache.get(ck);
218 if( !isStale(obj) )
219 return obj==NULL ? null : obj;
220 obj = table.getDbObject(key,rs,tableName);
221 if( !isStale( cache.put( ck, obj==null ? NULL : obj ) ) )
222 throw new RuntimeException();
223 return obj;
224 }
225 } finally {
226 unique.free(key);
227 }
228 }
229
230 public void uncache(final K key) {
231 if( key == null )
232 return;
233 final CacheKey ck = new CacheKey(this,key);
234 if( database.isInTransaction() ) {
235 doUncache(ck,transCache());
236 }
237 database.runJustAfterCommit( new Runnable() {
238 public void run() {
239 doUncache(ck,cache0);
240 }
241 } );
242 }
243
244 private static void doUncache(CacheKey ck,Map<CacheKey,DbObject> cache) {
245 DbObject obj = cache.remove(ck);
246 if( obj!=null && obj!=NULL0 ) {
247 DbRecordExt rec = (DbRecordExt)obj.getDbRecord();
248 rec.makeStale();
249 }
250 }
251
252 static void clearCache() {
253 while( !cache0.isEmpty() ) {
254 CacheKey[] a = cache0.keySet().toArray(new CacheKey[0]);
255 for( CacheKey ck : a ) {
256 doUncache( ck, cache0 );
257 }
258 }
259 }
260
261 }