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