系列十、SpringBoot + MyBatis + Redis实现分布式缓存(基于注解方式)

发布时间:2023年12月17日

一、概述

? ? ? ? 上篇文章?系列九、SpringBoot + MyBatis + Redis实现分布式缓存?介绍了基于xml方式实现分布式缓存的效果,当前大家使用的技术栈基本是springboot+各种框架的组合,而springboot显著的一个特点就是去xml配置,那么在无xml配置的情形下,又该如何实现分布式缓存呢?请看下面的代码实战

二、代码实战

2.1、分布式缓存相关的注解

? ? ? ? 基于注解方式的分布式缓存,主要涉及到如下几个注解:

? ? ? ? (1)@EnableCaching:一般标注在配置类上,表示开启Spring的缓存,如果不加此注解的话Spring自带的缓存将不生效;

? ? ? ? (2)@CacheConfig(cacheNames = "xxx"):一般标注在service类上,用于配置cache的名字,建议以当前service类的全路径名作为cache的名字;

? ? ? ? (3)@Cacheable:一般标识在service层的查询方法上,表示将一个方法的返回值缓存起来, ?默认情况下,缓存的key就是方法的参数,缓存的value就是方法的返回值,如果查询 方法无参数,则会使用默认的key,即SimpleKey [];

? ? ? ? (4)@CachePut(key = "#department.id"):一般加在service层的更新方法上(update),当数据库中的数据更新后,缓存中的数据也要跟着更新,使用此注解,可以将方法的返回值 自动更新到已经存在的key上

? ? ? ? (5)@CacheEvict:一般加在service层的删除方法上,当数据库中的数据删除后,相关的缓存也会被删除,使用该注解的时候,也可以配置按照某种条件删除(某种条件:@CacheEvict注解中的条件,例如:value、cacheNames、key、keyGenerator...)

2.2、项目概览

2.3、pom

<dependencies>
	<!-- springboot -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-devtools</artifactId>
	</dependency>

	<!-- 数据源 -->
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>8.0.26</version>
	</dependency>
	<dependency>
		<groupId>org.mybatis.spring.boot</groupId>
		<artifactId>mybatis-spring-boot-starter</artifactId>
		<version>2.3.1</version>
	</dependency>
	<dependency>
		<groupId>com.alibaba</groupId>
		<artifactId>druid-spring-boot-starter</artifactId>
		<version>1.1.10</version>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-redis</artifactId>
	</dependency>

	<!-- 工具 -->
	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
		<version>1.18.30</version>
	</dependency>
	<dependency>
		<groupId>cn.hutool</groupId>
		<artifactId>hutool-all</artifactId>
		<version>5.8.21</version>
	</dependency>
	<dependency>
		<groupId>org.apache.commons</groupId>
		<artifactId>commons-lang3</artifactId>
	</dependency>
	<dependency>
		<groupId>org.apache.commons</groupId>
		<artifactId>commons-collections4</artifactId>
		<version>4.4</version>
	</dependency>
	<dependency>
		<groupId>com.alibaba.fastjson2</groupId>
		<artifactId>fastjson2</artifactId>
		<version>2.0.25</version>
	</dependency>

</dependencies>

2.4、yml

server:
  port: 9999

spring:
  redis:
    host: 
    port: 6379
    database: 0
    password: 123456

  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/20231018_redis?useSSL=false&useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT
    username: root
    password: 123456

mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: org.stat.entity.model
  configuration:
    map-underscore-to-camel-case: true

logging:
  level:
    org:
      star:
        mapper: debug

2.5、主启动

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/12/10 12:44
 * @Description:
 *
 */
@MapperScan(basePackages = "org.star.mapper")
@SpringBootApplication
public class SpringbootRedisDistributeCacheAnnotationApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootRedisDistributeCacheAnnotationApplication.class, args);
    }


}

2.6、MyRedisConfig

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/12/10 15:28
 * @Description:
 * @EnableCaching的作用:开启Spring的缓存,如果不加此注解的话Spring自带的缓存将不生效
 *
 */
@EnableCaching
@Configuration
public class MyRedisConfig {

    /**
     * RedisTemplate k v 序列化
     *
     * @param connectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);

        redisTemplate.setKeySerializer(RedisSerializer.string());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        redisTemplate.setHashKeySerializer(RedisSerializer.string());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean
    public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory());
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                // 设置默认的超时时间为2小时
                .entryTtl(Duration.ofHours(2))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()))
                // 设置默认的缓存前缀
                .prefixCacheNameWith("REDIS_CACHE_");
        return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
    }

}

2.7、DepartmentDO

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/12/10 12:48
 * @Description:
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@ToString(callSuper = true)
public class DepartmentDO implements Serializable {
    /**
     * 编号
     */
    private Integer id;

    /**
     * 部门名称
     */
    private String departmentName;

}

2.8、DepartmentMapper.java

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/12/10 12:50
 * @Description:
 */
public interface DepartmentMapper {

    /**
     * 查询所有部门
     * @return
     */
    List<DepartmentDO> listAllDepartment();

    /**
     * 根据id查询部门信息
     * @param id
     * @return
     */
    DepartmentDO getDepartmentById(Integer id);

    /**
     * 根据id和departmentName查询部门
     * @param id
     * @param departmentName
     * @return
     */
    DepartmentDO getDepartment(Integer id,String departmentName);

    /**
     * 更新Department
     * @param department
     * @return
     */
    int updateDepartment(DepartmentDO department);

    /**
     * 删除部门
     * @param id
     */
    void deleteDepartment(Integer id);
}

2.9、DepartmentMapper.xml

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="org.star.mapper.DepartmentMapper">

    <select id="listAllDepartment" resultType="org.star.entity.model.DepartmentDO">
        select id,department_name from department
    </select>

    <select id="getDepartmentById" resultType="org.star.entity.model.DepartmentDO">
        select id,department_name from department where id = #{id}
    </select>

    <select id="getDepartment" resultType="org.star.entity.model.DepartmentDO">
        select id,department_name from department where id = #{id} and department_name = #{departmentName}
    </select>

    <update id="updateDepartment" useGeneratedKeys="true" keyProperty="id">
        update department set department_name = #{departmentName} where id = #{id}
        <selectKey resultType="org.star.entity.model.DepartmentDO" order="AFTER" keyProperty="id">
            select id,department_name from department where id = #{id}
        </selectKey>
    </update>

    <delete id="deleteDepartment">
        delete from department where id = #{id}
    </delete>

</mapper>

2.10、DepartmentService

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/12/10 20:00
 * @Description:
 * 基于注解的分布式缓存,redis中key的生成规则:${prefixCacheNameWith} + "_" + ${cacheNames} + "_" + ${key}
 *      说明:prefixCacheNameWith为RedisCacheManager中配置的前缀
 *      举例:
 *          (1)listAllDepartment ===> REDIS_CACHE_org.star.service.DepartmentService::SimpleKey []
 *          (2)getDepartmentById ===> REDIS_CACHE_org.star.service.DepartmentService::1
 *          (3)getDepartment ===> REDIS_CACHE_org.star.service.DepartmentService::SimpleKey [1,研发部]
 *
 */
@Service
@CacheConfig(cacheNames = "org.star.service.DepartmentService")
public class DepartmentService {

    @Resource
    private DepartmentMapper departmentMapper;

    /**
     * @return
     * @Cacheable的作用:
     *      @Cacheable注解一般加在查询方法上,表示将一个方法的返回值缓存起来,
     * 默认情况下,缓存的key就是方法的参数,缓存的value就是方法的返回值,如果查询
     * 方法无参数,则会使用默认的key,即SimpleKey []
     */
    @Cacheable
    public List<DepartmentDO> listAllDepartment() {
        List<DepartmentDO> departments = departmentMapper.listAllDepartment();

        return departments;
    }

    /**
     * 对于只有一个参数的查询方法,其key位id对应的值
     * @param id
     * @return
     */
    @Cacheable
    public DepartmentDO getDepartmentById(Integer id) {
        return departmentMapper.getDepartmentById(id);
    }

    /**
     *
     * 对于有多个参数的查询方法,其key为所有的参数,如果想修改,可以单独指定,例如:@Cacheable(key = "#id")
     * @param id
     * @param departmentName
     * @return
     */
    @Cacheable
    public DepartmentDO getDepartment(Integer id,String departmentName) {
        return departmentMapper.getDepartment(id,departmentName);
    }

    /**
     * @CachePut作用:
     *      @CachePut注解一般加在更新方法上(update),当数据库中的数据更新后,缓存中的数据也要跟着更新,使用此注解,可以将方法的返回值
     *      自动更新到已经存在的key上,示例如下:
     * @param department
     * @return
     */
    @CachePut(key = "#department.id")
    public DepartmentDO updateDepartment(DepartmentDO department) {
        departmentMapper.updateDepartment(department);
        return department;
    }

    /**
     * @CacheEvict()作用:
     *      @CacheEvict()注解一般加在删除方法上,当数据库中的数据删除后,相关的缓存也会被删除,使用该注解的时候,也可以配置按照某种条件
     *      删除(某种条件:@CacheEvict注解中的条件,例如:value、cacheNames、key、keyGenerator...)
     * @param id
     */
    @CacheEvict
    public void deleteDepartment(Integer id) {
        departmentMapper.deleteDepartment(id);
    }

}

2.11、DepartmentServiceTest

/**
 * @Author : 一叶浮萍归大海
 * @Date: 2023/12/10 20:07
 * @Description:
 */
@SpringBootTest
public class DepartmentServiceTest {

    @Resource
    private DepartmentService departmentService;

    @Test
    public void listAllDepartmentTest() {
        List<DepartmentDO> departments1 = departmentService.listAllDepartment();
        System.out.println("departments1 = " + departments1);

        System.out.println("=============================");

        List<DepartmentDO> departments2 = departmentService.listAllDepartment();
        System.out.println("departments2 = " + departments2);
    }

    @Test
    public void getDepartmentByIdTest() {
        DepartmentDO department1 = departmentService.getDepartmentById(1);
        System.out.println("department1 = " + department1);

        System.out.println("========================");
        DepartmentDO department2 = departmentService.getDepartmentById(1);
        System.out.println("department2 = " + department2);
    }

    @Test
    public void getDepartmentTest() {
        DepartmentDO department1 = departmentService.getDepartment(1, "研发部");
        System.out.println("department1 = " + department1);

        System.out.println("============================");

        DepartmentDO department2 = departmentService.getDepartment(1, "研发部");
        System.out.println("department2 = " + department2);
    }

    @Test
    public void updateDepartmentTest() {
        DepartmentDO department = new DepartmentDO().setDepartmentName("研发部444").setId(1);
        DepartmentDO updatedDepartment = departmentService.updateDepartment(department);
        System.out.println("updatedDepartment = " + updatedDepartment);
    }

    @Test
    public void deleteDepartmentTest() {
        departmentService.deleteDepartment(1);
    }

}

2.12、测试

2.12.1、listAllDepartmentTest

2.12.2、?getDepartmentByIdTest

2.12.3、getDepartmentTest

2.12.4、?updateDepartmentTest

2.12.5、?deleteDepartmentTest

文章来源:https://blog.csdn.net/HelloWorld20161112/article/details/134918073
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。