用户模块——整合 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. 更新佩戴状态,将徽章标记为已佩戴。

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

相关推荐
qq_529835355 分钟前
装饰器模式:如何用Java打扮一个对象?
java·开发语言·装饰器模式
日暮南城故里10 分钟前
Java学习------源码解析之StringBuilder
java·开发语言·学习·源码
一个public的class3 小时前
什么是 Java 泛型
java·开发语言·后端
士别三日&&当刮目相看3 小时前
JAVA学习*Object类
java·开发语言·学习
快来卷java3 小时前
MySQL篇(一):慢查询定位及索引、B树相关知识详解
java·数据结构·b树·mysql·adb
凸头4 小时前
I/O多路复用 + Reactor和Proactor + 一致性哈希
java·哈希算法
慵懒学者4 小时前
15 网络编程:三要素(IP地址、端口、协议)、UDP通信实现和TCP通信实现 (黑马Java视频笔记)
java·网络·笔记·tcp/ip·udp
anda01094 小时前
11-leveldb compact原理和性能优化
java·开发语言·性能优化
mqiqe4 小时前
Spring MVC 页面跳转方案与区别
python·spring·mvc
Pasregret5 小时前
04-深入解析 Spring 事务管理原理及源码
java·数据库·后端·spring·oracle