> ext
return list;
}
- /**
- * 是否开启缓存。
- *
- * 子类应注意是否需要重写{@linkplain #cacheCloneEntity(Entity)}方法。
- *
- *
- * @return
- */
- protected boolean cacheEnabled()
- {
- return (getCache() != null);
- }
-
- /**
- * 克隆缓存实体。
- *
- * 参考{@linkplain #cacheGet(Object)}、{@linkplain #cachePut(Object, Entity)}、{@linkplain #cachePutQueryResult(List)}。
- *
- *
- * 对于无序列化缓存(比如进程内缓存),应遵循{@linkplain CloneableEntity#clone()}规则;对于序列化缓存,则可直接返回原实体。
- *
- *
- * 此方法默认是现是:如果{@code value}是{@linkplain CloneableEntity},则返回{@linkplain CloneableEntity#clone()},否则,返回原对象。
- *
- *
- * @param value
- * @return
- */
- @SuppressWarnings("unchecked")
- protected T cacheCloneEntity(T value)
- {
- if (value instanceof CloneableEntity)
- return (T) ((CloneableEntity) value).clone();
-
- return value;
- }
-
- /**
- * 获取指定实体ID的缓存关键字。
- *
- * @param id
- * @return
- */
- protected Object toCacheKey(ID id)
- {
- return id;
- }
-
/**
* 从缓存中读取实体。
*
@@ -289,10 +241,10 @@ public abstract class AbstractMybatisEntityService> ext
*/
protected ValueWrapper cacheGet(ID id)
{
- if (!cacheEnabled())
+ if (!isCacheEnabled())
return null;
- ValueWrapper valueWrapper = cacheGet(getCache(), toCacheKey(id));
+ ValueWrapper valueWrapper = this.cache.get(toCacheKey(id));
if (valueWrapper == null)
return null;
@@ -317,13 +269,13 @@ public abstract class AbstractMybatisEntityService> ext
*/
protected void cachePut(ID id, T value)
{
- if (!cacheEnabled())
+ if (!isCacheEnabled())
return;
if (value != null)
value = cacheCloneEntity(value);
- cachePut(getCache(), toCacheKey(id), value);
+ this.cache.put(toCacheKey(id), value);
}
/**
@@ -336,7 +288,7 @@ public abstract class AbstractMybatisEntityService> ext
*/
protected void cachePutQueryResult(List values)
{
- if (!cacheEnabled())
+ if (!isCacheEnabled())
return;
int count = Math.min(values.size(), this.getCacheCountForQuery());
@@ -348,24 +300,147 @@ public abstract class AbstractMybatisEntityService> ext
if (value != null)
{
value = cacheCloneEntity(value);
- cachePut(getCache(), toCacheKey(value.getId()), value);
+ this.cache.put(toCacheKey(value.getId()), value);
}
}
}
protected void cacheEvict(ID id)
{
- if (!cacheEnabled())
+ if (!isCacheEnabled())
return;
- cacheEvict(getCache(), toCacheKey(id));
+ this.cache.evictImmediately(toCacheKey(id));
}
protected void cacheInvalidate()
{
- if (!cacheEnabled())
+ if (!isCacheEnabled())
return;
- cacheInvalidate(getCache());
+ this.cache.invalidate();
+ }
+
+ protected boolean isCacheEnabled()
+ {
+ return (this.cache != null && this.cache.isEnable());
+ }
+
+ /**
+ * 克隆缓存实体。
+ *
+ * 参考{@linkplain #cacheGet(Object)}、{@linkplain #cachePut(Object, Entity)}、{@linkplain #cachePutQueryResult(List)}。
+ *
+ *
+ * 如果{@linkplain #getCache()}的{@linkplain ServiceCache#isSerialized()}为{@code false}(比如进程内缓存),应遵循{@linkplain CloneableEntity#clone()}规则;
+ * 否则,可直接返回原实体。
+ *
+ *
+ * 此方法默认是现是:当需要克隆时,如果{@code value}是{@linkplain CloneableEntity},则返回{@linkplain CloneableEntity#clone()},否则,返回原对象。
+ *
+ *
+ * 调用此方法前应确保{@linkplain #isCacheEnabled()}为{@code true}。
+ *
+ *
+ * @param value
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ protected T cacheCloneEntity(T value)
+ {
+ if (this.cache.isSerialized())
+ return value;
+
+ if (value instanceof CloneableEntity)
+ return (T) ((CloneableEntity) value).clone();
+
+ return value;
+ }
+
+ /**
+ * 获取指定实体ID的缓存关键字。
+ *
+ * 调用此方法前应确保{@linkplain #isCacheEnabled()}为{@code true}。
+ *
+ *
+ * @param id
+ * @return
+ */
+ protected Object toCacheKey(ID id)
+ {
+ if (this.cache.isShared())
+ return new GlobalEntityCacheKey(getSqlNamespace(), id);
+ else
+ return id;
+ }
+
+ /**
+ * 全局实体缓存KEY。
+ *
+ * @author datagear@163.com
+ *
+ * @param
+ */
+ protected static class GlobalEntityCacheKey implements Serializable
+ {
+ private static final long serialVersionUID = 1L;
+
+ private final String namespace;
+
+ private final ID id;
+
+ public GlobalEntityCacheKey(String namespace, ID id)
+ {
+ super();
+ this.namespace = namespace;
+ this.id = id;
+ }
+
+ public String getNamespace()
+ {
+ return namespace;
+ }
+
+ public ID getId()
+ {
+ return id;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((id == null) ? 0 : id.hashCode());
+ result = prime * result + ((namespace == null) ? 0 : namespace.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ GlobalEntityCacheKey> other = (GlobalEntityCacheKey>) obj;
+ if (id == null)
+ {
+ if (other.id != null)
+ return false;
+ }
+ else if (!id.equals(other.id))
+ return false;
+ if (namespace == null)
+ {
+ if (other.namespace != null)
+ return false;
+ }
+ else if (!namespace.equals(other.namespace))
+ return false;
+ return true;
+ }
}
}
diff --git a/datagear-management/src/main/java/org/datagear/management/service/impl/AbstractMybatisService.java b/datagear-management/src/main/java/org/datagear/management/service/impl/AbstractMybatisService.java
index 0d346cc0..f455e28d 100644
--- a/datagear-management/src/main/java/org/datagear/management/service/impl/AbstractMybatisService.java
+++ b/datagear-management/src/main/java/org/datagear/management/service/impl/AbstractMybatisService.java
@@ -22,8 +22,6 @@ import org.datagear.persistence.Query;
import org.datagear.util.StringUtil;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;
-import org.springframework.cache.Cache;
-import org.springframework.cache.Cache.ValueWrapper;
/**
* 抽象基于Mybatis的服务类。
@@ -724,38 +722,6 @@ public abstract class AbstractMybatisService extends SqlSessionDaoSupport
return new HashMap<>();
}
- protected ValueWrapper cacheGet(Cache cache, Object key)
- {
- if (cache == null)
- return null;
-
- return cache.get(key);
- }
-
- protected void cachePut(Cache cache, Object key, Object value)
- {
- if (cache == null)
- return;
-
- cache.put(key, value);
- }
-
- protected void cacheEvict(Cache cache, Object key)
- {
- if (cache == null)
- return;
-
- cache.evict(key);
- }
-
- protected void cacheInvalidate(Cache cache)
- {
- if (cache == null)
- return;
-
- cache.invalidate();
- }
-
/**
* 获取sql语句的名字空间。
*
diff --git a/datagear-management/src/main/java/org/datagear/management/service/impl/HtmlTplDashboardWidgetEntityServiceImpl.java b/datagear-management/src/main/java/org/datagear/management/service/impl/HtmlTplDashboardWidgetEntityServiceImpl.java
index b19c7e9e..56e85e95 100644
--- a/datagear-management/src/main/java/org/datagear/management/service/impl/HtmlTplDashboardWidgetEntityServiceImpl.java
+++ b/datagear-management/src/main/java/org/datagear/management/service/impl/HtmlTplDashboardWidgetEntityServiceImpl.java
@@ -7,7 +7,6 @@
package org.datagear.management.service.impl;
-import java.util.List;
import java.util.Map;
import org.apache.ibatis.session.SqlSessionFactory;
@@ -122,22 +121,6 @@ public class HtmlTplDashboardWidgetEntityServiceImpl
return pagingQueryForAnalysisProjectId(user, pagingQuery, dataFilter, analysisProjectId);
}
- @Override
- protected boolean add(HtmlTplDashboardWidgetEntity entity, Map params)
- {
- boolean success = super.add(entity, params);
-
- return success;
- }
-
- @Override
- protected boolean update(HtmlTplDashboardWidgetEntity entity, Map params)
- {
- boolean success = super.update(entity, params);
-
- return success;
- }
-
@Override
protected boolean deleteById(String id, Map params)
{
@@ -152,13 +135,6 @@ public class HtmlTplDashboardWidgetEntityServiceImpl
return deleted;
}
- @Override
- protected void postProcessQuery(List list)
- {
- // XXX 查询操作仅用于展示,不必完全加载
- // super.postProcessSelects(list);
- }
-
@Override
protected void checkInput(HtmlTplDashboardWidgetEntity entity)
{
diff --git a/datagear-management/src/main/java/org/datagear/management/service/impl/ServiceCache.java b/datagear-management/src/main/java/org/datagear/management/service/impl/ServiceCache.java
new file mode 100644
index 00000000..4fb7891e
--- /dev/null
+++ b/datagear-management/src/main/java/org/datagear/management/service/impl/ServiceCache.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2018 datagear.tech
+ *
+ * Licensed under the LGPLv3 license:
+ * http://www.gnu.org/licenses/lgpl-3.0.html
+ */
+
+package org.datagear.management.service.impl;
+
+import org.springframework.cache.Cache;
+import org.springframework.cache.Cache.ValueWrapper;
+
+/**
+ * 服务缓存。
+ *
+ * @author datagear@163.com
+ *
+ */
+public class ServiceCache
+{
+ /** 缓存 */
+ private Cache cache = null;
+
+ /** 缓存是否开启序列化特性 */
+ private boolean serialized = false;
+
+ /** 是否是共享缓存 */
+ private boolean shared = false;
+
+ /** 是否禁用 */
+ private boolean disabled = false;
+
+ public ServiceCache()
+ {
+ super();
+ }
+
+ /**
+ * @param cache
+ * 允许{@code null}
+ */
+ public ServiceCache(Cache cache)
+ {
+ super();
+ this.cache = cache;
+ }
+
+ /**
+ * 获取缓存。
+ *
+ * 使用参考{@linkplain #isEnable()}、{@linkplain #isShared()}。
+ *
+ *
+ * @return 可能为{@code null}。
+ */
+ public Cache getCache()
+ {
+ return cache;
+ }
+
+ public void setCache(Cache cache)
+ {
+ this.cache = cache;
+ }
+
+ /**
+ * 缓存是否开启序列化特性。
+ *
+ * 当为{@code true}时,则不必在存入缓存前、取出缓存后,进行克隆以确保缓存不被改变。
+ *
+ *
+ * @return
+ */
+ public boolean isSerialized()
+ {
+ return serialized;
+ }
+
+ public void setSerialized(boolean serialized)
+ {
+ this.serialized = serialized;
+ }
+
+ /**
+ * 是否共享缓存。
+ *
+ * 当为{@code true}时,使用{@linkplain #getCache()}应生成全局缓存KEY。
+ *
+ *
+ * @return
+ */
+ public boolean isShared()
+ {
+ return shared;
+ }
+
+ public void setShared(boolean shared)
+ {
+ this.shared = shared;
+ }
+
+ public boolean isDisabled()
+ {
+ return disabled;
+ }
+
+ public void setDisabled(boolean disabled)
+ {
+ this.disabled = disabled;
+ }
+
+ /**
+ * 此服务缓存是否是启用的。
+ *
+ * 如果为{@code false},即使{@linkplain #getCache()}不为{@code null},也不应使用。
+ *
+ *
+ * 如果为{@code true},则{@linkplain #getCache()}不会为{@code null}。
+ *
+ *
+ * @return
+ */
+ public boolean isEnable()
+ {
+ if (this.disabled)
+ return false;
+
+ return (this.cache != null);
+ }
+
+ /**
+ * 获取缓存。
+ *
+ * 调用此方法前应确保{@linkplain #isEnable()}为{@code true}。
+ *
+ *
+ * @param key
+ * @return
+ */
+ public ValueWrapper get(Object key)
+ {
+ if (this.cache == null)
+ return null;
+
+ return this.cache.get(key);
+ }
+
+ /**
+ * 存入缓存。
+ *
+ * 调用此方法前应确保{@linkplain #isEnable()}为{@code true}。
+ *
+ *
+ * @param key
+ * @param value
+ */
+ public void put(Object key, Object value)
+ {
+ if (this.cache == null)
+ return;
+
+ this.cache.put(key, value);
+ }
+
+ /**
+ * 删除缓存。
+ *
+ * 调用此方法前应确保{@linkplain #isEnable()}为{@code true}。
+ *
+ *
+ * @param key
+ */
+ public void evictImmediately(Object key)
+ {
+ if (this.cache == null)
+ return;
+
+ this.cache.evictIfPresent(key);
+ }
+
+ /**
+ * 清空缓存。
+ *
+ * 调用此方法前应确保{@linkplain #isEnable()}为{@code true}。
+ *
+ */
+ public void invalidate()
+ {
+ if (this.cache == null)
+ return;
+
+ this.cache.invalidate();
+ }
+}
diff --git a/datagear-web/src/main/java/org/datagear/web/config/CoreConfig.java b/datagear-web/src/main/java/org/datagear/web/config/CoreConfig.java
index a5fe00d4..577904cf 100644
--- a/datagear-web/src/main/java/org/datagear/web/config/CoreConfig.java
+++ b/datagear-web/src/main/java/org/datagear/web/config/CoreConfig.java
@@ -55,6 +55,7 @@ import org.datagear.management.service.AuthorizationService;
import org.datagear.management.service.DataPermissionEntityService;
import org.datagear.management.service.DataSetEntityService;
import org.datagear.management.service.DataSetResDirectoryService;
+import org.datagear.management.service.EntityService;
import org.datagear.management.service.HtmlChartWidgetEntityService;
import org.datagear.management.service.HtmlTplDashboardWidgetEntityService;
import org.datagear.management.service.RoleService;
@@ -73,6 +74,7 @@ import org.datagear.management.service.impl.HtmlTplDashboardWidgetEntityServiceI
import org.datagear.management.service.impl.RoleServiceImpl;
import org.datagear.management.service.impl.RoleUserServiceImpl;
import org.datagear.management.service.impl.SchemaServiceImpl;
+import org.datagear.management.service.impl.ServiceCache;
import org.datagear.management.service.impl.SqlHistoryServiceImpl;
import org.datagear.management.service.impl.UserPasswordEncoder;
import org.datagear.management.service.impl.UserServiceImpl;
@@ -110,7 +112,6 @@ import org.datagear.web.util.XmlDriverEntityManagerInitializer;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.ApplicationContext;
@@ -757,11 +758,11 @@ public class CoreConfig implements ApplicationListener
for (AbstractMybatisEntityService es : entityServices.values())
{
- es.setCache(getEntityCacheBySimpleName(cacheManager, es.getClass()));
+ es.setCache(getServiceCache(cacheManager, es.getClass()));
if (es instanceof AbstractMybatisDataPermissionEntityService, ?>)
((AbstractMybatisDataPermissionEntityService, ?>) es)
- .setPermissionCache(getPermissionCacheBySimpleName(cacheManager, es.getClass()));
+ .setPermissionCache(getPermissionServiceCache(cacheManager, es.getClass()));
}
}
@@ -773,13 +774,27 @@ public class CoreConfig implements ApplicationListener
}
}
- protected Cache getEntityCacheBySimpleName(CacheManager cacheManager, Class> clazz)
+ @SuppressWarnings("rawtypes")
+ protected ServiceCache getServiceCache(CacheManager cacheManager,
+ Class extends EntityService> clazz)
{
- return cacheManager.getCache(clazz.getSimpleName());
+ return getServiceCache(cacheManager, clazz.getSimpleName());
}
- protected Cache getPermissionCacheBySimpleName(CacheManager cacheManager, Class> clazz)
+ @SuppressWarnings("rawtypes")
+ protected ServiceCache getPermissionServiceCache(CacheManager cacheManager,
+ Class extends EntityService> clazz)
{
- return cacheManager.getCache(clazz.getSimpleName() + "Permission");
+ return getServiceCache(cacheManager, clazz.getSimpleName() + "Permission");
+ }
+
+ protected ServiceCache getServiceCache(CacheManager cacheManager, String name)
+ {
+ ServiceCache cache = new ServiceCache(cacheManager.getCache(name));
+
+ cache.setDisabled(environment.getProperty("service.cache.disabled", Boolean.class, false));
+ cache.setDisabled(environment.getProperty("service.cache.serialized", Boolean.class, false));
+
+ return cache;
}
}
diff --git a/datagear-web/src/main/resources/org/datagear/web/application.properties b/datagear-web/src/main/resources/org/datagear/web/application.properties
index 9a9e6a5b..bc6d0d6a 100644
--- a/datagear-web/src/main/resources/org/datagear/web/application.properties
+++ b/datagear-web/src/main/resources/org/datagear/web/application.properties
@@ -71,6 +71,11 @@ datasource.password=
#数据库方言,可选项:derby、mysql、oracle、postgresql、default,留空则表示自动判断
datasourceDialect=
+#服务层缓存是否禁用:true 禁用;false 启用
+service.cache.disabled=false
+#服务层采用的缓存库是否支持序列化:true 支持;false 不支持,当为false时,服务层会克隆缓存对象以确保缓存不被修改
+service.cache.serialized=false
+
#Spring Boot配置
#-----------------------------------------