用户模块——整合 Spring 缓存(Cacheable)

徽章获取接口开发

在用户的个人页面,我们需要显示所有徽章 ,并区分哪些已经获得,哪些还没获得(已获得的显示亮色,未获得的显示灰色)。为此,我们需要开发一个获取徽章的接口 ,整个流程可以拆分为 两步

1. 获取所有徽章列表

首先,我们要从数据库中查出所有的徽章,因为徽章是固定的,不会变 ,所以我们可以缓存它,减少数据库查询的次数,提高性能。

java 复制代码
@Cacheable(value = "badges", key = "'all'")
public List<Badge> getAllBadges() {
    return badgeMapper.selectList(null);  // 查询所有徽章
}

⚡ 这里用了 @Cacheable,表示把徽章列表缓存起来,避免每次都查数据库,提高访问速度!

2. 判断用户拥有的徽章

获取所有徽章后,我们还要检查当前用户拥有的徽章,并调整它们的显示状态。

java 复制代码
public List<UserBadgeVO> getUserBadges(Long userId) {
    List<Badge> allBadges = getAllBadges();  // 获取所有徽章
    List<Long> userBadgeIds = userBadgeMapper.getUserBadgeIds(userId);  // 查询用户已获得的徽章ID

    return allBadges.stream()
        .map(badge -> new UserBadgeVO(badge, userBadgeIds.contains(badge.getId())))
        .sorted(Comparator.comparing(UserBadgeVO::isOwned).reversed())  // 已获得的排前面
        .collect(Collectors.toList());
}

✅ 这里的关键点

  • 先查出所有徽章

  • 查出用户拥有的徽章ID列表

  • 对比徽章ID ,已获得的标记为 亮色,未获得的标记为 灰色

  • 排序:已获得的放前面,未获得的放后面

最终接口

复制代码
@GetMapping("/user/badges")
public List<UserBadgeVO> getUserBadges(@RequestParam Long userId) {
    return badgeService.getUserBadges(userId);
}

这样做的好处

查询徽章时优先用缓存,减少数据库压力
只查询用户的徽章ID,提高效率
已获得的放前面,未获得的放后面,显示更友好

这样,徽章获取接口就开发完成啦!🚀

本地缓存优化

在开发中,我们通常会使用缓存 来提高性能,避免每次都去数据库查询。Spring 提供了缓存功能,可以将数据存储在内存中,让访问变得更快。但是,如何优化缓存,才能提高效率和减少内存消耗呢?下面我们就来介绍 本地缓存优化

1. 使用 Spring 缓存 + Caffeine

Spring 的缓存功能很方便,但我们可以通过结合 Caffeine(一个高效的本地缓存库)来实现更灵活、更高效的缓存管理。

首先,添加依赖:

java 复制代码
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>2.9.1</version>
</dependency>

然后,启用 Spring 缓存功能:

java 复制代码
@EnableCaching  // 启用缓存
@Configuration
public class CacheConfig {
    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        cacheManager.setCaffeine(Caffeine.newBuilder()
            .initialCapacity(100)  // 初始大小
            .maximumSize(200)  // 最大缓存数
            .expireAfterWrite(5, TimeUnit.MINUTES));  // 缓存5分钟后自动过期
        return cacheManager;
    }
}

这里,我们使用了 Caffeine 来优化缓存管理,设置了:

  • 初始大小:缓存初始时存储 100 条数据

  • 最大缓存大小:缓存最多只能存 200 条数据

  • 缓存过期时间:缓存数据会在 5 分钟后自动失效

2. 使用 @Cacheable 缓存数据

当我们需要缓存某些数据时,可以在方法上加上 @Cacheable 注解,这样每次调用时,Spring 会自动检查缓存中是否有数据,如果有就直接取缓存,没有的话再去数据库查询,并把查询结果缓存起来。

java 复制代码
@Cacheable(value = "badges", key = "'all'")
public List<Badge> getAllBadges() {
    return badgeMapper.selectList(null);  // 查询所有徽章
}

在这里,我们把 所有徽章 列表缓存起来,并设置缓存的名字为 badges,缓存的键为 'all'。这样就可以避免每次都去查询数据库,提升了性能。

3. 缓存更新与删除

有时候,我们需要更新 缓存或删除 缓存,比如在用户获得新徽章后,我们要更新缓存。可以使用 @CachePut@CacheEvict 注解来做这些操作。

  • @CachePut:更新缓存

  • @CacheEvict:清空缓存

java 复制代码
@CachePut(value = "badges", key = "'all'")
public List<Badge> updateBadges() {
    return badgeMapper.selectList(null);  // 更新缓存
}

@CacheEvict(value = "badges", key = "'all'")
public void clearCache() {
    // 清空缓存
}

这样做的好处

提高性能 :减少数据库查询,直接从内存中获取数据
自动管理缓存 :设置缓存大小、过期时间,自动清理过期数据
灵活控制缓存:可以更新或删除缓存,保证数据的一致性

通过 Caffeine 和 Spring 缓存的结合,我们就可以实现更高效、灵活的缓存管理,显著提高系统的性能!🚀

Spring 缓存三件套

在使用 Spring 缓存时,我们常常会用到 三件套@Cacheable@CachePut@CacheEvict。它们分别用于不同的缓存场景,下面我们逐个来看它们的用途和使用方法。

1. @Cacheable:缓存数据

@Cacheable 用来标记一个方法,表示当方法被调用时,先去缓存中查找数据,如果缓存中有数据,就直接返回缓存中的数据;如果没有,就执行方法逻辑并将结果缓存起来,以便下次使用。

使用示例

假设我们有一个方法用来查询所有徽章,我们希望将查询结果缓存起来,避免每次查询都去数据库。

java 复制代码
@Cacheable(value = "badges", key = "'all'")
public List<Badge> getAllBadges() {
    return badgeMapper.selectList(null);  // 查询所有徽章
}
  • value = "badges":缓存的名称是 badges

  • key = "'all'":缓存的键是 'all',表示所有徽章列表缓存用这个键来存储

这样,第一次查询会从数据库获取数据并存入缓存,之后的查询会直接从缓存中获取数据,提升性能。

2. @CachePut:更新缓存

@CachePut 用来更新缓存中的数据。每次调用标记的方法时,都会执行方法,并更新对应的缓存。通常用在更新数据的场景中。

使用示例

假设用户获得了一个新的徽章,我们需要更新缓存中的徽章列表:

复制代码
@CachePut(value = "badges", key = "'all'")
public List<Badge> updateBadges() {
    return badgeMapper.selectList(null);  // 更新缓存中的所有徽章
}
  • 每次调用 updateBadges() 时,都会执行方法,并更新缓存中的数据。

3. @CacheEvict:清除缓存

@CacheEvict 用来清除缓存中的数据。通常在数据更新或删除后,调用该方法来删除缓存,确保缓存数据是最新的。你可以选择删除特定的缓存,也可以删除所有缓存。

使用示例

假设我们删除了一个徽章后,需要清空缓存中的徽章列表,以便下次重新加载:

java 复制代码
@CacheEvict(value = "badges", key = "'all'")
public void clearCache() {
    // 清空缓存中的所有徽章
}
  • value = "badges":指定要清空的缓存名称是 badges

  • key = "'all'":指定要清除的缓存键是 'all'

总结:三件套的作用

  1. @Cacheable:查询数据时,先查缓存,没有再查数据库,查询结果缓存起来。

  2. @CachePut:更新缓存数据,每次调用都会更新缓存。

  3. @CacheEvict:清空缓存,用于数据删除或更新后的缓存清理。

通过这三件套,我们可以方便地管理缓存,提高系统性能,同时保持数据的一致性!

获取徽章列表

· 在开发中,我们需要展示所有的徽章列表,并告诉用户哪些已经获得,哪些还没有。这个功能我们可以通过一个简单的接口来实现。我们分为两个主要步骤:

1. 获取所有徽章数据

首先,我们需要从数据库中获取所有的徽章。由于徽章数据通常是固定的,不会频繁变化,所以可以缓存这个数据,以便更高效地获取。

java 复制代码
@Cacheable(value = "badges", key = "'all'")
public List<Badge> getAllBadges() {
    return badgeMapper.selectList(null);  // 查询所有徽章
}
  • @Cacheable:表示当查询徽章时,首先会检查缓存中是否有数据。如果有就直接从缓存获取,否则会去数据库查询,并将结果缓存起来。

  • value = "badges" :缓存的名字是 badges

  • key = "'all'" :缓存的键是 'all',表示缓存的是所有徽章。

这样,后续每次请求徽章数据时,都会直接从缓存中获取,避免了频繁的数据库查询,提高了性能。

2. 获取用户的徽章状态

获取所有徽章后,我们还需要检查每个用户是否已经获得某些徽章,并返回他们的状态(已获得/未获得)。假设每个用户的徽章信息存在 user_badge 表中,我们可以查询出用户已经获得的徽章,并标记它们。

java 复制代码
public List<UserBadgeVO> getUserBadges(Long userId) {
    List<Badge> allBadges = getAllBadges();  // 获取所有徽章
    List<Long> userBadgeIds = userBadgeMapper.getUserBadgeIds(userId);  // 查询用户已获得的徽章ID

    return allBadges.stream()
        .map(badge -> new UserBadgeVO(badge, userBadgeIds.contains(badge.getId())))
        .sorted(Comparator.comparing(UserBadgeVO::isOwned).reversed())  // 已获得的放前面
        .collect(Collectors.toList());
}
  • userBadgeMapper.getUserBadgeIds(userId):查询当前用户已获得的徽章ID列表。

  • UserBadgeVO:用于包装徽章信息和用户是否已获得该徽章的状态。

  • sorted():将已获得的徽章排在前面,未获得的放后面。

3. 返回徽章列表给前端

最后,定义一个接口方法,返回给前端所有徽章的状态:

java 复制代码
@GetMapping("/user/badges")
public List<UserBadgeVO> getUserBadges(@RequestParam Long userId) {
    return badgeService.getUserBadges(userId);  // 获取用户的徽章列表
}

总结

通过以上步骤,我们实现了获取所有徽章并判断用户是否拥有这些徽章的功能。

  1. 缓存所有徽章数据,提高查询效率。

  2. 查询用户已获得的徽章,标记徽章状态。

  3. 返回徽章列表,供前端展示。

这样一来,用户就能看到自己拥有的徽章和未获得的徽章,并且能高效地加载这些数据!🚀

佩戴徽章接口

在系统中,用户可以选择佩戴一个徽章,我们需要实现一个接口来处理这个功能。佩戴徽章时,我们首先要确认用户是否拥有该徽章,并且确认该徽章是否是一个有效的徽章。然后,我们将徽章的佩戴状态更新到数据库中。

1. 接口设计

我们首先需要设计一个接口,接收用户请求佩戴某个徽章。这个接口会检查用户是否拥有该徽章,且确保这个徽章是有效的。

java 复制代码
@PostMapping("/user/badge/{badgeId}/wear")
public String wearBadge(@PathVariable Long badgeId, @RequestParam Long userId) {
    badgeService.wearBadge(badgeId, userId);  // 调用服务层佩戴徽章
    return "佩戴成功";
}
  • badgeId:徽章的 ID,表示用户要佩戴的徽章。

  • userId:用户的 ID,表示要佩戴徽章的用户。

2. 服务层逻辑

在服务层,我们需要编写逻辑,处理徽章的佩戴。首先,我们需要检查用户是否拥有该徽章,然后判断它是否为有效的徽章,最后更新数据库中的佩戴状态。

java 复制代码
public void wearBadge(Long badgeId, Long userId) {
    // 1. 检查徽章是否存在
    Badge badge = badgeMapper.selectById(badgeId);
    if (badge == null) {
        throw new RuntimeException("徽章不存在");
    }

    // 2. 检查用户是否拥有此徽章
    boolean hasBadge = userBadgeMapper.userHasBadge(userId, badgeId);
    if (!hasBadge) {
        throw new RuntimeException("用户没有该徽章");
    }

    // 3. 确保徽章是有效的
    if (!badge.isValid()) {
        throw new RuntimeException("该徽章无效");
    }

    // 4. 更新用户佩戴的徽章
    userBadgeMapper.updateBadgeStatus(userId, badgeId, true);  // 将徽章状态设置为已佩戴
}
  • badgeMapper.selectById(badgeId):查询徽章信息,检查徽章是否存在。

  • userBadgeMapper.userHasBadge(userId, badgeId):检查用户是否拥有该徽章。

  • badge.isValid():检查徽章是否有效。

  • userBadgeMapper.updateBadgeStatus(userId, badgeId, true):更新数据库,将徽章的佩戴状态设置为已佩戴。

3. 错误处理

为了确保流程顺畅,我们还需要处理一些常见的错误情况:

  • 用户尝试佩戴一个不存在的徽章。

  • 用户没有获得该徽章。

  • 徽章无效。

这些错误我们通过抛出 RuntimeException 来进行处理,并给出相应的错误信息。

总结

通过这个接口,用户可以佩戴自己已获得的有效徽章,系统会先检查用户是否拥有该徽章,并确保徽章有效。如果条件满足,就更新徽章的佩戴状态。

  1. 查询徽章,确保徽章存在且有效。

  2. 检查用户是否拥有徽章

  3. 更新佩戴状态,将徽章标记为已佩戴。

这样就能顺利地让用户佩戴徽章啦!🎖

相关推荐
缺点内向7 小时前
Java:创建、读取或更新 Excel 文档
java·excel
带刺的坐椅7 小时前
Solon v3.4.7, v3.5.6, v3.6.1 发布(国产优秀应用开发框架)
java·spring·solon
四谎真好看9 小时前
Java 黑马程序员学习笔记(进阶篇18)
java·笔记·学习·学习笔记
桦说编程9 小时前
深入解析CompletableFuture源码实现(2)———双源输入
java·后端·源码
java_t_t9 小时前
ZIP工具类
java·zip
lang201509289 小时前
Spring Boot优雅关闭全解析
java·spring boot·后端
pengzhuofan10 小时前
第10章 Maven
java·maven
百锦再11 小时前
Vue Scoped样式混淆问题详解与解决方案
java·前端·javascript·数据库·vue.js·学习·.net
刘一说11 小时前
Spring Boot 启动慢?启动过程深度解析与优化策略
java·spring boot·后端
壹佰大多11 小时前
【spring如何扫描一个路径下被注解修饰的类】
java·后端·spring