org.jfaster.mango.operator.cache.CacheDriver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mango Show documentation
Show all versions of mango Show documentation
mango is a dao framework.
/*
* Copyright 2014 mango.jfaster.org
*
* The Mango Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package org.jfaster.mango.operator.cache;
import org.jfaster.mango.annotation.Cache;
import org.jfaster.mango.annotation.CacheBy;
import org.jfaster.mango.annotation.CacheIgnored;
import org.jfaster.mango.binding.BindingParameter;
import org.jfaster.mango.binding.BindingParameterInvoker;
import org.jfaster.mango.binding.InvocationContext;
import org.jfaster.mango.binding.ParameterContext;
import org.jfaster.mango.descriptor.MethodDescriptor;
import org.jfaster.mango.descriptor.ParameterDescriptor;
import org.jfaster.mango.exception.DescriptionException;
import org.jfaster.mango.parser.ASTJDBCIterableParameter;
import org.jfaster.mango.parser.ASTJDBCParameter;
import org.jfaster.mango.parser.ASTRootNode;
import org.jfaster.mango.stat.OneExecuteStat;
import org.jfaster.mango.util.Iterables;
import org.jfaster.mango.util.Strings;
import org.jfaster.mango.util.reflect.Reflection;
import org.jfaster.mango.util.reflect.TypeWrapper;
import javax.annotation.Nullable;
import java.lang.reflect.Type;
import java.util.*;
/**
* @author ash
*/
public class CacheDriver implements CacheBase, CacheSingleKey, CacheMultiKey {
/**
* 具体的缓存实现,通过{@link this#setCacheHandler(CacheHandler)}初始化
*/
private CacheHandler cacheHandler;
private Class> daoClass;
private Type returnType;
private Type elementType;
/**
* 缓存key前缀
*/
private String prefix;
/**
* 缓存过期控制
*/
private CacheExpire cacheExpire;
/**
* expire的数量
*/
private int expireNum;
/**
* 是否缓存数据库中的null对象
*/
private boolean cacheNullObject;
/**
* 是否缓存数据库中的空列表
*/
private boolean cacheEmptyList;
/**
* cacheBy相关信息
*/
private List cacheByItems = new ArrayList();
/**
* 是否使用多key缓存
*/
private boolean useMultipleKeys;
/**
* "msg_id in (:1)"中的msg_id
*/
private String propertyOfMapper;
public CacheDriver(MethodDescriptor md, ASTRootNode rootNode, CacheHandler cacheHandler, ParameterContext context) {
this.cacheHandler = cacheHandler;
this.daoClass = md.getDaoClass();
this.returnType = md.getReturnType();
this.elementType = md.getReturnDescriptor().getMappedType();
init(md, rootNode, context);
}
@Override
public boolean isUseMultipleKeys() {
return useMultipleKeys;
}
@Override
public boolean isCacheNullObject() {
return cacheNullObject;
}
@Override
public boolean isCacheEmptyList() {
return cacheEmptyList;
}
@Override
public void setToCache(String key, Object value, OneExecuteStat stat) {
boolean success = false;
long now = System.nanoTime();
try {
cacheHandler.set(key, value, getExptimeSeconds(), daoClass);
success = true;
} finally {
long cost = System.nanoTime() - now;
if (success) {
stat.recordCacheSetSuccess(cost);
} else {
stat.recordCacheSetException(cost);
}
}
}
@Override
public void addToCache(String key, Object value, OneExecuteStat stat) {
boolean success = false;
long now = System.nanoTime();
try {
cacheHandler.add(key, value, getExptimeSeconds(), daoClass);
success = true;
} finally {
long cost = System.nanoTime() - now;
if (success) {
stat.recordCacheAddSuccess(cost);
} else {
stat.recordCacheAddException(cost);
}
}
}
@Override
public void deleteFromCache(String key, OneExecuteStat stat) {
boolean success = false;
long now = System.nanoTime();
try {
cacheHandler.delete(key, daoClass);
success = true;
} finally {
long cost = System.nanoTime() - now;
if (success) {
stat.recordCacheDeleteSuccess(cost);
} else {
stat.recordCacheDeleteException(cost);
}
}
}
@Override
public void batchDeleteFromCache(Set keys, OneExecuteStat stat) {
if (keys.size() > 0) {
boolean success = false;
long now = System.nanoTime();
try {
cacheHandler.batchDelete(keys, daoClass);
success = true;
} finally {
long cost = System.nanoTime() - now;
if (success) {
stat.recordCacheBatchDeleteSuccess(cost);
} else {
stat.recordCacheBatchDeleteException(cost);
}
}
}
}
@Nullable
@Override
public Object getFromCache(String key, OneExecuteStat stat) {
boolean success = false;
long now = System.nanoTime();
try {
Object value = cacheHandler.get(key, returnType, daoClass);
success = true;
return value;
} finally {
long cost = System.nanoTime() - now;
if (success) {
stat.recordCacheGetSuccess(cost);
} else {
stat.recordCacheGetException(cost);
}
}
}
@Nullable
@Override
public Map getBulkFromCache(Set keys, OneExecuteStat stat) {
if (keys.size() > 0) {
boolean success = false;
long now = System.nanoTime();
try {
Map values = cacheHandler.getBulk(keys, elementType, daoClass);
success = true;
return values;
} finally {
long cost = System.nanoTime() - now;
if (success) {
stat.recordCacheGetBulkSuccess(cost);
} else {
stat.recordCacheGetBulkException(cost);
}
}
}
return null;
}
@Override
public String getCacheKey(InvocationContext context) {
StringBuilder key = new StringBuilder(prefix);
for (CacheByItem item : cacheByItems) {
Object obj = context.getBindingValue(item.getbindingParameterInvoker());
if (obj == null) {
throw new NullPointerException("value of " + item.getFullName() + " can't be null");
}
key.append("_").append(obj);
}
return key.toString();
}
@Override
public Class> getOnlyCacheByClass() {
return getOnlyCacheByItem(cacheByItems).getActualClass();
}
@Override
public Set getCacheKeys(InvocationContext context) {
Iterables iterables = new Iterables(getOnlyCacheByObj(context));
Set keys = new HashSet();
for (Object obj : iterables) {
String key = getCacheKey(obj);
keys.add(key);
}
return keys;
}
@Override
public String getCacheKey(Object obj) {
return prefix + "_" + obj;
}
@Override
public Object getOnlyCacheByObj(InvocationContext context) {
CacheByItem item = getOnlyCacheByItem(cacheByItems);
Object obj = context.getBindingValue(item.getbindingParameterInvoker());
if (obj == null) {
throw new NullPointerException("value of " + item.getFullName() + " can't be null");
}
return obj;
}
@Override
public void setOnlyCacheByObj(InvocationContext context, Object obj) {
CacheByItem item = getOnlyCacheByItem(cacheByItems);
context.setBindingValue(item.getbindingParameterInvoker(), obj);
}
@Override
public String getPropertyOfMapper() {
return propertyOfMapper;
}
@Override
public int getExptimeSeconds() {
return cacheExpire.getExpireTime() * expireNum;
}
private void init(MethodDescriptor md, ASTRootNode rootNode, ParameterContext context) {
for (ParameterDescriptor pd : md.getParameterDescriptors()) {
CacheBy cacheByAnno = pd.getAnnotation(CacheBy.class);
if (cacheByAnno != null) {
String parameterName = context.getParameterNameByPosition(pd.getPosition());
String propertyPaths = cacheByAnno.value();
for (String propertyPath : propertyPaths.split(",")) {
propertyPath = propertyPath.trim();
BindingParameterInvoker invoker = context.getBindingParameterInvoker(BindingParameter.create(parameterName, propertyPath, null));
Type cacheByType = invoker.getTargetType();
TypeWrapper tw = new TypeWrapper(cacheByType);
cacheByItems.add(new CacheByItem(parameterName, propertyPath, tw.getMappedClass(), invoker));
useMultipleKeys = useMultipleKeys || tw.isIterable();
}
}
}
int cacheByNum = cacheByItems.size();
if (useMultipleKeys && cacheByNum > 1) { // 当@CacheBy修饰in语句时,只能有1个@CacheBy
throw new IncorrectCacheByException("when @CacheBy modification interable parameter, " +
"there can be only one @CacheBy");
}
Cache cacheAnno = md.getAnnotation(Cache.class);
CacheIgnored cacheIgnoredAnno = md.getAnnotation(CacheIgnored.class);
if (cacheAnno != null) { // dao类使用cache
if (cacheIgnoredAnno == null) { // method不禁用cache
if (cacheByNum == 0) {
throw new IllegalStateException("if use cache, each method " +
"expected one or more @CacheBy annotation on parameter " +
"but found 0");
}
prefix = cacheAnno.prefix();
cacheExpire = Reflection.instantiateClass(cacheAnno.expire());
expireNum = cacheAnno.num();
cacheNullObject = cacheAnno.cacheNullObject();
cacheEmptyList = cacheAnno.cacheEmptyList();
checkCacheBy(rootNode, cacheByItems);
} else {
if (cacheByNum > 0) {
throw new DescriptionException("if @CacheIgnored is on method, " +
"@CacheBy can not on method's parameter");
}
}
} else {
if (cacheByNum > 0) {
throw new DescriptionException("if @Cache is not defined, " +
"@CacheBy can not on method's parameter");
}
if (cacheIgnoredAnno != null) {
throw new DescriptionException("if @Cache is not defined, " +
"@CacheIgnored can not on method");
}
}
if (useMultipleKeys) {
CacheByItem cacheByItem = getOnlyCacheByItem(cacheByItems);
for (ASTJDBCIterableParameter jip : rootNode.getJDBCIterableParameters()) {
if (jip.getBindingParameter().getParameterName().equals(cacheByItem.getParameterName())
&& jip.getBindingParameter().getPropertyPath().equals(cacheByItem.getPropertyPath())) {
propertyOfMapper = jip.getPropertyOfMapper();
break;
}
}
}
}
private static CacheByItem getOnlyCacheByItem(List cacheByItems) {
if (cacheByItems.size() != 1) {
throw new IllegalStateException("size of cacheByItems expected 1 but " + cacheByItems.size());
}
return cacheByItems.get(0);
}
/**
* 检测{@link CacheBy}定位到的参数db中是否有用到,如果db中没有用到,
* 则抛出{@link IncorrectCacheByException}
*/
private static void checkCacheBy(ASTRootNode rootNode, List cacheByItems) {
List jps = rootNode.getJDBCParameters();
for (CacheByItem cacheByItem : cacheByItems) {
String parameterName = cacheByItem.getParameterName();
String propertyPath = cacheByItem.getPropertyPath();
boolean pass = false;
for (ASTJDBCParameter jp : jps) {
if (jp.getBindingParameter().getParameterName().equals(parameterName) &&
jp.getBindingParameter().getPropertyPath().equals(propertyPath)) {
pass = true;
break;
}
}
List jips = rootNode.getJDBCIterableParameters();
for (ASTJDBCIterableParameter jip : jips) {
if (jip.getBindingParameter().getParameterName().equals(parameterName) &&
jip.getBindingParameter().getPropertyPath().equals(propertyPath)) {
pass = true;
break;
}
}
if (!pass) {
throw new IncorrectCacheByException("CacheBy " +
cacheByItem.getFullName() + " can't match any db parameter");
}
}
}
private static class CacheByItem {
private final String parameterName;
private final String propertyPath;
private final Class> actualClass;
private final BindingParameterInvoker bindingParameterInvoker;
public CacheByItem(
String parameterName,
String propertyPath,
Class> actualClass,
BindingParameterInvoker bindingParameterInvoker) {
this.parameterName = parameterName;
this.propertyPath = propertyPath;
this.actualClass = actualClass;
this.bindingParameterInvoker = bindingParameterInvoker;
}
public String getParameterName() {
return parameterName;
}
public String getPropertyPath() {
return propertyPath;
}
private Class> getActualClass() {
return actualClass;
}
private BindingParameterInvoker getbindingParameterInvoker() {
return bindingParameterInvoker;
}
public String getFullName() {
return Strings.getFullName(parameterName, propertyPath);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy