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}