最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot+MyBatis+Redis實現(xiàn)分布式緩存

 更新時間:2024年01月10日 11:30:53   作者:YYAugenstern  
本文主要介紹了SpringBoot+MyBatis+Redis實現(xiàn)分布式緩存,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

一、緩存介紹

1.1、概述

緩存是計算機內(nèi)存中的一段數(shù)據(jù)(PS:內(nèi)存中的數(shù)據(jù)具有讀寫快、斷電立即消失的特點),合理地使用緩存能夠提高網(wǎng)站的吞吐量和運行效率,減輕數(shù)據(jù)庫的訪問壓力。那么哪些數(shù)據(jù)適合緩存呢?使用緩存時,一定是數(shù)據(jù)庫中的數(shù)據(jù)極少發(fā)生改變,更多用于查詢的情況,例如:省、市、區(qū)、縣、村等數(shù)據(jù)。

1.2、本地緩存 vs 分布式緩存

  • 本地緩存:存儲在應(yīng)用服務(wù)器內(nèi)存中的數(shù)據(jù)稱之為本地緩存(local cache); 
  • 分布式緩存:存儲在當(dāng)前應(yīng)用服務(wù)器內(nèi)存之外的數(shù)據(jù)稱之為分布式緩存(distribute cache);
  • 集群:將同一服務(wù)的多個節(jié)點放在一起,共同為系統(tǒng)提供服務(wù)的過程稱之為集群(cluster);
  • 分布式:由多個不同的服務(wù)集群共同對系統(tǒng)提供服務(wù),那么這個系統(tǒng)就被稱之為分布式系統(tǒng)(distribute system);

1.3、MyBatis默認(rèn)的緩存策略

關(guān)于MyBatis的一級緩存、二級緩存請參考這篇文章,這里不再贅述。單機版的mybatis一級緩存默認(rèn)是開啟的,開啟二級緩存也很簡單,再mybatis的核心配置文件和xxxMapper.xml中分別添加如下配置即可激活MyBatis的二級緩存:

二級緩存也叫SqlSeesionFactory級別的緩存,其特點是所有會話共享。不管是一級緩存還是二級緩存,這些緩存都是本地緩存,適用于單機版?;ヂ?lián)網(wǎng)發(fā)展的今天,生產(chǎn)級別的服務(wù),不可能再使用單機版的了,基本都是微服務(wù)+分布式那一套,如果還使用MyBatis默認(rèn)的緩存策略,顯然是行不通的,為了解決這個問題,分布式緩存應(yīng)運而生。

二、MyBatis中使用分布式緩存

2.1、基本思路

(1)自定義緩存實現(xiàn)Cache接口;

(2)在xxxMapper.xml中開啟二級緩存時指明緩存的類型;

2.2、代碼實戰(zhàn)

2.2.1、項目概覽

2.2.2、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>

	<!-- 數(shù)據(jù)源 -->
	<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.2.3、yml

server:
  port: 9999

spring:
  redis:
    host: xxxx
    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.2.4、MyRedisConfig

/**
 * @Author : 一葉浮萍?xì)w大海
 * @Date: 2023/12/10 15:28
 * @Description:
 */
@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;
    }

}

2.2.5、MyRedisCache

/**
 * @Author : 一葉浮萍?xì)w大海
 * @Date: 2023/12/10 15:30
 * @Description:
 */
public class MyRedisCache implements Cache {

    /**
     * id為mapper中的namespace
     */
    private final String id;

    private RedisTemplate getRedisTemplate() {
        RedisTemplate redisTemplate = (RedisTemplate) MyApplicationContextAware.getBean("redisTemplate");
        return redisTemplate;
    }



    /**
     * 必須存在構(gòu)造方法
     *
     * @param id
     */
    public MyRedisCache(String id) {
        System.out.println("RedisCache id============>" + id);
        this.id = id;
    }

    /**
     * 返回Cache的唯一標(biāo)識
     *
     * @return
     */
    @Override
    public String getId() {
        return this.id;
    }

    /**
     * 往Redis緩存中存儲數(shù)據(jù)
     * @param key
     * @param value
     */
    @Override
    public void putObject(Object key, Object value) {
        System.out.println("putObject key : " + key);
        System.out.println("putObject value : " + value);
        getRedisTemplate().opsForHash().put(Convert.toStr(id),key2MD5(Convert.toStr(key)),value);
    }

    /**
     * 從Redis緩存中取數(shù)據(jù)
     * @param key
     * @return
     */
    @Override
    public Object getObject(Object key) {
        System.out.println("getObject key : " + key);

        return getRedisTemplate().opsForHash().get(Convert.toStr(id),key2MD5(Convert.toStr(key)));
    }

    /**
     * 主要事項:這個方法為MyBatis的保留方法,默認(rèn)沒有實現(xiàn),后續(xù)版本可能會實現(xiàn)
     * @param key
     * @return
     */
    @Override
    public Object removeObject(Object key) {
        System.out.println("removeObject key(根據(jù)指定Key刪除緩存) : " + key);
        return null;
    }

    /**
     * 只要執(zhí)行了增刪改操作都會執(zhí)行清空緩存的操作
     */
    @Override
    public void clear() {
        System.out.println("清空緩存");
        getRedisTemplate().delete(Convert.toStr(id));
    }

    /**
     * 計算緩存數(shù)量
     * @return
     */
    @Override
    public int getSize() {
        Long size = getRedisTemplate().opsForHash().size(Convert.toStr(id));
        return size.intValue();
    }

    /**
     * 將Key進(jìn)行MD5加密
     * @param key
     * @return
     */
    private String key2MD5(String key) {
        return DigestUtils.md5DigestAsHex(key.getBytes(StandardCharsets.UTF_8));
    }
}

2.2.6、DepartmentDO

/**
 * @Author : 一葉浮萍?xì)w大海
 * @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.2.7、DepartmentMapper

/**
 * @Author : 一葉浮萍?xì)w大海
 * @Date: 2023/12/10 12:50
 * @Description:
 */
public interface DepartmentMapper {

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

}

2.2.8、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">

    <!-- 開啟基于Redis的二級緩存 -->
    <cache type="org.star.cache.MyRedisCache"/>

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

</mapper>

2.2.9、DepartmentMapperTest

/**
 * @Author : 一葉浮萍?xì)w大海
 * @Date: 2023/12/10 12:51
 * @Description:
 */
@SpringBootTest
public class DepartmentMapperTest {

    @Autowired
    private DepartmentMapper departmentMapper;

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

}

2.3、存在的問題

2.3.1、問題說明

項目中如果某個業(yè)務(wù)涉及到的查詢僅僅是單表查詢,即類似上述的查詢,這樣使用分布式緩存一點問題沒有,但是當(dāng)有多張表關(guān)聯(lián)查詢時,將會出現(xiàn)問題。會出現(xiàn)什么問題呢?假設(shè)當(dāng)前有兩個持久化類,它們具有一對一的關(guān)聯(lián)關(guān)系,例如員工 & 部門,從員工的角度看一個員工屬于一個部門,部門表查詢會緩存一條數(shù)據(jù),員工表查詢時也會緩存一條數(shù)據(jù),下次再查詢時將不會從DB中查詢了,而是從緩存中取,那么當(dāng)員工表中執(zhí)行級聯(lián)更新(增、刪、改)時,將會清空員工對應(yīng)的緩存 & 更新DB中員工表和部門表的數(shù)據(jù),這個時候如果再次查詢部門表中的數(shù)據(jù),由于緩存中的數(shù)據(jù)還在,再次查詢時直接從緩存中取數(shù)據(jù)了,導(dǎo)致查詢到的數(shù)據(jù)(緩存中的數(shù)據(jù))和實際數(shù)據(jù)庫表中的數(shù)據(jù)不一致!案例演示(基于上邊的案例,增加員工信息):

2.3.2、EmployeeDO

/**
 * @Author : 一葉浮萍?xì)w大海
 * @Date: 2023/12/10 15:38
 * @Description:
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@ToString(callSuper = true)
public class EmployeeDO implements Serializable {
    /**
     * 員工編號
     */
    private Integer id;

    /**
     * 姓名
     */
    private String name;

    /**
     * 年齡
     */
    private Integer age;

    /**
     * 部門
     */
    private DepartmentDO department;

}

2.3.3、EmployeeMapper

public interface EmployeeMapper {

    /**
     * 查詢指定id員工的個人信息和部門信息
     * @param id
     * @return
     */
    EmployeeDO getDetail(Integer id);

    /**
     * 級聯(lián)更新員工信息(更新員工信息 & 部門信息)
     * @param param
     */
    void updateEmployeeCascade(EmployeeDO param);

}

2.3.4、EmployeeMapper.xml

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

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

    <!-- 開啟基于Redis的分布式緩存 -->
    <cache type="org.star.cache.MyRedisCache"/>

    <resultMap id="employeeDetail" type="org.star.entity.model.EmployeeDO">
        <id property="id" column="id"></id>
        <result property="name" column="name"></result>
        <result property="age" column="age"></result>
        <association property="department" javaType="org.star.entity.model.DepartmentDO">
            <id property="id" column="id"></id>
            <result property="departmentName" column="department_name"></result>
        </association>
    </resultMap>
    <select id="getDetail" resultMap="employeeDetail">
        select e.id, e.name,e.age, d.department_name
        from employee e,
             department d
        where e.department_id = d.id
          and e.id = #{id}
    </select>

    <delete id="updateEmployeeCascade">
        update employee e left join department d
        on e.department_id = d.id
        <set>
            <if test="name != null and name != ''">
                e.name = #{name},
            </if>
            <if test="age != null">
                e.age = #{age},
            </if>
            <if test="department.departmentName != null and department.departmentName != ''">
                d.department_name = #{department.departmentName}
            </if>
        </set>
        where e.id = #{id}
    </delete>

</mapper>

2.3.5、EmployeeMapperTest

/**
 * @Author : 一葉浮萍?xì)w大海
 * @Date: 2023/12/10 15:42
 * @Description:
 */
@SpringBootTest
public class EmployeeMapperTest {

    @Autowired
    private EmployeeMapper employeeMapper;

    @Autowired
    private DepartmentMapper departmentMapper;

    @Test
    public void listAllUserTest() {
        List<EmployeeDO> employeeDOS1 = employeeMapper.listAllEmployee();
        System.out.println("employeeDOS1 = " + employeeDOS1);
        List<EmployeeDO> employeeDOS2 = employeeMapper.listAllEmployee();
        System.out.println("employeeDOS2 = " + employeeDOS2);
    }

    @Test
    public void getUserByIdTest() {
        EmployeeDO employee1 = employeeMapper.getEmployeeById(2);
        System.out.println("employee1 ============> " + employee1);
        EmployeeDO employee2 = employeeMapper.getEmployeeById(2);
        System.out.println("employee2 ============> " + employee2);
    }

    @Test
    public void getDetailTest() {
        EmployeeDO employeeDO1 = employeeMapper.getDetail(2);
        System.out.println("employeeDO1 = " + employeeDO1);
        EmployeeDO employeeDO2 = employeeMapper.getDetail(2);
        System.out.println("employeeDO2 = " + employeeDO2);
    }

    @Test
    public void relationShipTest() {
        EmployeeDO employeeDO = employeeMapper.getDetail(2);
        System.out.println("employeeDO = " + employeeDO);
        List<DepartmentDO> departmentDOS = departmentMapper.listAllDepartment();
        System.out.println("departmentDOS = " + departmentDOS);
    }

    @Test
    public void updateEmployeeCascadeTest() {
        EmployeeDO employeeDO = new EmployeeDO()
                .setId(2)
                .setName("劉亦菲")
                .setAge(18)
                .setDepartment(
                        new DepartmentDO()
                                .setId(2)
                                .setDepartmentName("市場部")
                        );
        employeeMapper.updateEmployee(employeeDO);
    }

}

2.3.6、測試 

(1)執(zhí)行EmployeeMapperTest #getDetailTest

(2)執(zhí)行 DepartmentMapperTest #listAllDepartmentTest

(3)級聯(lián)更新 EmployeeMapperTest #updateEmployeeCascadeTest,將id為2的部門名稱改為市場部,執(zhí)行完此操作后,redis中員工相關(guān)的緩存將被清空;

(4)再次執(zhí)行DepartmentMapperTest #listAllDepartmentTest

結(jié)果分析:查詢到的數(shù)據(jù)和數(shù)據(jù)庫中的數(shù)據(jù)不符。

原因:

具有級聯(lián)關(guān)系的查詢,當(dāng)執(zhí)行級聯(lián)更新(增、刪、改)時將會觸發(fā)清空redis緩存,而清空緩存是按照mapper中配置的namespace進(jìn)行刪除的,導(dǎo)致被關(guān)聯(lián)的那一方即使DB中的數(shù)據(jù)被更新了,redis中對應(yīng)的緩存也不會被清空。     

2.3.7、解決方案

在級聯(lián)更新的xxxMapper.xml中使用<cache-ref type="xxx"/>進(jìn)行級聯(lián)清空緩存,如下:

<cache-ref namespace="org.star.mapper.DepartmentMapper"/>

到此這篇關(guān)于SpringBoot+MyBatis+Redis實現(xiàn)分布式緩存的文章就介紹到這了,更多相關(guān)SpringBoot MyBatis Redis分布式緩存內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot+Thymeleaf實現(xiàn)生成PDF文檔

    SpringBoot+Thymeleaf實現(xiàn)生成PDF文檔

    Thymeleaf是一個現(xiàn)代的服務(wù)器端?Java?模板引擎,適用于?Web?和獨立環(huán)境。Thymeleaf?的主要目標(biāo)是為您的開發(fā)工作流程帶來優(yōu)雅的自然模板,本文就來用它實現(xiàn)生成PDF,感興趣的可以了解一下
    2022-09-09
  • Java中JSON數(shù)據(jù)操作與處理全面指南

    Java中JSON數(shù)據(jù)操作與處理全面指南

    JSON(JavaScript Object Notation)是數(shù)據(jù)交換的輕量級格式,它以易于人類閱讀和機器解析的方式組織數(shù)據(jù),基于鍵值對,在Java開發(fā)中,處理JSON數(shù)據(jù)通常涉及使用Jackson、Gson、Fastjson等庫,本文給大家介紹JSON數(shù)據(jù)操作與處理全面指南,感興趣的朋友一起看看吧
    2024-10-10
  • Java中Arrays類和Collections類常用方法示例詳解

    Java中Arrays類和Collections類常用方法示例詳解

    本文總結(jié)了Java中Arrays和Collections類的常用方法,涵蓋數(shù)組填充、排序、搜索、復(fù)制、列表轉(zhuǎn)換等操作,幫助開發(fā)者高效處理集合與數(shù)組數(shù)據(jù),感興趣的朋友跟隨小編一起看看吧
    2025-07-07
  • Windows安裝Maven并配置環(huán)境變量

    Windows安裝Maven并配置環(huán)境變量

    Maven是一個基于Java的項目管理工具,可以管理項目的構(gòu)建、測試和部署,本文就來介紹一下Windows安裝Maven并配置環(huán)境變量,感興趣的可以了解一下
    2024-11-11
  • Java 獲取泛型的類型實例詳解

    Java 獲取泛型的類型實例詳解

    這篇文章主要介紹了Java 獲取泛型的類型實現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • SpringBoot整合Netty心跳機制過程詳解

    SpringBoot整合Netty心跳機制過程詳解

    這篇文章主要介紹了SpringBoot整合Netty心跳機制過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-02-02
  • Postman form-data、x-www-form-urlencoded的區(qū)別及說明

    Postman form-data、x-www-form-urlencoded的區(qū)別及說明

    這篇文章主要介紹了Postman form-data、x-www-form-urlencoded的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • IDEA插件之mybatisx插件使用教程(超詳細(xì)!)

    IDEA插件之mybatisx插件使用教程(超詳細(xì)!)

    MybatisX 是一款基于IDEA的快速開發(fā)插件,為效率而生,下面這篇文章主要給大家介紹了關(guān)于IDEA插件之mybatisx插件使用的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-06-06
  • IDEA編譯時報常量字符串過長的解決辦法

    IDEA編譯時報常量字符串過長的解決辦法

    本文主要介紹了IDEA編譯時報常量字符串過長的解決辦法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • SpringBoot如何讀取resources目錄下的文件

    SpringBoot如何讀取resources目錄下的文件

    這篇文章主要介紹了SpringBoot如何讀取resources目錄下的文件問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07

最新評論

全南县| 南阳市| 保康县| 新郑市| 花垣县| 井冈山市| 平阳县| 定结县| 金湖县| 泗水县| 盐源县| 马山县| 博爱县| 长泰县| 将乐县| 金华市| 铁岭县| 德惠市| 临潭县| 吐鲁番市| 阿合奇县| 乐至县| 焦作市| 和林格尔县| 两当县| 华阴市| 祁阳县| 安溪县| 清涧县| 杨浦区| 马公市| 汝城县| 称多县| 盘山县| 措美县| 将乐县| 凤山县| 平定县| 西林县| 宁陕县| 平遥县|