All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gu.sql2java.TableCache Maven / Gradle / Ivy

There is a newer version: 5.2.0
Show newest version
package gu.sql2java;

import static com.google.common.base.Preconditions.*;
import static gu.sql2java.SimpleLog.*;

import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;

import gu.sql2java.TableManager.Action;
import gu.sql2java.exception.ObjectRetrievalException;
import gu.sql2java.exception.RuntimeDaoException;

class TableCache extends ColumnCache 
	implements TableListener{
	/** 执行缓存更新的单线程池对象 */
	private static final ExecutorService executor = MoreExecutors.getExitingExecutorService(
			new ThreadPoolExecutor(1, 1,
	                0L, TimeUnit.MILLISECONDS,
	                new LinkedBlockingQueue(),
	                new ThreadFactoryBuilder().setNameFormat("sql2java-cache-updater-%d").build()));
    private final Map> indexCachers;
	/**
	 * constructor
* @param metaData meta data for table * @param updateStrategy cache update strategy,{@link Constant#DEFAULT_STRATEGY} be used if {@code null} * @param maximumSize maximum capacity of cache ,{@link Constant#DEFAULT_CACHE_MAXIMUMSIZE } be used if {@code null} or <=0,see also {@link CacheBuilder#maximumSize(long)} * @param duration cache data expired time,{@link Constant#DEFAULT_DURATION} be used if {@code null} or <=0,see also {@link CacheBuilder#expireAfterAccess(long, TimeUnit)} * @param unit time unit for {@code duration},{@link Constant#DEFAULT_TIME_UNIT} be used if {@code null} ,see also {@link CacheBuilder#expireAfterAccess(long, TimeUnit)} */ public TableCache(RowMetaData metaData,UpdateStrategy updateStrategy,Long maximumSize, Long duration, TimeUnit unit) { super(metaData,null,updateStrategy,maximumSize, duration, unit); ImmutableMap.Builder> builder = ImmutableMap.builder(); // 索引缓存对象的更新策略 UpdateStrategy indexStrategy = UpdateStrategy.refresh.equals(this.updateStrategy) ? UpdateStrategy.always : this.updateStrategy; for(IndexMetaData index : metaData.getUniqueIndices().values()){ builder.put( index.name, new ColumnCache( this.metaData, index.name, indexStrategy, this.maximumSize, this.duration, this.unit)); } indexCachers = builder.build(); manager.bindForeignKeyListenerForDeleteRule(); } /** 注册侦听器 */ public void registerListener() { manager.registerListener(this); if(debug){ log("REGISTER LISTENER FOR INDEX AND PRIMARY KEY of " + metaData.tablename); } } /** 注销侦听器 */ public void unregisterListener() { manager.unregisterListener(this); } /** * force update bean to all caches that excluding specified by {@link #recursive} * @param bean */ private void updateAll(B bean){ update(bean, UpdateStrategy.always); for(IKeyCache cache : indexCachers.values()){ cache.update(bean, UpdateStrategy.always); } } /** * return record (B) that unique indexed by 'keys' * @param indexName index name * @param keys values for index key * @return B instance if found * @throws ObjectRetrievalException not found record */ public B getBeanByIndex(String indexName,Object... keys)throws ObjectRetrievalException{ return checkNotNull(indexCachers.get(indexName),"INVALID indexName %s",indexName).getBean(keys); } /** * return record (B) that unique indexed by 'keys' * @param indexName index name * @param keys values for index key * @return B instance if found,or null */ public B getBeanByIndexUnchecked(String indexName,Object... keys){ return checkNotNull(indexCachers.get(indexName),"INVALID indexName %s",indexName).getBeanUnchecked(keys); } /** * wrap the 'action' for updating cache while retrieve data from database * @param action * @return {@link Action} instance */ public Action wrap(Action action){ if(action == null || action instanceof TableCache.CacheWrapper || action instanceof BaseTableManager.DeleteBeanAction){ return action; } return new CacheWrapper(action); } @Override public B remove(B bean) { B mem = super.remove(bean); for(IKeyCache cache : indexCachers.values()){ if(cache.hasValidKey(bean)){ cache.remove(bean); }else if(cache.hasValidKey(mem)){ cache.remove(mem); } } return mem; } /** * 删除当前缓存及indexCachers中所有缓存中主键(keys)指定的记录 * @param primaryKeys * @return 返回删除的记录,如果不存在返回{@code null} * @since 3.30.0 */ public B removeCached(Object... primaryKeys) { B removed = super.removeCached(primaryKeys); for(IKeyCache cache:indexCachers.values()) { cache.remove(removed); } return removed; } /** * 根据索引名删除当前缓存及indexCachers中所有缓存中主键(keys)指定的记录 * @param indexName 索引名 * @param indexKeys 索引对象的字段值 * @return 返回删除的记录,如果不存在返回{@code null} * @since 3.30.0 */ public B removeCachedByIndex(String indexName,Object... indexKeys) { if(null != indexName) { IKeyCache indexCache = checkNotNull(indexCachers.get(indexName),"INVALID indexName %s",indexName); return remove(indexCache.removeCached(indexKeys)); } return null; } ////////////////////////////////////////////// // TableListener IMPLEMENTATION ////////////////////////////////////////////// @Override public void afterUpdate(B bean) { if(!UpdateStrategy.refresh.equals(updateStrategy)){ // 从数据库获取完整数据记录 // 更新策略为refresh时,update方法会调用loadfromDatabase从数据库更新所以这里不需要执行 bean = loadfromDatabase(bean.primaryValues()); } update(bean); if(UpdateStrategy.refresh.equals(updateStrategy)){ // 从主键cache中获取完整数据记录 bean = getBean(bean.primaryValues()); } for(IKeyCache cache : indexCachers.values()){ cache.update(bean); } } @Override public void afterInsert(B bean) { update(bean); for(IKeyCache cache : indexCachers.values()){ cache.update(bean); } } @Override public void afterDelete(B bean) { remove(bean); } @Override public void beforeInsert(B bean) throws RuntimeDaoException {} @Override public void beforeUpdate(B bean) throws RuntimeDaoException {} @Override public void beforeDelete(B bean) throws RuntimeDaoException {} @Override public void done() throws RuntimeDaoException {} @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + ((indexCachers == null) ? 0 : indexCachers.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (!(obj instanceof TableCache)) { return false; } TableCache other = (TableCache) obj; if (indexCachers == null) { if (other.indexCachers != null) { return false; } } else if (!indexCachers.equals(other.indexCachers)) { return false; } return true; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("TableCache [super="); builder.append(super.toString()); builder.append(",indexCachers="); builder.append(indexCachers); builder.append("]"); return builder.toString(); } private class CacheWrapper implements Action{ private final Action action; public CacheWrapper(Actionaction){ this.action = checkNotNull(action,"action is null"); } @Override public void call(final B bean) { action.call(bean); // 异步执行缓存更新 executor.execute(new Runnable() { @Override public void run() { updateAll(bean); } }); } } }