From a7fa87cf97a36b957c5a02e292826339c6d7af40 Mon Sep 17 00:00:00 2001 From: datagear Date: Tue, 17 Aug 2021 14:39:23 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0ServiceCache=E7=B1=BB?= =?UTF-8?q?=EF=BC=8C=E7=94=A8=E4=BA=8E=E6=8A=BD=E8=B1=A1=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=B1=82=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datagear/analysis/TemplateDashboard.java | 2 + ...actMybatisDataPermissionEntityService.java | 32 ++- .../impl/AbstractMybatisEntityService.java | 199 ++++++++++++------ .../service/impl/AbstractMybatisService.java | 34 --- ...mlTplDashboardWidgetEntityServiceImpl.java | 24 --- .../management/service/impl/ServiceCache.java | 194 +++++++++++++++++ .../org/datagear/web/config/CoreConfig.java | 29 ++- .../org/datagear/web/application.properties | 5 + 8 files changed, 381 insertions(+), 138 deletions(-) create mode 100644 datagear-management/src/main/java/org/datagear/management/service/impl/ServiceCache.java diff --git a/datagear-analysis/src/main/java/org/datagear/analysis/TemplateDashboard.java b/datagear-analysis/src/main/java/org/datagear/analysis/TemplateDashboard.java index b03907f4..9d6089d4 100644 --- a/datagear-analysis/src/main/java/org/datagear/analysis/TemplateDashboard.java +++ b/datagear-analysis/src/main/java/org/datagear/analysis/TemplateDashboard.java @@ -15,6 +15,8 @@ package org.datagear.analysis; */ public class TemplateDashboard extends Dashboard { + private static final long serialVersionUID = 1L; + /** 模板 */ private String template; diff --git a/datagear-management/src/main/java/org/datagear/management/service/impl/AbstractMybatisDataPermissionEntityService.java b/datagear-management/src/main/java/org/datagear/management/service/impl/AbstractMybatisDataPermissionEntityService.java index fd7a6118..98758e0c 100644 --- a/datagear-management/src/main/java/org/datagear/management/service/impl/AbstractMybatisDataPermissionEntityService.java +++ b/datagear-management/src/main/java/org/datagear/management/service/impl/AbstractMybatisDataPermissionEntityService.java @@ -30,7 +30,6 @@ import org.datagear.persistence.PagingQuery; import org.datagear.persistence.Query; import org.datagear.util.StringUtil; import org.mybatis.spring.SqlSessionTemplate; -import org.springframework.cache.Cache; import org.springframework.cache.Cache.ValueWrapper; /** @@ -42,7 +41,7 @@ import org.springframework.cache.Cache.ValueWrapper; public abstract class AbstractMybatisDataPermissionEntityService> extends AbstractMybatisEntityService implements DataPermissionEntityService { - private Cache permissionCache; + private ServiceCache permissionCache; public AbstractMybatisDataPermissionEntityService() { @@ -59,12 +58,12 @@ public abstract class AbstractMybatisDataPermissionEntityService permissions = new HashMap(); - if (this.permissionCache == null) + if (!isPermissionCacheEnabled()) return permissions; String userId = user.getId(); @@ -363,10 +362,10 @@ public abstract class AbstractMybatisDataPermissionEntityService + * 调用此方法前应确保{@linkplain #isPermissionCacheEnabled()}为{@code true}。 + *

* * @param id * @return */ protected Object toPermissionCacheKey(ID id) { - return id; + if (this.permissionCache.isShared()) + return new GlobalEntityCacheKey(getSqlNamespace() + "Permission", id); + else + return id; + } + + protected boolean isPermissionCacheEnabled() + { + return (this.permissionCache != null && this.permissionCache.isEnable()); } /** diff --git a/datagear-management/src/main/java/org/datagear/management/service/impl/AbstractMybatisEntityService.java b/datagear-management/src/main/java/org/datagear/management/service/impl/AbstractMybatisEntityService.java index 9f765311..614ba0b8 100644 --- a/datagear-management/src/main/java/org/datagear/management/service/impl/AbstractMybatisEntityService.java +++ b/datagear-management/src/main/java/org/datagear/management/service/impl/AbstractMybatisEntityService.java @@ -7,6 +7,7 @@ package org.datagear.management.service.impl; +import java.io.Serializable; import java.util.List; import java.util.Map; @@ -20,7 +21,6 @@ import org.datagear.persistence.PagingData; import org.datagear.persistence.PagingQuery; import org.datagear.persistence.Query; import org.mybatis.spring.SqlSessionTemplate; -import org.springframework.cache.Cache; import org.springframework.cache.Cache.ValueWrapper; import org.springframework.cache.support.SimpleValueWrapper; @@ -33,7 +33,7 @@ import org.springframework.cache.support.SimpleValueWrapper; public abstract class AbstractMybatisEntityService> extends AbstractMybatisService implements EntityService { - private Cache cache = null; + private ServiceCache cache = null; /** * 查询操作时缓存实体数目。 @@ -61,12 +61,12 @@ public abstract class AbstractMybatisEntityService> ext super(sqlSessionTemplate, dialect); } - public Cache getCache() + public ServiceCache getCache() { return cache; } - public void setCache(Cache cache) + public void setCache(ServiceCache cache) { this.cache = cache; } @@ -230,54 +230,6 @@ public abstract class AbstractMybatisEntityService> 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 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 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配置 #-----------------------------------------