gu.sql2java.TableCache Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sql2java-manager Show documentation
Show all versions of sql2java-manager Show documentation
sql2java manager class package for accessing database
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);
}
});
}
}
}