我需要在我的Java代码中使用缓存的地图。地图从DB加载,需要定期从DB重新加载(对于地图中的所有数据)。因为我们现在无法导入任何新包。谷歌的guava包或代码示例中是否有任何现有功能?
如果将地图实现为线程安全,那就更好了。但如果它不够简单,它仍然可以。
“LoadingCache”是我喜欢的,但它没有数据初始化方法让我在开始时将数据放入地图。并且它需要在地图到期后每次“获取”时到达数据库。
谢谢!
一些示例代码可能会有所帮助:
public interface AToBMapper
{
public static final String DEFAULT_B_NAME = "DEFAULT";
public String getBForA(final String a);
}
public class AToBMapperImpl implements AToBMapper
{
private final SomeDAO dao;
private Map<String, String> cachedMap;
public AToBMapperImpl(final SomeDAO dao)
{
this.dao = dao;
cachedMap = new HashMap<String, String>();
}
public String getBForA(final String a)
{
// if the map is not initialized, initialize it with the data
// if the map is expired, refresh all the data in the map
// return the mapped B for A (if there is no mapping for A, return the "DEFAULT")
}
private Map<String, String> getTheData(final List<String> listOfB)
{
Map<String, String> newData = dao.getAToBMapping(listOfB);
}
}
答案 0 :(得分:1)
“LoadingCache”是我喜欢的,但它没有数据 初始化方法让我把数据放到地图上 开始。
当然,Cache
扩展了LoadingCache
接口的putAll(Map<K, V>)
方法。方法将指定映射中的所有映射复制到缓存。
您也可以使用类似的put(K, V)
方法。
修改强>
根据您的评论,我可以告诉您根本不需要LoadingCache
,而是自己保持所有条目的到期。以下是您可以使用的简单示例(仅限JDK和Guava的类):
public class AToBMapperImpl implements AToBMapper {
public static final long EXPIRE_TIME_IN_SECONDS =
TimeUnit.SECONDS.convert(1, TimeUnit.HOURS); // or whatever
private final SomeDAO dao;
private final ConcurrentMap<String, String> cache;
private final Stopwatch stopwatch;
public AToBMapperImpl(SomeDAO dao) {
this.dao = dao;
stopwatch = new Stopwatch();
cache = new MapMaker().concurrencyLevel(2).makeMap();
}
@Override
public synchronized String getBForA(final String a) {
// if the map is not initialized, initialize it with the data
if (!stopwatch.isRunning()) {
cache.putAll(getNewCacheContents());
stopwatch.start();
}
// if the map is expired, refresh all the data in the map
if (stopwatch.elapsedTime(TimeUnit.SECONDS) >= EXPIRE_TIME_IN_SECONDS) {
cache.clear();
cache.putAll(getNewCacheContents());
stopwatch.reset();
}
// return the mapped String for A
// (if there is no mapping for A, return the "DEFAULT")
return cache.containsKey(a) ? cache.get(a) : new String(DEFAULT_B_NAME);
}
private Map<String, String> getNewCacheContents() {
return getTheData(Arrays.asList("keys", "you", "want", "to", "load"));
}
private Map<String, String> getTheData(List<String> listOfB) {
return dao.getAToBMapping(listOfB);
}
}
答案 1 :(得分:1)
虽然@Xaerxess提供了一个可行的解决方案,但我觉得你仍然可以通过简单地使用具有单个键值的LoadingCache
实例来实现这一目标。我确实理解你对场景的推理,因为我已经处理过单个对象加载可能和整个集合一样昂贵的项目。
public class AToBMapperImpl implements AToBMapper {
private final LoadingCache<Boolean, Map<String, String>> cachedMap;
public AToBMapperImpl(final SomeDAO dao, final List<String> listOfB) {
this.cachedMap = CacheBuilder.newBuilder()
.expireAfterAccess(4, TimeUnit.HOURS)
.build(new CacheLoader<Boolean, Map<String, String>>() {
@Override
public Map<String, String> load(Boolean k) throws Exception {
return dao.getAToBMapping(listOfB);
}
});
}
@Override
public String getBForA(String a) {
try {
Map<String, String> map = cachedMap.get(Boolean.TRUE);
return map.containsKey(a) ? map.get(a) : DEFAULT_B_NAME;
} catch (ExecutionException ex) {
// Do what you need to do on exception.
throw new RuntimeException(ex.getMessage(), ex);
}
}
}
这样,缓存就绑定到一个条目,其值是您所追求的地图。您将获得到期时间的好处。如果您发现listOfB
可能会随着时间的推移而发生变化,则可能需要编写另一种服务方法来检索缓存加载程序随后可以调用的方法。
答案 2 :(得分:-1)
您不需要数据初始化方法,它可以在第一次访问时初始化。 以下是如何执行此操作的示例代码
cachedWhatever =
cacheBuilder
.maximumSize(5)
.expireAfterAccess(30, TimeUnit.MINUTES)
.removalListener(new RemovalListener<String, CachedWhatever>() {
@Override
public void onRemoval(RemovalNotification<String, CachedMetadata> notification) {
logger.info("removed");
}
})
.build(new CacheLoader<String, CachedWhatever>(){
@Override
public CachedMetadata load(String databaseName) throws Exception {
logger.info("created");
return new CachedWhatever();
}
});
并在使用
访问缓存后cachedWhatever.get([key]);
如果不存在,它将创建缓存
并使用cachedWhatever.invalidate([key]);
将清除密钥的缓存