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.table.loadcaching.java.vm
007// ______________________________________________________
008package net.gdface.facelog.db;
009
010import java.util.concurrent.ConcurrentMap;
011import java.util.concurrent.ExecutionException;
012import java.util.concurrent.TimeUnit;
013
014import com.google.common.cache.CacheBuilder;
015import com.google.common.cache.CacheLoader;
016import com.google.common.cache.LoadingCache;
017import com.google.common.util.concurrent.UncheckedExecutionException;
018
019import net.gdface.facelog.db.exception.ObjectRetrievalException;
020
021/**
022 * 
023 * 基于 {@link LoadingCache}实现表数据缓存,并可以通过{@link TableListener}实现缓存数据自动更新
024 * @author guyadong
025 *
026 * @param <K> 主键类型(Primary or Unique)
027 * @param <B> 数据库记录对象类型(Java Bean)
028 */
029public abstract class BaseTableLoadCaching<K ,B extends BaseBean<B>> implements ITableCache<K, B> {
030    private final LoadingCache<K, B> cache;
031    protected final ConcurrentMap<K, B> cacheMap;
032    protected final  TableListener.Adapter<B> tableListener;
033    /** 当前更新策略 */
034    private final UpdateStrategy updateStrategy;
035    /** 
036     * 返回bean中主键值
037     * @param bean input record bean
038     * @return K value
039     */
040    protected abstract K returnKey(B bean);
041    /** 
042     * 从数据库中加载主键(pk)指定的记录 
043     * @param key primary key
044     * @return B bean
045     * @throws Exception
046     */
047    protected abstract B loadfromDatabase(K key)throws Exception;
048
049    public BaseTableLoadCaching(){
050        this(DEFAULT_CACHE_MAXIMUMSIZE,
051                DEFAULT_DURATION,
052                DEFAULT_TIME_UNIT);
053    }
054    public BaseTableLoadCaching(long maximumSize){
055        this(maximumSize,
056                DEFAULT_DURATION,
057                DEFAULT_TIME_UNIT);
058    }
059    public BaseTableLoadCaching(long maximumSize,long durationMinutes){
060        this(maximumSize,durationMinutes,DEFAULT_TIME_UNIT);
061    }
062    public BaseTableLoadCaching(long maximumSize,long duration, TimeUnit unit) {
063        this(DEFAULT_STRATEGY,maximumSize,duration,unit);
064    }
065    /**
066     * 构造函数
067     * @param updateStrategy 缓存更新策略
068     * @param maximumSize 最大缓存容量,参见 {@link CacheBuilder#maximumSize(long)}
069     * @param duration 失效时间,参见 {@link CacheBuilder#expireAfterWrite(long, TimeUnit)}
070     * @param unit {@code duration}的时间单位
071     */
072    public BaseTableLoadCaching(UpdateStrategy updateStrategy,long maximumSize,long duration, TimeUnit unit) {        
073        if(null == updateStrategy ){
074            updateStrategy = DEFAULT_STRATEGY;
075        }
076        if(0 >= maximumSize){
077            maximumSize = DEFAULT_CACHE_MAXIMUMSIZE;
078        }
079        if(0 >= duration){
080            maximumSize = DEFAULT_DURATION;
081        }
082        if(null == unit){
083            unit = DEFAULT_TIME_UNIT;
084        }
085        this.updateStrategy = updateStrategy;
086        cache = CacheBuilder.newBuilder()
087            .maximumSize(maximumSize)
088            .expireAfterWrite(duration, unit)
089            .build(
090                new CacheLoader<K,B>() {
091                    @Override
092                    public B load(K key) throws Exception {
093                        return loadfromDatabase(key);
094                    }});
095        cacheMap = cache.asMap();
096        // 初始化侦听器,当表数据改变时自动更新缓存
097        tableListener = new TableListener.Adapter<B>(){
098            @Override
099            public void afterUpdate(B bean) {
100                update(bean);
101            }
102            
103            @Override
104            public void afterInsert(B bean) {
105                update(bean);
106            }
107            
108            @Override
109            public void afterDelete(B bean) {
110                // the remove method allow null key
111                // see also com.google.common.cache.LocalCache.remove(Object key)
112                cacheMap.remove(returnKey(bean));
113            }};
114    }
115    /**
116     * @see com.google.common.cache.LoadingCache#get(Object)
117     */
118    @Override
119    public B getBean(K key)throws ExecutionException{
120        return cache.get(key);
121    }
122    /**
123     * @see com.google.common.cache.LoadingCache#getIfPresent(Object)
124     */
125    @Override
126    public B getBeanIfPresent(K key){
127        return null == key ? null : cache.getIfPresent(key);
128    }
129    /**
130     * @see com.google.common.cache.LoadingCache#getUnchecked(Object)
131     */
132    @Override
133    public B getBeanUnchecked(K key){
134        try{
135            return cache.getUnchecked(key);
136        }catch(UncheckedExecutionException e){
137            if(e.getCause() instanceof ObjectRetrievalException){
138                return null;
139            }
140            throw e;
141        }        
142    }
143    @Override
144    public void remove(B bean){
145        cacheMap.remove(returnKey(bean));
146    }
147    /**
148     * 根据当前更新策略({@link UpdateStrategy})将{@code bean}更新到缓存
149     * @see ITableCache#update(net.gdface.facelog.db.BaseBean)
150     */
151    @Override
152    public void update(B bean){
153        if(UpdateStrategy.refresh == updateStrategy){
154            updateStrategy.update(cacheMap, new ReloadEntry(returnKey(bean)));
155        }else{
156            updateStrategy.update(cacheMap, new ImmutableEntry<K,B>(returnKey(bean),bean));
157        }
158    }
159    private class ReloadEntry extends ImmutableEntry<K,B>{
160        public ReloadEntry(K key) {
161            super(key);
162        }
163
164        @Override
165        public B reload()throws Exception {
166            return loadfromDatabase(getKey());
167        }        
168    }
169    public UpdateStrategy getUpdateStrategy() {
170        return updateStrategy;
171    }
172    /**
173     * @return cacheMap
174     */
175    public ConcurrentMap<K, B> getCacheMap() {
176        return cacheMap;
177    }
178}
179