001package gu.sql2java;
002
003import static com.google.common.base.Preconditions.*;
004import static gu.sql2java.SimpleLog.*;
005
006import java.util.Map;
007import java.util.concurrent.TimeUnit;
008
009import com.google.common.cache.CacheBuilder;
010import com.google.common.collect.ImmutableMap;
011
012import gu.sql2java.TableManager.Action;
013import gu.sql2java.exception.ObjectRetrievalException;
014import gu.sql2java.exception.RuntimeDaoException;
015
016public class TableCache<B extends BaseBean> extends ColumnCache<B> 
017        implements TableListener<B>{
018    private final Map<String, IKeyCache<B>> indexCachers;
019        /**
020         * constructor<br>
021         * @param metaData meta data for table 
022     * @param updateStrategy cache update strategy,{@link Constant#DEFAULT_STRATEGY} be used if {@code null}
023     * @param maximumSize maximum capacity of cache ,{@link Constant#DEFAULT_CACHE_MAXIMUMSIZE } be used if {@code null} or <=0,see also {@link CacheBuilder#maximumSize(long)}
024     * @param duration cache data expired time,{@link Constant#DEFAULT_DURATION} be used if {@code null} or <=0,see also {@link CacheBuilder#expireAfterWrite(long, TimeUnit)}
025     * @param unit time unit for {@code duration},{@link Constant#DEFAULT_TIME_UNIT} be used if {@code null} ,see also {@link CacheBuilder#expireAfterWrite(long, TimeUnit)}
026         */
027        public TableCache(RowMetaData metaData,UpdateStrategy updateStrategy,Long maximumSize, Long duration, TimeUnit unit) {
028                super(metaData,null,updateStrategy,maximumSize, duration, unit);
029                ImmutableMap.Builder<String, IKeyCache<B>> builder = ImmutableMap.builder();
030                for(IndexMetaData index : metaData.getUniqueIndices().values()){
031                        builder.put(
032                                        index.name, 
033                                        new ColumnCache<B>(
034                                                                        this.metaData,
035                                                                        index.name,
036                                                                        this.updateStrategy,
037                                                                        this.maximumSize,
038                                                                        this.duration,
039                                                                        this.unit));
040                }
041                indexCachers = builder.build();
042                manager.bindForeignKeyListenerForDeleteRule();
043        }
044        /** ×¢²áÕìÌýÆ÷ */
045    public void registerListener() {        
046        manager.registerListener(this);
047        if(debug){
048                log("REGISTER LISTENER FOR INDEX AND PRIMARY KEY of " + metaData.tablename);
049        }
050    }
051    /** ×¢ÏúÕìÌýÆ÷ */
052    public void unregisterListener() {
053        manager.unregisterListener(this);
054    }
055    
056    /**
057     * force update bean to all caches that excluding specified by {@link #recursive}
058     * @param bean
059     */
060    private void updateAll(B bean){
061        update(bean, UpdateStrategy.always);
062        for(IKeyCache<B> cache : indexCachers.values()){
063                cache.update(bean, UpdateStrategy.always);
064        }
065    }
066    /**
067     * return record (B) that unique indexed by 'keys'
068     * @param indexName index name 
069     * @param keys values for index key 
070     * @return B instance if found
071     * @throws ObjectRetrievalException not found record
072     */
073    public B getBeanByIndex(String indexName,Object... keys)throws ObjectRetrievalException{
074                return checkNotNull(indexCachers.get(indexName),"INVALID indexName %s",indexName).getBean(keys);
075        }
076        /**
077         * return record (B) that unique indexed by 'keys'
078     * @param indexName index name 
079     * @param keys values for index key 
080     * @return B instance if found,or null
081         */
082        public B getBeanByIndexUnchecked(String indexName,Object... keys){
083                return checkNotNull(indexCachers.get(indexName),"INVALID indexName %s",indexName).getBeanUnchecked(keys);
084        }
085        
086        /**
087         * wrap the 'action' for updating cache while retrieve data from database 
088         * @param action
089         * @return Action
090         */
091        public Action<B> wrap(Action<B> action){
092                if(action != null){
093                if(!(action instanceof TableCache.CacheWrapper)){
094                        action = new CacheWrapper(action);
095                }
096            }
097                return action;
098        }
099        
100        //////////////////////////////////////////////
101    // TableListener IMPLEMENTATION
102        //////////////////////////////////////////////
103        
104        @Override
105    public void afterUpdate(B bean) {
106        update(bean);
107        for(IKeyCache<B> cache : indexCachers.values()){
108                cache.update(bean);
109        }
110    }
111    
112    @Override
113    public void afterInsert(B bean) {
114        update(bean);
115        for(IKeyCache<B> cache : indexCachers.values()){
116                cache.update(bean);
117        }
118    }
119        
120        @Override
121    public void afterDelete(B bean) {
122        remove(bean);
123        for(IKeyCache<B> cache : indexCachers.values()){
124                cache.remove(bean);
125        }
126    }
127        
128        @Override
129        public void beforeInsert(B bean) throws RuntimeDaoException {}
130        @Override
131        public void beforeUpdate(B bean) throws RuntimeDaoException {}
132        @Override
133        public void beforeDelete(B bean) throws RuntimeDaoException {}
134        @Override
135        public void done() throws RuntimeDaoException {}
136        
137        @Override
138        public int hashCode() {
139                final int prime = 31;
140                int result = super.hashCode();
141                result = prime * result + ((indexCachers == null) ? 0 : indexCachers.hashCode());
142                return result;
143        }
144        @Override
145        public boolean equals(Object obj) {
146                if (this == obj) {
147                        return true;
148                }
149                if (!super.equals(obj)) {
150                        return false;
151                }
152                if (!(obj instanceof TableCache)) {
153                        return false;
154                }
155                TableCache<?> other = (TableCache<?>) obj;
156                if (indexCachers == null) {
157                        if (other.indexCachers != null) {
158                                return false;
159                        }
160                } else if (!indexCachers.equals(other.indexCachers)) {
161                        return false;
162                }
163                return true;
164        }
165        @Override
166        public String toString() {
167                StringBuilder builder = new StringBuilder();
168                builder.append("TableCache [super=");
169                builder.append(super.toString());
170                builder.append(",indexCachers=");
171                builder.append(indexCachers);
172                builder.append("]");
173                return builder.toString();
174        }
175    private class CacheWrapper implements Action<B>{
176        private final Action<B> action;
177        public CacheWrapper(Action<B>action){
178            this.action = checkNotNull(action,"action is null");
179        }
180        @Override
181        public void call(B bean) {
182            action.call(bean);
183                updateAll(bean);
184        }
185    }
186}