5

我需要在我的 Java 代码中缓存地图。地图是从数据库加载的,需要定期从数据库重新加载(对于地图中的所有数据)。由于我们现在无法导入任何新包。谷歌的番石榴包或代码示例中是否有任何现有功能?

最好将映射实现为线程安全的。但如果它不够简单,它仍然可以。

“LoadingCache”是我喜欢的一种,但它没有数据初始化方法让我在开始时将数据放入地图中。并且在地图过期后每次“get”都需要到达数据库。

谢谢!


一些示例代码可能会有所帮助:

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);
    }
}
4

3 回答 3

1

“LoadingCache”是我喜欢的一种,但它没有数据初始化方法让我在开始时将数据放入地图中。

当然它确实有这样的方法——putAll(Map<K, V>)Cache接口LoadingCache扩展。该方法将所有映射从指定映射复制到缓存

您也可以使用类似的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);
  }
}
于 2013-03-12T09:58:08.823 回答
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可能会随着时间而改变,您可能需要编写另一个服务方法来检索缓存加载器然后可以调用的方法。

于 2013-10-07T05:24:50.817 回答
-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]);将清除密钥的缓存

于 2013-03-12T10:15:12.680 回答