001package gu.sql2java;
002
003import static com.google.common.base.Preconditions.checkNotNull;
004import static com.google.common.base.Preconditions.checkState;
005import static com.google.common.base.Preconditions.checkArgument;
006import static gu.sql2java.Managers.baseManagerOf;
007import static gu.sql2java.Managers.getBaseTableManager;
008import static com.google.common.base.MoreObjects.firstNonNull;
009
010import java.util.Collection;
011import java.util.Collections;
012import java.util.Map;
013import java.util.Map.Entry;
014import java.util.concurrent.ConcurrentMap;
015import java.util.concurrent.locks.ReentrantReadWriteLock;
016import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
017import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
018
019import com.google.common.base.Function;
020import com.google.common.base.Predicate;
021import com.google.common.base.Predicates;
022import com.google.common.collect.MapBuilder;
023import com.google.common.collect.Maps;
024import com.google.common.collect.Multimap;
025import com.google.common.collect.MultimapBuilder;
026import com.google.common.collect.Multimaps;
027import com.google.common.collect.SetMultimap;
028
029import gu.sql2java.IFuzzyMatchFilter.MatchErrorHandler;
030import gu.sql2java.TableManager.Action;
031import gu.sql2java.exception.RuntimeDaoException;
032
033/**
034 * 对表字段实现模糊搜索的基类
035 * @author guyadong
036 *
037 * @param <B> 数据库表记录类型
038 * @param <K> 搜索键类型
039 */
040public abstract class BaseFieldSearcher <B extends BaseBean,K>{
041        protected final RowMetaData metaData;
042        protected final BaseTableManager<B> manager;
043        private final int[] keyIds;
044        private final int[] effectColumnIds;
045        /**
046         * 主键--搜索键映射
047         */
048        protected final ConcurrentMap<Object[], K> pks;
049        protected final ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock();
050        private Collection<B> effectedBeans;
051        private BaseRow beforeUpdatedBean;
052        private final Listener listener;
053        private IFuzzyMatchFilter<K> defaultMatchFilter;
054        private MatchErrorHandler<K> errorHandler;
055        public BaseFieldSearcher(RowMetaData metaData,int ...effectColumnIds) {
056        this.metaData = checkNotNull(metaData,"metaData is null");
057                this.manager = getBaseTableManager(metaData.tablename);
058                this.keyIds = metaData.primaryKeyIds;
059                this.effectColumnIds = checkEffectIds(effectColumnIds);
060                this.pks = MapBuilder.newConcurrentMap();
061                this.listener = new Listener();
062                this.defaultMatchFilter = new BaseFuzzyMatchFilter.DefaultFuzzyFilter<>();
063        }
064        public BaseFieldSearcher(RowMetaData metaData,String ...effectColumnNames) {
065                this(metaData, checkNotNull(metaData,"metaData is null").columnIDsOf(effectColumnNames));
066        }
067        public <M extends TableManager<B>>BaseFieldSearcher(Class<M>interfaceClass,int[] effectColumnId) {
068                this(baseManagerOf(interfaceClass).metaData, effectColumnId);
069        }
070        public <M extends TableManager<B>>BaseFieldSearcher(Class<M>interfaceClass,String ... effectColumnNames) {
071                this(baseManagerOf(interfaceClass).metaData, effectColumnNames);
072        }
073        
074        public String getTablename(){
075                return metaData.tablename;
076        }
077        private int[] checkEffectIds(int[] effectColumnIds){
078                checkArgument(effectColumnIds != null && effectColumnIds.length > 0,"effectColumnIds is null or empty");
079                for(int columnId:checkNotNull(effectColumnIds,"effectColumnIds is null")){
080                        checkArgument(columnId>=0 && columnId<metaData.columnCount,"INVALID columnId %s",columnId);
081                }
082                return effectColumnIds;
083        }
084        
085        /**
086         * 根据bean记录计算出key的值
087         * @param bean
088         * @return
089         */
090        protected abstract K keyOf(B bean);
091        protected IFuzzyMatchFilter<K> getDefaultMatchFilter(){
092                return defaultMatchFilter;
093        }
094        public BaseFieldSearcher<B, K> setDefaultMatchFilter(IFuzzyMatchFilter<K> defaultMatchFilter) {
095                if(defaultMatchFilter != null){
096                        this.defaultMatchFilter = defaultMatchFilter;
097                }
098                return this;
099        }
100        public BaseFieldSearcher<B, K> setErrorHandler(MatchErrorHandler<K> errorHandler) {
101                this.errorHandler = errorHandler;
102                return this;
103        }
104        /**
105         * 返回当记录更新时受影响的其他记录,如果没有返回空集合,
106         * 子类可根据需要重写此方法
107         * @param beforeUpdateBean
108         * @return 受影响的记录集合
109         */
110        protected Collection<B> getEffectedBeansOnUpdate(B beforeUpdateBean){
111                return Collections.emptyList();
112        }
113        public BaseFieldSearcher<B, K> init(){
114                WriteLock lock = rwlock.writeLock();
115                lock.lock();
116                try{
117                        pks.clear();
118                        manager.loadAll(new Action<B>(){
119                                @Override
120                                public void call(B bean) {
121                                        add(bean);
122                                }});
123                        manager.registerListener(listener);
124                } finally {
125                        lock.unlock();
126                }
127                return this;
128        }
129        
130        public BaseFieldSearcher<B, K> uninit(){
131                WriteLock lock = rwlock.writeLock();
132                lock.lock();
133                try{
134                        pks.clear();
135                        manager.unregisterListener(listener);
136                } finally {
137                        lock.unlock();
138                }
139                return this;
140        }
141        protected void add(B bean) {
142                if(bean != null){
143                        K key = keyOf(bean);
144                        if(key != null){
145                                Object[] pk = bean.primaryValues();
146                                pks.put(pk,key);
147                        }
148                }
149        }
150        protected void update(B bean) {
151                if(bean != null){
152                        K newKey = keyOf(bean);
153                        Object[] pk = bean.primaryValues();
154                        if(newKey != null){
155                                // 增加新的key
156                                pks.put(pk, newKey);
157                        }
158                
159                }
160        }
161        /**
162         * @param key 请求匹配的key
163         * @param matchFlags 匹配标志
164         * @param matchFilter 模糊匹配过滤器
165         * @param pkFilter 主键过滤器,用于根据主键过滤表记录
166         * @return
167         */
168        public final Multimap<K, Object[]> searchPk(K key, int matchFlags,IFuzzyMatchFilter<K> matchFilter, Predicate<Object[]> pkFilter){
169                SetMultimap<K, Object[]> mm = MultimapBuilder.hashKeys().hashSetValues().build();
170                if(key == null){
171                        return mm;
172                }
173                ReadLock lock = rwlock.readLock();
174                lock.lock();
175                try {
176                        Map<Object[], K> matched = Maps.filterEntries(pks, new EntryMatchFilter(key,matchFlags,matchFilter, pkFilter));
177                        for( Entry<Object[], K> entry:matched.entrySet()){
178                                mm.put(entry.getValue(), entry.getKey());
179                        }
180                        return mm;
181                } finally {
182                        lock.unlock();
183                }
184        }
185        private static final Function<Object[],Object> onePk = new Function<Object[],Object>(){
186
187                @Override
188                public Object apply(Object[] input) {
189                        return input[0];
190                }
191        };
192        /**
193         * 单主键模糊匹配
194         * @param key 请求匹配的key
195         * @param matchFlags 匹配标志
196         * @param matchFilter 模糊匹配过滤器
197         * @param pkFilter 主键过滤器,用于根据主键过滤表记录
198         * @return
199         */
200        @SuppressWarnings("unchecked")
201        public final <T> Multimap<K, T> search(K key, int matchFlags, IFuzzyMatchFilter<K> matchFilter, Predicate<T> pkFilter){
202                checkState(keyIds.length == 1,"Unsupported Operation caused by the primary count > 1");
203                
204                Predicate<Object[]> objsPkFilters = null;
205                if(pkFilter != null){
206                        objsPkFilters = Predicates.compose(pkFilter,  (Function<Object[], T>)onePk);
207                }
208                Multimap<K, Object[]> pk = searchPk(key, matchFlags, matchFilter, objsPkFilters); 
209                return Multimaps.transformValues(pk, (Function<Object[], T>)onePk);
210        }
211        
212        public K getPk(Object[] pk){
213                if(pk == null){
214                        return null;
215                }
216                ReadLock lock = rwlock.readLock();
217                lock.lock();
218                try {
219                        return pks.get(pk);
220                } finally {
221                        lock.unlock();
222                }
223        }
224
225        public int[] getEffectColumnIds() {
226                return effectColumnIds;
227        }
228
229        /**
230         * @return first column id of effectColumnIds
231         */
232        public int getEffectColumnId() {
233                return effectColumnIds[0];
234        }
235        
236        /**
237         * @return first column name of effectColumnIds
238         */
239        public String getEffectColumnName() {
240                return metaData.columnNameOf(effectColumnIds[0]);
241        }
242        
243        private class Listener extends TableListener.Adapter<B>{
244                @Override
245                public void afterInsert(B bean) throws RuntimeDaoException {
246                        WriteLock lock = rwlock.writeLock();
247                        lock.lock();
248                        try{
249                                add(bean);
250                        } finally {
251                                lock.unlock();
252                        }
253                }
254                private boolean isModified(B bean){
255                        for(int columnId:effectColumnIds){
256                                if(bean.isModified(columnId)){
257                                        return true;
258                                }
259                        }
260                        return false;
261                }
262                @Override
263                public void beforeUpdate(B bean) throws RuntimeDaoException {
264                        // 保留更新前的数据
265                        beforeUpdatedBean = ((BaseRow)bean).clone();
266                        if(isModified(bean)){
267                                // 如果指定的字段被更新保留受影响的数据
268                                effectedBeans = getEffectedBeansOnUpdate(bean);
269                        }else{
270                                effectedBeans = Collections.emptyList();
271                        }
272                }
273        
274                @Override
275                public void afterUpdate(B bean) throws RuntimeDaoException {
276                        // effectedBeans 为 null,只可能因为侦听器是被异步调用的
277                        checkState(beforeUpdatedBean != null,"beforeUpdatedBean must not be null");
278                        WriteLock lock = rwlock.writeLock();
279                        lock.lock();
280                        try{
281                                for(B effectedBean:effectedBeans){
282                                        update(effectedBean);
283                                }
284                                update(bean);
285                        } finally {
286                                lock.unlock();
287                                beforeUpdatedBean = null;
288                        }
289                }
290        
291                @Override
292                public void afterDelete(B bean) throws RuntimeDaoException {
293                        WriteLock lock = rwlock.writeLock();
294                        lock.lock();
295                        try {
296                                Object[] pk = bean.primaryValues();
297                                pks.remove(pk);
298                        } finally {
299                                lock.unlock();
300                        }
301                }
302        }
303        class EntryMatchFilter implements Predicate<Entry<Object[], K>>{
304                private final IFuzzyMatchFilter<K> matchFilter;
305                private final Predicate<Object[]> pkFilter;
306                
307                EntryMatchFilter(K key,int matchFlags,IFuzzyMatchFilter<K> matchFilter, Predicate<Object[]> pkFilter) {
308                        this.matchFilter = firstNonNull(matchFilter,getDefaultMatchFilter()).withErrorHandler(errorHandler)
309                                        .withPattern(key, matchFlags);
310                        this.pkFilter = firstNonNull(pkFilter, Predicates.<Object[]>alwaysTrue());
311                }
312                @Override
313                public boolean apply(Entry<Object[], K> input) {
314                        return matchFilter.apply(input.getValue()) && pkFilter.apply(input.getKey());
315                }
316                
317        }
318}