添加ServiceCache类,用于抽象服务层缓存

This commit is contained in:
datagear 2021-08-17 14:39:23 +08:00
parent 8b6d9fe827
commit a7fa87cf97
8 changed files with 381 additions and 138 deletions

View File

@ -15,6 +15,8 @@ package org.datagear.analysis;
*/
public class TemplateDashboard extends Dashboard
{
private static final long serialVersionUID = 1L;
/** 模板 */
private String template;

View File

@ -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<ID, T extends DataPermissionEntity<ID>>
extends AbstractMybatisEntityService<ID, T> implements DataPermissionEntityService<ID, T>
{
private Cache permissionCache;
private ServiceCache permissionCache;
public AbstractMybatisDataPermissionEntityService()
{
@ -59,12 +58,12 @@ public abstract class AbstractMybatisDataPermissionEntityService<ID, T extends D
super(sqlSessionTemplate, dialect);
}
public Cache getPermissionCache()
public ServiceCache getPermissionCache()
{
return permissionCache;
}
public void setPermissionCache(Cache permissionCache)
public void setPermissionCache(ServiceCache permissionCache)
{
this.permissionCache = permissionCache;
}
@ -300,7 +299,7 @@ public abstract class AbstractMybatisDataPermissionEntityService<ID, T extends D
{
Map<ID, Integer> permissions = new HashMap<ID, Integer>();
if (this.permissionCache == null)
if (!isPermissionCacheEnabled())
return permissions;
String userId = user.getId();
@ -363,10 +362,10 @@ public abstract class AbstractMybatisDataPermissionEntityService<ID, T extends D
protected Integer cacheGetPermission(ID id, String userId)
{
if (this.permissionCache == null)
if (!isPermissionCacheEnabled())
return null;
ValueWrapper valueWrapper = cacheGet(this.permissionCache, toPermissionCacheKey(id));
ValueWrapper valueWrapper = this.permissionCache.get(toPermissionCacheKey(id));
UserIdPermissionMap upm = (valueWrapper == null ? null : (UserIdPermissionMap) valueWrapper.get());
return (upm == null ? null : upm.getPermission(userId));
@ -374,17 +373,17 @@ public abstract class AbstractMybatisDataPermissionEntityService<ID, T extends D
protected void cachePutPermission(ID id, String userId, int permission)
{
if (this.permissionCache == null)
if (!isPermissionCacheEnabled())
return;
Object key = toPermissionCacheKey(id);
ValueWrapper valueWrapper = cacheGet(this.permissionCache, key);
ValueWrapper valueWrapper = this.permissionCache.get(key);
UserIdPermissionMap upm = (valueWrapper == null ? null : (UserIdPermissionMap) valueWrapper.get());
if (upm == null)
{
upm = new UserIdPermissionMap();
cachePut(this.permissionCache, key, upm);
this.permissionCache.put(key, upm);
}
upm.putPermission(userId, permission);
@ -392,13 +391,24 @@ public abstract class AbstractMybatisDataPermissionEntityService<ID, T extends D
/**
* 获取指定实体ID的权限缓存关键字
* <p>
* 调用此方法前应确保{@linkplain #isPermissionCacheEnabled()}{@code true}
* </p>
*
* @param id
* @return
*/
protected Object toPermissionCacheKey(ID id)
{
return id;
if (this.permissionCache.isShared())
return new GlobalEntityCacheKey<ID>(getSqlNamespace() + "Permission", id);
else
return id;
}
protected boolean isPermissionCacheEnabled()
{
return (this.permissionCache != null && this.permissionCache.isEnable());
}
/**

View File

@ -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<ID, T extends Entity<ID>> extends AbstractMybatisService<T>
implements EntityService<ID, T>
{
private Cache cache = null;
private ServiceCache cache = null;
/**
* 查询操作时缓存实体数目
@ -61,12 +61,12 @@ public abstract class AbstractMybatisEntityService<ID, T extends Entity<ID>> 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<ID, T extends Entity<ID>> ext
return list;
}
/**
* 是否开启缓存
* <p>
* 子类应注意是否需要重写{@linkplain #cacheCloneEntity(Entity)}方法
* </p>
*
* @return
*/
protected boolean cacheEnabled()
{
return (getCache() != null);
}
/**
* 克隆缓存实体
* <p>
* 参考{@linkplain #cacheGet(Object)}{@linkplain #cachePut(Object, Entity)}{@linkplain #cachePutQueryResult(List)}
* </p>
* <p>
* 对于无序列化缓存比如进程内缓存应遵循{@linkplain CloneableEntity#clone()}规则对于序列化缓存则可直接返回原实体
* </p>
* <p>
* 此方法默认是现是如果{@code value}{@linkplain CloneableEntity}则返回{@linkplain CloneableEntity#clone()}否则返回原对象
* </p>
*
* @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;
}
/**
* 从缓存中读取实体
* <p>
@ -289,10 +241,10 @@ public abstract class AbstractMybatisEntityService<ID, T extends Entity<ID>> 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<ID, T extends Entity<ID>> 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<ID, T extends Entity<ID>> ext
*/
protected void cachePutQueryResult(List<T> values)
{
if (!cacheEnabled())
if (!isCacheEnabled())
return;
int count = Math.min(values.size(), this.getCacheCountForQuery());
@ -348,24 +300,147 @@ public abstract class AbstractMybatisEntityService<ID, T extends Entity<ID>> 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());
}
/**
* 克隆缓存实体
* <p>
* 参考{@linkplain #cacheGet(Object)}{@linkplain #cachePut(Object, Entity)}{@linkplain #cachePutQueryResult(List)}
* </p>
* <p>
* 如果{@linkplain #getCache()}{@linkplain ServiceCache#isSerialized()}{@code false}比如进程内缓存应遵循{@linkplain CloneableEntity#clone()}规则
* 否则可直接返回原实体
* </p>
* <p>
* 此方法默认是现是当需要克隆时如果{@code value}{@linkplain CloneableEntity}则返回{@linkplain CloneableEntity#clone()}否则返回原对象
* </p>
* <p>
* 调用此方法前应确保{@linkplain #isCacheEnabled()}{@code true}
* </p>
*
* @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的缓存关键字
* <p>
* 调用此方法前应确保{@linkplain #isCacheEnabled()}{@code true}
* </p>
*
* @param id
* @return
*/
protected Object toCacheKey(ID id)
{
if (this.cache.isShared())
return new GlobalEntityCacheKey<ID>(getSqlNamespace(), id);
else
return id;
}
/**
* 全局实体缓存KEY
*
* @author datagear@163.com
*
* @param <ID>
*/
protected static class GlobalEntityCacheKey<ID> 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;
}
}
}

View File

@ -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<T> 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语句的名字空间
*

View File

@ -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<String, Object> params)
{
boolean success = super.add(entity, params);
return success;
}
@Override
protected boolean update(HtmlTplDashboardWidgetEntity entity, Map<String, Object> params)
{
boolean success = super.update(entity, params);
return success;
}
@Override
protected boolean deleteById(String id, Map<String, Object> params)
{
@ -152,13 +135,6 @@ public class HtmlTplDashboardWidgetEntityServiceImpl
return deleted;
}
@Override
protected void postProcessQuery(List<HtmlTplDashboardWidgetEntity> list)
{
// XXX 查询操作仅用于展示不必完全加载
// super.postProcessSelects(list);
}
@Override
protected void checkInput(HtmlTplDashboardWidgetEntity entity)
{

View File

@ -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;
}
/**
* 获取缓存
* <p>
* 使用参考{@linkplain #isEnable()}{@linkplain #isShared()}
* </p>
*
* @return 可能为{@code null}
*/
public Cache getCache()
{
return cache;
}
public void setCache(Cache cache)
{
this.cache = cache;
}
/**
* 缓存是否开启序列化特性
* <p>
* 当为{@code true}则不必在存入缓存前取出缓存后进行克隆以确保缓存不被改变
* </p>
*
* @return
*/
public boolean isSerialized()
{
return serialized;
}
public void setSerialized(boolean serialized)
{
this.serialized = serialized;
}
/**
* 是否共享缓存
* <p>
* 当为{@code true}使用{@linkplain #getCache()}应生成全局缓存KEY
* </p>
*
* @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;
}
/**
* 此服务缓存是否是启用的
* <p>
* 如果为{@code false}即使{@linkplain #getCache()}不为{@code null}也不应使用
* </p>
* <p>
* 如果为{@code true}{@linkplain #getCache()}不会为{@code null}
* </p>
*
* @return
*/
public boolean isEnable()
{
if (this.disabled)
return false;
return (this.cache != null);
}
/**
* 获取缓存
* <p>
* 调用此方法前应确保{@linkplain #isEnable()}{@code true}
* </p>
*
* @param key
* @return
*/
public ValueWrapper get(Object key)
{
if (this.cache == null)
return null;
return this.cache.get(key);
}
/**
* 存入缓存
* <p>
* 调用此方法前应确保{@linkplain #isEnable()}{@code true}
* </p>
*
* @param key
* @param value
*/
public void put(Object key, Object value)
{
if (this.cache == null)
return;
this.cache.put(key, value);
}
/**
* 删除缓存
* <p>
* 调用此方法前应确保{@linkplain #isEnable()}{@code true}
* </p>
*
* @param key
*/
public void evictImmediately(Object key)
{
if (this.cache == null)
return;
this.cache.evictIfPresent(key);
}
/**
* 清空缓存
* <p>
* 调用此方法前应确保{@linkplain #isEnable()}{@code true}
* </p>
*/
public void invalidate()
{
if (this.cache == null)
return;
this.cache.invalidate();
}
}

View File

@ -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<ContextRefreshedEvent>
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<ContextRefreshedEvent>
}
}
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;
}
}

View File

@ -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配置
#-----------------------------------------