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}