番石榴缓存中的重复密钥

时间:2019-03-14 20:37:29

标签: java spring spring-boot guava

我已经在我的SpringBoot服务之一中实现了这个Guava缓存:

public class CachedMenuServiceImpl implements MenuService {

        private  LoadingCache<String, MenuChart2> menuChartCache = CacheBuilder.newBuilder()
                .maximumSize(15)
                .expireAfterAccess(15, TimeUnit.MINUTES)
                .build(
                        new CacheLoader<String, MenuChart2>() {
                            public MenuChart2 load(String menuSymbol) throws ExecutionException {
                                   return generateCharts (menuSymbol);    
                            }
                        }
                );



    @Override
    public MenuChart2 getCharts (String menuSymbol) throws ExecutionException {

        return menuChartCache.get(menuSymbol);      
    }


public MenuChart2 generateCharts (String MenuSymbol) throws ExecutionException {

        MenuChart2 menuChart2;

       …
       return menuChart2;
}


public MenuChart2 generateCharts (String menuSymbol) throws ExecutionException {


        if (LOG.isDebugEnabled()) {
            LOG.debug ("generating charts for {} ", menuSymbol);
        }

        Menu menu = findBySymbol(menuSymbol).get();

        Map<LocalDate, MenuChart2.Statistics> last30DPerDay =   

                menuPriceByDayService.findAllOrderByUpdateDate(menu, DateUtils.monthlyDate(), 30)
                .stream()
                .sorted(comparing(MenuPriceByDay::getUpdateDate))
                .collect(Collectors
                        .toMap(MenuPriceByDay::getUpdateLocalDate, p -> new MenuChart2().new Statistics( p.getMinPrice().doubleValue(), 
                                                                                                                            p.getMaxPrice().doubleValue(),
                                                                                                                            p.getAvgPrice().doubleValue())));

            Map<LocalDate, MenuChart2.Statistics> last3MPerDay =    

                    menuPriceByDayService.findAllOrderByUpdateDate(menu, DateUtils.quarterlyDate(), 92)
                    .stream()
                    .sorted(comparing(MenuPriceByDay::getUpdateDate))
                    .collect(Collectors
                            .toMap(MenuPriceByDay::getUpdateLocalDate, p -> new MenuChart2().new Statistics( p.getMinPrice().doubleValue(), 
                                                                                                                                p.getMaxPrice().doubleValue(),
                                                                                                                                p.getAvg;

            Map<LocalDate, MenuChart2.Statistics> last6MPerDay =    

                    menuPriceByDayService.findAllOrderByUpdateDate(menu, DateUtils.semestralDate(), 26)
                    .stream()
                    .sorted(comparing(MenuPriceByDay::getUpdateDate))
                    .collect(Collectors
                            .toMap(MenuPriceByDay::getUpdateLocalDate, p -> new MenuChart2().new Statistics( p.getMinPrice().doubleValue(), 
                                                                                                                                p.getMaxPrice().doubleValue(),
                                                                                                                                p.getAvgPrice().doubleValue())));

            Map<LocalDate, MenuChart2.Statistics> last1YPerDay =    

                    menuPriceByDayService.findAllOrderByUpdateDate(menu, DateUtils.yearlylDate(), 52)
                    .stream()
                    .sorted(comparing(MenuPriceByDay::getUpdateDate))
                    .collect(Collectors
                            .toMap(MenuPriceByDay::getUpdateLocalDate, p -> new MenuChart2().new Statistics( p.getMinPrice().doubleValue(), 
                                                                                                                                p.getMaxPrice().doubleValue(),
                                                                                                                                p.getAvgPrice().doubleValue())));


            Map<LocalDateTime, DoubleSummaryStatistics> priceStatisticsXhour =

                    menuPriceService.findAll(menu).parallelStream()
                            .filter(cp -> cp.getUpdateDate().after(DateUtils.yesterday()))
                            .sorted(comparing(MenuPrice::getUpdateDate))
                            .collect(Collectors.groupingBy(cp -> cp.getUpdateLocalDateHour(),
                                    Collectors.summarizingDouble(cp -> cp.getPriceInDouble())))
                            .entrySet().parallelStream().sorted(Map.Entry.comparingByKey())
                            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
                                    (oldValue, newValue) -> oldValue, LinkedHashMap::new));


            MenuChart2 menuChart2 = new MenuChart2();

            menuChart2.setLas24HPerHour (priceStatisticsXhour);
            menuChart2.setLast30DPerDay (last30DPerDay);
            menuChart2.setLast1YPerDay  (last1YPerDay);
            menuChart2.setLast3MPerDay  (last3MPerDay);
            menuChart2.setLast6MPerDay  (last6MPerDay);

            return menuChart2;

    }

但是当我访问它时,出现此错误:

com.google.common.util.concurrent.UncheckedExecutionException: java.lang.IllegalStateException: Duplicate key com.tdk.api.json.MenuChart2$Statistics@6f377595
    at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2218)
    at com.google.common.cache.LocalCache.get(LocalCache.java:4147)
    at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:4151)
    at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:5140)
    at com.tdk.backend.service.CachedMenuServiceImpl.getCharts(CachedMenuServiceImpl.java:189)
    at com.tdk.backend.service.CachedMenuServiceImpl$$FastClassBySpringCGLIB$$2ea84be7.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)

...

Caused by: java.lang.IllegalStateException: Duplicate key com.tdk.api.json.MenuChart2$Statistics@6f377595
    at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
    at java.util.HashMap.merge(HashMap.java:1254)
    at java.util.stream.Collectors.lambda$toMap$58(Collectors.java:1320)
    at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
    at java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:352)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
    at com.tdk.backend.service.CachedMenuServiceImpl.generateCharts(CachedMenuServiceImpl.java:238)
    at com.tdk.backend.service.CachedMenuServiceImpl$1.load(CachedMenuServiceImpl.java:86)
    at com.tdk.backend.service.CachedMenuServiceImpl$1.load(CachedMenuServiceImpl.java:84)
    at com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3708)
    at com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2416)
    at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2299)
    at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2212)
    ... 101 common frames omitted

1 个答案:

答案 0 :(得分:0)

异常似乎不是来自Guava缓存。它似乎是使用流从您那里收集并将其收集到地图中的。当您调用.get(..)时会发生异常,因为它是LoadingCache,因此当它在generate方法中运行代码时,就会生成异常。您可能正在做类似

的操作
.stream().collect(
                Collectors.toMap(o -> someFunctionToGetKey(),
                                 o -> someFunctionToGetValue())
        );

但是,在此实现中,您没有提供关于如果生成两个相同密钥会发生什么的逻辑。您需要指定第三个参数,该参数定义遇到重复密钥时要采取的行为。例如,考虑我们有这个基本的pojo

@Data
class Model {
    private int id;
    private String name;
}

我们有List<Model> list = new ArrayList<>();

您可以执行以下操作:

                list.stream()
                .collect(Collectors.toMap(
                        Model::getId,
                        Model::getName,
                        (v1,v2) -> v2
                ));

第三个参数(v2,v2) -> v2指示收集器如果列表中有两个具有相同ID的条目该怎么做。在此示例中,它仅将name的最新值用作键id