All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gu.sql2java.redis.cache.RedisCache Maven / Gradle / Ivy

The newest version!
package gu.sql2java.redis.cache;

import gu.simplemq.Channel;
import gu.simplemq.SimpleLog;
import gu.simplemq.redis.JedisPoolLazy;
import gu.simplemq.redis.RedisFactory;
import gu.simplemq.redis.RedisTable;
import gu.sql2java.BaseBean;
import gu.sql2java.IndexMetaData;
import gu.sql2java.RowMetaData;
import gu.sql2java.TableListener.Adapter;
import gu.sql2java.TableManager;
import gu.sql2java.exception.RuntimeDaoException;
import gu.sql2java.manager.Managers;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import static gu.sql2java.redis.cache.RedisCaches.getCacheKeyPrefix;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.primitives.Ints;


/**
 * 基于REDIS的数据库表记录缓存
* 实现与数据库内容实时同步 * @author guyadong * * @param */ public class RedisCache extends Adapter{ private Timer timer; protected final RedisTable table; protected final Channel channel; protected final RowMetaData metaData; /** * Java Bean到JSON转换器实例 */ private Function jsonFormatter; private int[] jsonFields = new int[0]; private boolean include = false; private Set columns= Sets.newHashSet(); private String keyName; /** * 构造方法 * @param keyPrefix KEY的统一前缀,为{@code null}或空使用默认值{@link RedisCaches#getCacheKeyPrefix() } * @param beanClass 数据库记录对象 * @param columnName 用作REDIS key的字段名,为{@code null}或空默认使用主键作为REDIS key的字段名 */ RedisCache(String keyPrefix,Class beanClass, String columnName) { if(isNullOrEmpty(keyPrefix)){ keyPrefix = getCacheKeyPrefix(); } metaData = RowMetaData.getMetaData(beanClass); if(isNullOrEmpty(columnName)){ checkArgument(1 == metaData.primaryKeyCount,"privary key must only one"); keyName = metaData.primaryKeyNames[0]; }else if(Iterables.tryFind(Arrays.asList(metaData.primaryKeyNames[0]),columnName::equals).isPresent()){ /** columnName为主键字段名时,只能有一个主键 */ checkArgument(1 == metaData.primaryKeyCount,"privary key must only one"); keyName = columnName; }else { Optional opt = Iterables.tryFind(metaData.getUniqueIndices().values(),m->{ return m.readableName.equals(columnName) || m.name.equals(columnName) || Iterables.tryFind(m.columns,columnName::equals).isPresent(); }); checkArgument(opt.isPresent() && 1 == opt.get().columns.size() , "INVALID columnName %s,only one column and UNIQUE index required",columnName); keyName = opt.get().columns.get(0); } String channelName = RedisCaches.channelNameOf(keyPrefix,beanClass.getSimpleName(), keyName); channel = new Channel<>(channelName, JSONObject.class); table = RedisFactory.getTable(channel, JedisPoolLazy.getDefaultInstance()); table.setKeyHelper(b->String.valueOf(b.get(keyName))); jsonFormatter = this::formatAsJson; } /** * 构造方法 * @param beanClass 数据库记录对象 * @param columnName 用作REDIS key的字段名,为{@code null}或空默认使用主键作为REDIS key的字段名 */ RedisCache(Class beanClass, String columnName) { this(null,beanClass,columnName); } private JSONObject asJson(V bean){ return jsonFormatter.apply(bean); } /** * 设置Java Bean到JSON转换器实例,不指定使用默认转换所有字段的实例 * @param jsonFormater * @return 转换的JSON对象 */ public RedisCache setJsonFormater(Function jsonFormater) { if(null != jsonFormater){ this.jsonFormatter = jsonFormater; } return this; } /** * 将所有数据库加载到REDIS * @param reg 是否注册侦听器 * @return 当前对象 */ private void loadAllIntoCache(boolean reg){ TableManager manager = Managers.managerOf(metaData.tablename); SimpleLog.log("load all rows of {} INTO {} CACHE",metaData.tablename,keyName); manager.loadAll(bean->{ table.set(asJson(bean), false); }); if(reg) { manager.registerListener(this); } } /** * 启动缓存
* 将所有数据库加载到REDIS,并注册侦听器 * @return 当前对象 */ public RedisCache start(){ int count = table.clear(); /** 先删除REDIS上已经有记录,再重新从数据库加载所有记录 */ SimpleLog.log("clean {} CACHE {} rows",keyName,count); loadAllIntoCache(true); return this; } @Override public final void afterInsert(V bean) throws RuntimeDaoException { table.set(asJson(bean), false); } @Override public final void afterUpdate(V bean) throws RuntimeDaoException { table.set(asJson(bean), false); } @Override public final void afterDelete(V bean) throws RuntimeDaoException { table.removeValues(asJson(bean)); } /** * 配置数据库对象转换为JSON时类型为JSON的字段ID * @param jsonFields JSON字段,为{@code null}忽略 * @return 当前对象 */ public RedisCache jsonFields(int... jsonFields) { if(null != jsonFields){ this.jsonFields = jsonFields; } return this; } /** * 配置数据库对象转换为JSON时类型为JSON的字段名列表 * @param jsonFields JSON字段,为{@code null}忽略 * @return 当前对象 */ public RedisCache jsonFields(String... jsonFields) { if(null != jsonFields){ List ids = Lists.transform(Arrays.asList(jsonFields),metaData::columnIDOf); ids = Lists.newArrayList(Iterables.filter(ids, id->id>=0)); int[] array = Ints.toArray(ids); this.jsonFields = array; } return this; } /** * 配置数据库对象转换为JSON时类型为JSON的字段名列表 * @param jsonFields JSON字段,为{@code null}忽略 * @return 当前对象 */ public RedisCache jsonFields(IterablejsonFields) { if(null != jsonFields){ Iterable ids = Iterables.transform(jsonFields,metaData::columnIDOf); ArrayList list = Lists.newArrayList(Iterables.filter(ids, id->id>=0)); int[] array = Ints.toArray(list); this.jsonFields = array; } return this; } /** * 配置数据库对象转换为JSON时的输出字段 * @param include 为{@code true}时{@code columns}为需要输出的字段白名单, * 只有在名单中的字段才会被输出,否则为输出字段黑名单,在名单中的字段不会被输出 * @param columns 白名单/黑名单字段名列表 * @return 当前对象 */ public RedisCache columns(boolean include,Iterable columns) { this.include = include; if(null != columns){ this.columns = Sets.newHashSet(Iterables.filter(columns,s->!isNullOrEmpty(s))); if(include){ this.columns.addAll(Arrays.asList(metaData.primaryKeyNames)); } } return this; } /** * 配置数据库对象转换为JSON时的输出字段 * @param include 为{@code true}时{@code columns}为需要输出的字段白名单, * 只有在名单中的字段才会被输出,否则为输出字段黑名单,在名单中的字段不会被输出 * @param columns 白名单/黑名单字段名列表 * @return 当前对象 */ public RedisCache columns(boolean include,String ... columns) { return columns(include,null == columns ? Collections.emptyList():Arrays.asList(columns)); } /** * 开启主动更新缓存机制
* 定期执行{@link #loadAllIntoCache(boolean)} 更新缓存数据 * @param period 更新周期 * @param timeUnit 时间单位 * @return 当前对象 * @since 3.20.1 */ public synchronized RedisCache updatePeriodically(long period,TimeUnit timeUnit){ long periodMills = TimeUnit.MILLISECONDS.convert(period, checkNotNull(timeUnit,"timeUnit is null")); if(null == timer) { timer = new Timer("timer-update-cache-"+metaData.tablename,true); }else { timer.cancel(); } timer.schedule(new TimerTask() { @Override public void run() { loadAllIntoCache(false); }}, periodMills,periodMills); return this; } /** * 格式化一个数据库记录
* 如果指定了的JSON字段,则String类型JSON字段解析成JSON对象 * * @param bean 数据库记录对象 * @param jsonFields JSON字段,为{@code null}忽略 * @return JSONObject对象 */ private JSONObject formatAsJson(V bean){ JSONObject m; if(null != bean){ m = new JSONObject(bean.asNameValueMap(true,include,columns)); if(null != jsonFields){ Ints.asList(jsonFields).forEach( f ->{ String c = metaData.columnNameOf(f); if(null != c){ if((include ? columns.contains(c) : !columns.contains(c))){ m.put(c, BaseBeanSupport.asJson(bean, f)); } } }); } }else { m = new JSONObject(); } return m; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy