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}