【问题标题】:How to cache (entire table or results of Domain.list() method) in grails?如何在 grails 中缓存(整个表或 Domain.list() 方法的结果)?
【发布时间】:2014-12-15 06:57:52
【问题描述】:

我有一个小表 SYMBOLS,用于填充 ui 上的下拉列表。由于该表主要包含静态数据,因此我想缓存其内容。

我的问题是每次调用 Symbol.list() 方法时,都会导致对数据库的查询执行。

领域类:

package com.perseus.ui.model

class Symbol implements Serializable, Comparable<Symbol> {

private static final long serialVersionUID = 1L;

String exchange
String symbol
String description
int index

static constraints = {
    exchange(nullable:false, blank: false)
    symbol(nullable:false, blank: false)
    description(nullable:false, blank: false)
}

static mapping = {
    id composite: ['exchange', 'symbol']
    table 'Symbols'
    cache 'read-only'
}

String toString() {
    return description
}

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result
            + ((exchange == null) ? 0 : exchange.hashCode());
    result = prime * result + ((symbol == null) ? 0 : symbol.hashCode());
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Symbol other = (Symbol) obj;
    if (exchange == null) {
        if (other.exchange != null)
            return false;
    } else if (!exchange.equals(other.exchange))
        return false;
    if (symbol == null) {
        if (other.symbol != null)
            return false;
    } else if (!symbol.equals(other.symbol))
        return false;
    return true;
}

@Override
public int compareTo(Symbol o) {
    int result = 0
    if(this.index)
        result = -1
    else if(o.index) {
        result = 1
    } else {
        result = this.symbol.compareTo(o.symbol)
    }
    return result;
}


}

resources.groovy

// Place your Spring DSL code here
beans = {
xmlns cache: 'http://www.springframework.org/schema/cache'
xmlns aop: 'http://www.springframework.org/schema/aop'

importBeans('classpath:config/beans.xml')

cache.'advice'(id: 'symbolCacheAdvice',
        'cache-manager': 'grailsCacheManager') {
        caching(cache: 'symbols') {
            cacheable(method: 'list')
        }
    }

aop.config {
    advisor('advice-ref': 'symbolCacheAdvice',
        pointcut: 'execution(* com.perseus.ui.model.Symbol.*(..))')
}

}

用户界面元素:&lt;g:select name="symbol" from="${Symbol.list(readOnly: true)}" optionKey="symbol" /&gt;

我尝试了以下选项,但它们似乎都不起作用:

  1. 休眠二级缓存:对象被缓存,但 Symbol.list() 总是命中数据库。
  2. 启用查询缓存:没有帮助
  3. Marked Symbols.list() 方法@Cachable:令人惊讶的是,即使这样也不起作用。请参阅 resources.groovy。

我相信这是非常常见的用例,实现它的最佳方法是什么?一种明显的解决方案是手动缓存它,但这听起来不太好。

【问题讨论】:

  • 我建议您改用 Grails 缓存插件,正如您从文档中看到的那样,它相当容易配置和使用:grails-plugins.github.io/grails-cache/guide/usage.html
  • 嗨 Joshua,我已经尝试过了,请检查选项 3 和 resources.groovy;它没有用。
  • 我没有看到您使用的是实际插件本身,而是使用弹簧缓存。如果您已经尝试过实际的插件,我深表歉意。我个人在这方面取得了巨大的成功。
  • 你试过自动生成方法的插件吗? Symbol.list() 方法是由 grails 自动生成的,我无法使用注解。
  • 我永远不会直接注释域,我会使用适当的服务层并对其进行注释。

标签: hibernate grails caching


【解决方案1】:

一般来说,避免对 GORM 查询结果使用缓存插件 - Hibernate 的 1 级和 2 级缓存非常可靠,并且可以抵抗过时的数据,因为当这些结果可能受到更改的影响时,它们会刷新缓存的数据。但是如果你直接使用 Ehcache,或者像 Spring Cache 或 Grails 插件这样的包装 API,你必须知道什么时候使数据无效,这可能是一个难题。

如果您计划仅通过 list() 方法访问缓存数据,这是有道理的,因为它都将在内存中,那么您需要一个使用查询缓存的变体,并且必须在 DataSource 中启用它。时髦的:

hibernate {
   cache.use_second_level_cache = true
   cache.use_query_cache = true
   ...
}

list() 没有缓存选项,您可以运行等效的条件查询:

def symbols = Symbol.createCriteria().list {
   cache true
}

如果您想按 ID 访问项目,请运行查询以获取所有 ID,然后在启动时循环并为每个 ID 调用 Symbol.get(id)。单个实例缓存在自己的缓存中,而不是查询缓存中,因此它们不太可能提前失效。

另外请注意,您需要配置底层缓存实现,并配置缓存大小、内存中的元素、溢出到磁盘等。Ehcache 中的默认 TTL 是 120 秒——也就是只有 2 分钟。但是,如果您很少或从不编辑/创建/删除这些内容,则可以根据需要缓存。

【讨论】:

  • 谢谢 Burt,所以缓存条件查询结果只会缓存 Symbol 域类的 id 而不是整个对象?只是想了解为什么我们需要为每个对象显式调用Symbol.get(id)
  • 我应用了 grails 缓存插件并添加了一个服务层。标记了服务层方法@Cacheable 并且工作正常。
  • @BurtBeckwith :我在 withCriteria 中尝试了上面的代码(缓存为真),性能似乎没有任何差异,无论是否缓存,都需要相同的毫秒数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多