001// ______________________________________________________ 002// Generated by sql2java - https://github.com/10km/sql2java-2-6-7 (custom branch) 003// modified by guyadong from 004// sql2java original version https://sourceforge.net/projects/sql2java/ 005// JDBC driver used at code generation time: com.mysql.jdbc.Driver 006// template: base.junction.table.cache.java.vm 007// ______________________________________________________ 008package net.gdface.facelog.db; 009 010import java.util.Collection; 011import java.util.Objects; 012import java.util.Set; 013import java.util.concurrent.ConcurrentMap; 014import java.util.concurrent.ExecutionException; 015import java.util.concurrent.TimeUnit; 016 017import com.google.common.base.Function; 018import com.google.common.base.Predicate; 019import com.google.common.cache.CacheBuilder; 020import com.google.common.cache.CacheLoader; 021import com.google.common.cache.LoadingCache; 022import com.google.common.collect.Collections2; 023import com.google.common.collect.ImmutableSet; 024import com.google.common.collect.Sets; 025import com.google.common.util.concurrent.UncheckedExecutionException; 026import static com.google.common.base.Preconditions.checkNotNull; 027 028import net.gdface.facelog.db.ITableCache.ImmutableEntry; 029import net.gdface.facelog.db.ITableCache.UpdateStrategy; 030import net.gdface.facelog.db.exception.ObjectRetrievalException; 031 032/** 033 * 034 * 基于 {@link LoadingCache}实现MANY-TO-MANY 联接表(junction table)数据缓存,并可以通过{@link TableListener}实现缓存数据自动更新<br> 035 * 联接表(junction table)定义:主键为两个字段K1,K2,并且两个字段又各是联接另外两张表的外键 036 * @author guyadong 037 * 038 * @param <K1> 外键1类型(Foreign Key) 039 * @param <K2> 外键2类型(Foreign Key) 040 * @param <B> 数据库记录对象类型(Java Bean) 041 */ 042public abstract class BaseJunctionTableCache<K1 ,K2,B extends BaseBean<B>> { 043 @SuppressWarnings("serial") 044 private static class CollectionReturnException extends Exception{} 045 public final class Key{ 046 public K1 k1; 047 public K2 k2; 048 Key(K1 k1, K2 k2) { 049 this.k1 = k1; 050 this.k2 = k2; 051 } 052 @Override 053 public int hashCode() { 054 final int prime = 31; 055 int result = 1; 056 result = prime * result + ((k1 == null) ? 0 : k1.hashCode()); 057 result = prime * result + ((k2 == null) ? 0 : k2.hashCode()); 058 return result; 059 } 060 @Override 061 public boolean equals(Object obj) { 062 if (this == obj){ 063 return true; 064 } 065 if (obj == null || Key.class != obj.getClass()){ 066 return false; 067 } 068 @SuppressWarnings("unchecked") 069 Key other = (Key) obj; 070 return (Objects.equals(k1, other.k1) && Objects.equals(k2, other.k2)); 071 } 072 } 073 /** 返回一个用于查询的临时对象 */ 074 private Key asTmpKey(K1 k1,K2 k2){ 075 return new Key(k1,k2); 076 } 077 private final LoadingCache<Key, B> cache; 078 private final ConcurrentMap<Key, B> cacheMap; 079 protected final TableListener.Adapter<B> tableListener; 080 /** 当前更新策略 */ 081 private final UpdateStrategy updateStrategy; 082 private final Function<B, K1> funReturnK1 =new Function<B,K1>(){ 083 @Override 084 public K1 apply(B input) { 085 return returnK1(input); 086 }}; 087 private final Function<B, K2> funReturnK2 = new Function<B,K2>(){ 088 @Override 089 public K2 apply(B input) { 090 return returnK2(input); 091 }}; 092 /** 093 * 返回{@code bean}中外键K1值,{@code bean}为{@code null}时返回{@code null} 094 * @param bean 095 * @return 096 */ 097 protected abstract K1 returnK1(B bean); 098 /** 099 * 返回{@code bean}中外键K2值,{@code bean}为{@code null}时返回{@code null} 100 * @param bean 101 * @return 102 */ 103 protected abstract K2 returnK2(B bean); 104 /** 105 * 从数据库中加载外键指定的记录,没有找到指定的记录则抛出异常{@link ObjectRetrievalException}<br> 106 * {@code Key.k1,Key.k2}都不为{@code null}返回{@code B},否则返回{@code Collection<B>}<br> 107 * 不可返回{@code null} 108 * @param key 109 * @return 110 * @throws ObjectRetrievalException 111 * @throws Exception 112 */ 113 protected abstract Object loadfromDatabase(Key key)throws Exception; 114 /** 注册侦听器 */ 115 public abstract void registerListener(); 116 /** 注销侦听器 */ 117 public abstract void unregisterListener(); 118 public BaseJunctionTableCache(){ 119 this(ITableCache.DEFAULT_CACHE_MAXIMUMSIZE, 120 ITableCache.DEFAULT_DURATION, 121 ITableCache.DEFAULT_TIME_UNIT); 122 } 123 public BaseJunctionTableCache(long maximumSize){ 124 this(maximumSize, 125 ITableCache.DEFAULT_DURATION, 126 ITableCache.DEFAULT_TIME_UNIT); 127 } 128 public BaseJunctionTableCache(long maximumSize,long durationMinutes){ 129 this(maximumSize,durationMinutes,ITableCache.DEFAULT_TIME_UNIT); 130 } 131 public BaseJunctionTableCache(long maximumSize,long duration, TimeUnit unit) { 132 this(ITableCache.DEFAULT_STRATEGY,maximumSize,duration,unit); 133 } 134 /** 135 * 构造函数 136 * @param updateStrategy 缓存更新策略 137 * @param maximumSize 最大缓存容量,参见 {@link CacheBuilder#maximumSize(long)} 138 * @param duration 失效时间,参见 {@link CacheBuilder#expireAfterWrite(long, TimeUnit)} 139 * @param unit {@code duration}的时间单位 140 */ 141 public BaseJunctionTableCache(UpdateStrategy updateStrategy,long maximumSize,long duration, TimeUnit unit) { 142 if(null == updateStrategy ){ 143 updateStrategy = ITableCache.DEFAULT_STRATEGY; 144 } 145 if(0 >= maximumSize){ 146 maximumSize = ITableCache.DEFAULT_CACHE_MAXIMUMSIZE; 147 } 148 if(0 >= duration){ 149 maximumSize = ITableCache.DEFAULT_DURATION; 150 } 151 if(null == unit){ 152 unit = ITableCache.DEFAULT_TIME_UNIT; 153 } 154 this.updateStrategy = updateStrategy; 155 cache = CacheBuilder.newBuilder() 156 .maximumSize(maximumSize) 157 .expireAfterWrite(duration, unit) 158 .build( 159 new CacheLoader<Key,B>() { 160 @SuppressWarnings("unchecked") 161 @Override 162 public B load(Key key) throws Exception { 163 Object obj = loadfromDatabase(key); 164 try{ 165 return (B)obj; 166 }catch(ClassCastException e){ 167 if(obj instanceof Collection){ 168 for(B bean:(Collection<B>)obj){ 169 update(bean); 170 } 171 throw new CollectionReturnException(); 172 } 173 throw e; 174 } 175 }}); 176 cacheMap = cache.asMap(); 177 178 // 初始化侦听器,当表数据改变时自动更新缓存 179 tableListener = new TableListener.Adapter<B>(){ 180 @Override 181 public void afterUpdate(B bean) { 182 update(bean); 183 } 184 185 @Override 186 public void afterInsert(B bean) { 187 update(bean); 188 } 189 190 @Override 191 public void afterDelete(B bean) { 192 remove(bean); 193 }}; 194 } 195 private<K> Set<B> filter(final K k,final Function <B,K>fun){ 196 return null == k 197 ? ImmutableSet.<B>of() 198 : Sets.newHashSet(Collections2.filter(cacheMap.values(), new Predicate<B>(){ 199 @Override 200 public boolean apply(B input) { 201 return Objects.equals(k, fun.apply(input)); 202 }})); 203 } 204 /** 205 * 返回数据库中匹配{@code k1}的所有记录,没有结果返回则抛出异常 206 * @see com.google.common.cache.LoadingCache#get(Object) 207 * @throws ObjectRetrievalException not found 208 * @throws ExecutionException 209 */ 210 public Set<B> getBeansByK1(K1 k1)throws ExecutionException{ 211 try{ 212 cache.get(asTmpKey(k1,null)); 213 // dead code 不会执行到这里 214 return null; 215 }catch(ExecutionException e){ 216 if( e.getCause() instanceof CollectionReturnException){ 217 return filter(k1,funReturnK1); 218 } 219 throw e; 220 } 221 } 222 /** 223 * 返回内存cache中匹配{@code k1}的所有记录,没有结果返回则返回empty Set 224 * @see com.google.common.cache.LoadingCache#getIfPresent(Object) 225 */ 226 public Set<B> getBeansByK1IfPresent(K1 k1){ 227 return filter(k1,funReturnK1); 228 } 229 /** 230 * 返回数据库中匹配{@code k1}的所有记录,没有结果返回则返回empty Set 231 * @see com.google.common.cache.LoadingCache#getUnchecked(Object) 232 * @throws UncheckedExecutionException 233 */ 234 public Set<B> getBeansByK1Unchecked(K1 k1){ 235 try{ 236 cache.getUnchecked(asTmpKey(k1,null)); 237 // dead code 不会执行到这里 238 return null; 239 }catch(UncheckedExecutionException e){ 240 if( e.getCause() instanceof CollectionReturnException){ 241 return filter(k1,funReturnK1); 242 } 243 throw e; 244 } 245 } 246 /** see also {@link #getBeansByK1(Object)}*/ 247 public Set<B> getBeansByK2(K2 k2)throws ExecutionException{ 248 try{ 249 cache.get(asTmpKey(null,k2)); 250 // dead code 不会执行到这里 251 return null; 252 }catch(ExecutionException e){ 253 if( e.getCause() instanceof CollectionReturnException){ 254 return filter(k2,funReturnK2); 255 } 256 throw e; 257 } 258 } 259 /** see also {@link #getBeansByK1IfPresent(Object)} */ 260 public Set<B> getBeansByK2IfPresent(K2 k2){ 261 return filter(k2,funReturnK2); 262 } 263 /** see also {@link #getBeansByK1Unchecked(Object)} */ 264 public Set<B> getBeansByK2Unchecked(K2 k2){ 265 try{ 266 cache.getUnchecked(asTmpKey(null,k2)); 267 // dead code 不会执行到这里 268 return null; 269 }catch(UncheckedExecutionException e){ 270 if( e.getCause() instanceof CollectionReturnException){ 271 return filter(k2,funReturnK2); 272 } 273 throw e; 274 } 275 } 276 /** 返回数据库中与{@code k1,k2}(不可为{@code null})匹配的记录<br> 277 * 如果没找到记录则抛出异常 278 * @see com.google.common.cache.LoadingCache#get(Object) 279 * @throws ObjectRetrievalException not found 280 * @throws ExecutionException 281 */ 282 public B getBean(K1 k1,K2 k2) throws ExecutionException{ 283 return cache.get(new Key(checkNotNull(k1),checkNotNull(k2))); 284 } 285 /** 286 * 返回内存cache中与{@code k1,k2}匹配的记录<br> 287 * 如果没找到记录则返回{@code null} 288 * @see com.google.common.cache.LoadingCache#getIfPresent(Object) 289 */ 290 public B getBeanIfPresent(K1 k1,K2 k2){ 291 return null ==k1 || null == k2 ? null : cache.getIfPresent(asTmpKey(k1,k2)); 292 } 293 /** 294 * 返回数据库中与{@code k1,k2}匹配的记录<br> 295 * 如果没找到记录则返回{@code null} 296 * @see com.google.common.cache.LoadingCache#getUnchecked(Object) 297 */ 298 public B getBeanUnchecked(K1 k1,K2 k2){ 299 try{ 300 return null ==k1 || null == k2 ? null : cache.getUnchecked(new Key(k1,k2)); 301 }catch(UncheckedExecutionException e){ 302 if(e.getCause() instanceof ObjectRetrievalException){ 303 return null; 304 } 305 throw e; 306 } 307 } 308 309 /** 从缓存中删除{@code bean}指定的记录 */ 310 public void remove(B bean){ 311 K1 k1 = returnK1(bean); 312 K2 k2 = returnK2(bean); 313 if(null !=k1 && null != k2){ 314 cacheMap.remove(new Key(k1,k2)); 315 } 316 } 317 /** 318 * 更新{@code bean}到指定的缓存对象{@code cacheMap} 319 * @param bean 320 */ 321 public void update(B bean){ 322 K1 k1 = returnK1(bean); 323 K2 k2 = returnK2(bean); 324 if(null !=k1 && null != k2){ 325 if(UpdateStrategy.refresh == updateStrategy){ 326 updateStrategy.update(cacheMap, new ReloadEntry(new Key(k1,k2))); 327 }else{ 328 updateStrategy.update(cacheMap, new ImmutableEntry<Key,B>(new Key(k1,k2),bean)); 329 } 330 } 331 } 332 private class ReloadEntry extends ImmutableEntry<Key,B>{ 333 public ReloadEntry(Key key) { 334 super(key); 335 } 336 337 @SuppressWarnings("unchecked") 338 @Override 339 public B reload()throws Exception { 340 return (B) loadfromDatabase(getKey()); 341 } 342 } 343 public UpdateStrategy getUpdateStrategy() { 344 return updateStrategy; 345 } 346}