优化SQL查询的数量

时间:2019-04-28 22:10:14

标签: java spring

我使用以下代码生成包含数据的图表:

    @GetMapping("/terminals")
    public ResponseEntity<Map<String, List<TopTerminalsDTO>>> getTopTerminals(
            @RequestParam(value = "start_date", required = true) String start_date,
            @RequestParam(value = "end_date", required = true) String end_date) {


        final List<PaymentTransactionsDailyFacts> list = dashboardService.findTop_Terminals(start_dateTime, end_dateTime);

        final Collector<PaymentTransactionsDailyFacts, List<TopTerminalsDTO>, List<TopTerminalsDTO>> terminalsCollector = Collector
                .of(ArrayList::new, (terminals, p) -> terminals.add(mapper.toTopTerminalsDTO(p)),
                        (accumulator, terminals) -> {
                            accumulator.addAll(terminals);
                            return accumulator;
                        });

        final Map<String, List<TopTerminalsDTO>> final_map = list.stream().filter(p -> p.getTerminal_id() != null)
                .collect(Collectors.groupingBy(p -> getTerminalName(p.getTerminal_id()), terminalsCollector));

        return ResponseEntity.ok(final_map);
    }

    private String getTerminalName(Integer id) {      
        Optional<Terminals> obj = terminalService.findById(id);       
        return obj.map(Terminals::getName).orElse("");
    }

但是我注意到getTerminalName被叫了10次以上,以从数字转换名字。您知道如何通过一些优化来减少通话次数吗?

2 个答案:

答案 0 :(得分:2)

修改findTop_TerminalsPaymentTransactionsDailyFacts以包括名称(使用SQL LEFT JOIN子句)。


或者,在列表中扫描所有终端ID,然后调用List<Terminals> list = terminalService.findByIds(idList);方法以使用SQL IN子句获取所有这些终端。

注意:注意SQL语句中可以包含多少个?标记。

然后构建一个Map<Integer, String>映射终端ID到名称,并将getTerminalName方法替换为地图查找。

答案 1 :(得分:1)

听起来像是临时缓存的情况,范围仅限于此请求,或者如果终端名称足够稳定,则可能更长。

很显然,像幕后的ehCache之类的东西很适合此操作,但是我经常对战术性的缓存很感兴趣,特别是如果我不想将缓存的值保留在此即时请求之外。

例如:

    TerminalNameCache cache = new TerminalNameCache();

    final Map<String, List<TopTerminalsDTO>> final_map = list.stream()
            .filter(p -> p.getTerminal_id() != null)
            .collect(Collectors.groupingBy(
                    p -> cache.getTerminalName(p.getTerminal_id()),
                    terminalsCollector));

然后TerminalNameCache只是父Controller类中的一个内部类。这是为了允许它从问题中调用现有的private String getTerminalName(Integer id)方法(如下面的ParentControllerClass所示):

private class TerminalNameCache {
    private final Map<Integer, String> cache = new ConcurrentHashMap<>();

    private String getTerminalName(Integer id) {
        return cache.computeIfAbsent(id,
                id2 -> ParentControllerClass.this.getTerminalName(id2));
    }
}

如果这看起来已成为一种模式,则值得将缓存重构为更可重用的内容。例如。这可以基于对通用函数的缓存调用:

public class CachedFunction<T, R> implements Function<T, R> {
    private final Function<T, R> function;
    private final Map<T, R> cache = new ConcurrentHashMap<>();

    public CachedFunction(Function<T, R> function) {
        this.function = function;
    }

    @Override
    public R apply(T t) {
        return cache.computeIfAbsent(t, t2 -> function.apply(t2));
    }
}

然后将这样使用:

    CachedFunction<Integer, String> cachedFunction = new CachedFunction<>(
            id -> getTerminalName(id));

    final Map<String, List<TopTerminalsDTO>> final_map = list.stream()
            .filter(p -> p.getTerminal_id() != null)
            .collect(Collectors.groupingBy(
                    p -> cachedFunction.apply(p.getTerminal_id()),
                    terminalsCollector));