欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

一、緩存介紹

1.1、概述

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

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

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

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

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

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

二、MyBatis中使用分布式緩存

2.1、基本思路

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

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

2.2、代碼實(shí)戰(zhàn)

2.2.1、項(xiàng)目概覽

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)識(shí)
     *
     * @return
     */
    @Override
    public String getId() {
        return this.id;
    }

    /**
     * 往Redis緩存中存儲(chǔ)數(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)));
    }

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

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

    /**
     * 計(jì)算緩存數(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 {
    /**
     * 編號(hào)
     */
    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的二級(jí)緩存 -->
    <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、問題說明

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

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

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

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

}

2.3.3、EmployeeMapper

public interface EmployeeMapper {

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

    /**
     * 級(jí)聯(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("市場(chǎng)部")
                        );
        employeeMapper.updateEmployee(employeeDO);
    }

}

2.3.6、測(cè)試 

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

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

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

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

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

原因:

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

2.3.7、解決方案

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

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

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

相關(guān)文章

  • 詳解java注解相關(guān)知識(shí)

    詳解java注解相關(guān)知識(shí)

    今天給大家?guī)淼氖顷P(guān)于Java的相關(guān)知識(shí),文章圍繞著java注解的使用展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • springsecurity第三方授權(quán)認(rèn)證的項(xiàng)目實(shí)踐

    springsecurity第三方授權(quán)認(rèn)證的項(xiàng)目實(shí)踐

    Spring security 是一個(gè)強(qiáng)大的和高度可定制的身份驗(yàn)證和訪問控制框架,本文主要介紹了springsecurity第三方授權(quán)認(rèn)證的項(xiàng)目實(shí)踐,具有一定的參考價(jià)值,感興趣可以了解一下
    2023-08-08
  • java使用Abobe Acrobat DC生成模板

    java使用Abobe Acrobat DC生成模板

    這篇文章主要介紹了java使用Abobe Acrobat DC生成模板,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • 深入理解Maven環(huán)境搭建和介紹

    深入理解Maven環(huán)境搭建和介紹

    這篇文章主要介紹了深入理解Maven環(huán)境搭建和介紹,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-12-12
  • Mybatis?List列表In查詢實(shí)現(xiàn)的注意事項(xiàng)說明

    Mybatis?List列表In查詢實(shí)現(xiàn)的注意事項(xiàng)說明

    這篇文章主要介紹了Mybatis?List列表In查詢實(shí)現(xiàn)的注意事項(xiàng)說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • spring-boot 多線程并發(fā)定時(shí)任務(wù)的解決方案

    spring-boot 多線程并發(fā)定時(shí)任務(wù)的解決方案

    這篇文章主要介紹了spring-boot 多線程并發(fā)定時(shí)任務(wù)的解決方案,需要的朋友可以參考下
    2019-08-08
  • Java Yml格式轉(zhuǎn)換為Properties問題

    Java Yml格式轉(zhuǎn)換為Properties問題

    本文介紹了作者編寫一個(gè)Java工具類來解決在線YAML到Properties轉(zhuǎn)換時(shí)屬性內(nèi)容遺漏的問題,通過遍歷YAML文件的樹結(jié)構(gòu),作者成功實(shí)現(xiàn)了屬性的完整轉(zhuǎn)換,總結(jié)指出,該工具類適用于多種數(shù)據(jù)類型,并且代碼簡(jiǎn)潔易懂
    2024-12-12
  • Java之String.format()方法案例講解

    Java之String.format()方法案例講解

    這篇文章主要介紹了Java之String.format()方法案例講解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • 解決@PostConstruct注解導(dǎo)致的程序無(wú)法啟動(dòng)(@PostConstruct的執(zhí)行)

    解決@PostConstruct注解導(dǎo)致的程序無(wú)法啟動(dòng)(@PostConstruct的執(zhí)行)

    這篇文章主要介紹了解決@PostConstruct注解導(dǎo)致的程序無(wú)法啟動(dòng)(@PostConstruct的執(zhí)行)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • JDBC Template基本使用方法詳解

    JDBC Template基本使用方法詳解

    這篇文章主要介紹了JDBC Template基本使用方法詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06

最新評(píng)論