使用@Cacheable注解的现实世界DAO最佳实践

5

我是Spring的新手,对它提供的所有功能都非常兴奋!

目前,我正在尝试现代化我的现有项目中的一些DAO。具体来说,我想用Spring Jdbc支持取代旧的普通JDBC代码,并引入Spring的透明缓存。

  1. 第一个问题:我是否正确认为在DAO层添加@Cacheable注释是正确的方法?或者您是在业务层这样做的?

  2. 我可以按照网络上找到的所有简单示例进行操作。然而,当涉及到更多实际的代码时,我陷入了困境:

具体而言,我有几个返回我的模型Publisher实例的方法。在我的手动缓存实现中,我总是确保为不同的getPublisher()方法缓存并获取了一个实例。然而,在使用@Cacheable并尝试将缓存封装在DAO内部时,我遇到了问题,因为该注释无法在本地方法调用中使用(由于代理)。

下面是我的示例代码:

@Cacheable("publisherCache")
public Publisher getPublisher(int publisherId)
{
    String sql = "SELECT * FROM publisher LEFT JOIN publisherContacts USING (publisherId) WHERE publisherId=? ORDER BY publisherContactId ASC";
    return getJdbcTemplate().query(sql, publisherResultSetExtractor, publisherId);
}

public List<Publisher> findVisiblePublishers()
{
    List<Publisher> publishers = new LinkedList<Publisher>();
    for (int publisherId : findVisiblePublisherIds())
    {
        publishers.add(getPublisher(publisherId));
    }
    return publishers;
}

@Cacheable(value = "publisherCache", key = "'list'")
private List<Integer> findVisiblePublisherIds()
{
    String sql = "SELECT publisherId FROM publisher WHERE isVisible='yes' ORDER BY imprintName";
    return getJdbcTemplate().queryForList(sql, Integer.class);
}

public Publisher findNearestPublisher(String appx)
{
    appx = "%" + appx + "%";
    String sql = "SELECT publisherId FROM publisher WHERE publisherName LIKE ? OR imprintName LIKE ? ORDER BY imprintName DESC LIMIT 1";
    Integer publisherId = getJdbcTemplate().queryForObject(sql, Integer.class, appx, appx);
    if (publisherId == null)
        return null;
    else
        return getPublisher(publisherId);
}

这是我的想法,如前所述,不起作用。
目前唯一的替代方案是:
a)仅缓存`getPublisher(publisherId)`方法,并定义所有返回`Publisher`的其他方法返回`int`或其列表。从API角度来看,这并不自然。作为服务或业务逻辑,我希望从DAO中获取实例,而不仅仅是ID。
b)将`@Cacheable`添加到所有方法中,复制缓存并使用比所需更多的内存(假设有很多出版商并且它们是重型对象)。
在这个必须非常普遍的问题上,最佳实践是什么?感谢任何见解。

最好将两个问题分别放在两个 Stack Overflow 的问题中。 - Raedwald
为什么不缓存“findVisiblePublishers”方法的结果呢?是的,第一次调用时会运行两个数据库查询,但仅此而已。我认为这是一个很好的缓存目标。 - Mikhail
谢谢Mikhail。因为它将缓存我返回的每个出版商列表的Publisher对象(并缓存)。上面使用getPublisher(id)的方法不起作用。当您从同一类中调用getPublisher(id)时,不会询问缓存,而是每次执行一个DB请求。这意味着我有两个相同的Publisher实例被缓存,一次在列表中,一次作为单个项目。当我有更多类似的方法时,情况会变得更糟。 - marc82ch
1个回答

0
常见的模式如下: 对于持久层,您可以使用Hibernate二级缓存。 对于服务层,您可以使用@Cacheable来缓存长时间计算或Web服务调用结果等内容。

嗨Anton,感谢您的回答。这回答了我第一个问题的一部分。所以,当我的目的是保存DB请求(我不能使用Hibernate / JPA)时,在DAO层进行操作是正确的。无论如何,即使在服务层中,我对第二个问题也有同样的问题。将@Cacheable移动到服务级别并不能解决我重复缓存的问题。 - marc82ch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接