From 58d08c5787c503b699fa22671ad65841a2312fe6 Mon Sep 17 00:00:00 2001 From: Redkale <22250530@qq.com> Date: Wed, 22 Feb 2017 20:57:13 +0800 Subject: [PATCH] =?UTF-8?q?@Cacheable=E5=A2=9E=E5=8A=A0=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=BC=93=E5=AD=98=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/javax/persistence/Cacheable.java | 21 ++++++--- .../source/DistributeTableStrategy.java | 1 + src/org/redkale/source/EntityCache.java | 45 +++++++++++++++---- src/org/redkale/source/EntityInfo.java | 2 +- .../redkale/test/source/CacheTestBean.java | 12 ++--- 5 files changed, 60 insertions(+), 21 deletions(-) diff --git a/src/javax/persistence/Cacheable.java b/src/javax/persistence/Cacheable.java index 9cbd2a749..b52d17ed4 100644 --- a/src/javax/persistence/Cacheable.java +++ b/src/javax/persistence/Cacheable.java @@ -1,4 +1,4 @@ -/******************************************************************************* +/** ***************************************************************************** * Copyright (c) 2008 - 2013 Oracle Corporation. All rights reserved. * * This program and the accompanying materials are made available under the @@ -12,7 +12,7 @@ * Linda DeMichiel - Java Persistence 2.1 * Linda DeMichiel - Java Persistence 2.0 * - ******************************************************************************/ + ***************************************************************************** */ package javax.persistence; import static java.lang.annotation.ElementType.TYPE; @@ -27,19 +27,28 @@ import java.lang.annotation.Target; * The value of the Cacheable annotation is inherited by * subclasses; it can be overridden by specifying * Cacheable on a subclass. - * - *

Cacheable(false) means that the entity and its state must + * + *

+ * Cacheable(false) means that the entity and its state must * not be cached by the provider. - * + * * @since Java Persistence 2.0 */ -@Target( { TYPE }) +@Target({TYPE}) @Retention(RUNTIME) public @interface Cacheable { /** * (Optional) Whether or not the entity should be cached. + * * @return boolean */ boolean value() default true; + + /** + * (Optional) 定时自动更新缓存的周期秒数,为0表示不做定时更新, 大于0表示每经过interval秒后会自动从数据库中拉取数据更新Cache + * + * @return + */ + int interval() default 0; } diff --git a/src/org/redkale/source/DistributeTableStrategy.java b/src/org/redkale/source/DistributeTableStrategy.java index fa23546b2..4d83937a9 100644 --- a/src/org/redkale/source/DistributeTableStrategy.java +++ b/src/org/redkale/source/DistributeTableStrategy.java @@ -8,6 +8,7 @@ package org.redkale.source; import java.io.Serializable; /** + * 不能与Cacheable同时使用 * *

* 详情见: https://redkale.org diff --git a/src/org/redkale/source/EntityCache.java b/src/org/redkale/source/EntityCache.java index a85b1e77f..f34e82ac8 100644 --- a/src/org/redkale/source/EntityCache.java +++ b/src/org/redkale/source/EntityCache.java @@ -29,10 +29,10 @@ public final class EntityCache { private static final Logger logger = Logger.getLogger(EntityCache.class.getName()); - private final ConcurrentHashMap map = new ConcurrentHashMap(); + private ConcurrentHashMap map = new ConcurrentHashMap(); // CopyOnWriteArrayList 插入慢、查询快; 10w数据插入需要3.2秒; ConcurrentLinkedQueue 插入快、查询慢;10w数据查询需要 0.062秒, 查询慢40%; - private final Collection list = new ConcurrentLinkedQueue(); + private Collection list = new ConcurrentLinkedQueue(); private final Map> sortComparators = new ConcurrentHashMap<>(); @@ -52,8 +52,13 @@ public final class EntityCache { final EntityInfo info; - public EntityCache(final EntityInfo info) { + final int interval; + + private ScheduledThreadPoolExecutor scheduler; + + public EntityCache(final EntityInfo info, final Cacheable c) { this.info = info; + this.interval = c == null ? 0 : c.interval(); this.type = info.getType(); this.creator = info.getCreator(); this.primary = info.primary; @@ -80,15 +85,35 @@ public final class EntityCache { public void fullLoad() { if (info.fullloader == null) return; - clear(); + this.fullloaded = false; + ConcurrentHashMap newmap = new ConcurrentHashMap(); List all = info.fullloader.apply(info.source, type); if (all != null) { all.stream().filter(x -> x != null).forEach(x -> { - this.map.put(this.primary.get(x), x); + newmap.put(this.primary.get(x), x); }); - this.list.addAll(all); } + this.list = all == null ? new ConcurrentLinkedQueue() : new ConcurrentLinkedQueue(all); + this.map = newmap; this.fullloaded = true; + if (this.interval > 0) { + this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> { + final Thread t = new Thread(r, "EntityCache-" + type + "-Thread"); + t.setDaemon(true); + return t; + }); + this.scheduler.scheduleAtFixedRate(() -> { + ConcurrentHashMap newmap2 = new ConcurrentHashMap(); + List all2 = info.fullloader.apply(info.source, type); + if (all2 != null) { + all2.stream().filter(x -> x != null).forEach(x -> { + newmap2.put(this.primary.get(x), x); + }); + } + this.list = all2 == null ? new ConcurrentLinkedQueue() : new ConcurrentLinkedQueue(all2); + this.map = newmap2; + }, interval - System.currentTimeMillis() / 1000 % interval, interval, TimeUnit.SECONDS); + } } public Class getType() { @@ -97,8 +122,12 @@ public final class EntityCache { public void clear() { this.fullloaded = false; - this.list.clear(); - this.map.clear(); + this.list = new ConcurrentLinkedQueue(); + this.map = new ConcurrentHashMap(); + if (this.scheduler != null) { + this.scheduler.shutdownNow(); + this.scheduler = null; + } } public boolean isFullLoaded() { diff --git a/src/org/redkale/source/EntityInfo.java b/src/org/redkale/source/EntityInfo.java index c7c007b3a..96ec02dfc 100644 --- a/src/org/redkale/source/EntityInfo.java +++ b/src/org/redkale/source/EntityInfo.java @@ -249,7 +249,7 @@ public final class EntityInfo { //----------------cache-------------- Cacheable c = type.getAnnotation(Cacheable.class); if (this.table == null || (!cacheForbidden && c != null && c.value())) { - this.cache = new EntityCache<>(this); + this.cache = new EntityCache<>(this, c); } else { this.cache = null; } diff --git a/test/org/redkale/test/source/CacheTestBean.java b/test/org/redkale/test/source/CacheTestBean.java index 142510d44..f32c09ab2 100644 --- a/test/org/redkale/test/source/CacheTestBean.java +++ b/test/org/redkale/test/source/CacheTestBean.java @@ -38,9 +38,9 @@ public class CacheTestBean { BiFunction fullloader = (s, z) -> list; Method method = EntityInfo.class.getDeclaredMethod("load", Class.class, int.class, boolean.class, Properties.class, DataSource.class, BiFunction.class); - method.setAccessible(true); + method.setAccessible(true); final EntityInfo info = (EntityInfo) method.invoke(null, CacheTestBean.class, 0, true, new Properties(), null, fullloader); - EntityCache cache = new EntityCache(info); + EntityCache cache = new EntityCache(info, null); cache.fullLoad(); System.out.println(cache.queryColumnMap("pkgid", FilterFunc.COUNT, "name", null)); @@ -49,9 +49,9 @@ public class CacheTestBean { System.out.println(cache.queryColumnMap("pkgid", FilterFunc.SUM, "price", null)); System.out.println(cache.queryColumnMap("pkgid", FilterFunc.MAX, "price", null)); System.out.println(cache.queryColumnMap("pkgid", FilterFunc.MIN, "price", null)); - - System.out.println(cache.find(null, FilterNode.create("name", FilterExpress.EQUAL, "BB"))); - System.out.println(cache.find(null, FilterNode.create("name", FilterExpress.IGNORECASEEQUAL, "BB"))); + + System.out.println(cache.find(null, FilterNode.create("name", FilterExpress.EQUAL, "BB"))); + System.out.println(cache.find(null, FilterNode.create("name", FilterExpress.IGNORECASEEQUAL, "BB"))); System.out.println(cache.querySheet(null, null, FilterNode.create("name", FilterExpress.IGNORECASENOTLIKE, "B"))); } @@ -90,7 +90,7 @@ public class CacheTestBean { @Override public String toString() { - return JsonConvert.root().convertTo(this); + return JsonConvert.root().convertTo(this); } }