1. 程式人生 > >在Spring Boot中使用資料快取

在Spring Boot中使用資料快取

春節就要到了,在回家之前要趕快把今年欠下的技術債還清。so,今天繼續。Spring Boot前面已經預熱了n篇部落格了,今天我們來繼續看如何在Spring Boot中解決資料快取問題。本篇部落格是以初識在Spring Boot中使用JPA為基礎的,先了解如何實現資料訪問,然後才好實現資料快取。OK,對於Spring Boot尚有疑問的小夥伴可以先移步這裡從SpringMVC到Spring Boot,老司機請略過。
OK,廢話不多說,開始今天的技術之旅吧。
在實際開發中,對於要反覆讀寫的資料,最好的處理方式是將之在記憶體中快取一份,頻繁的資料庫訪問會造成程式效率低下,同時記憶體的讀寫速度本身就要強於硬碟。Spring在這一方面給我們提供了諸多的處理手段,而Spring Boot又將這些處理方式進一步簡化,接下來我們就來看看如何在Spring Boot中解決資料快取問題。

建立Project並新增資料庫驅動

Spring Boot的建立方式還是和我們前文提到的建立方式一樣,不同的是這裡選擇新增的依賴不同,這裡我們新增Web、Cache和JPA依賴,如下圖:
這裡寫圖片描述
建立成功之後,接下來新增資料庫驅動,我還是使用MySql,在pom.xml中新增資料庫驅動,如下:

<dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version
>
5.1.40</version> </dependency>

配置application.properties

這個application.properties的配置還是和初識在Spring Boot中使用JPA一樣,各個引數的含義我這裡也不再贅述,我們直接來看程式碼:

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/sang?useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root spring.datasource.password=sang spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true spring.jackson.serialization.indent_output=true

建立實體類

@Entity
public class Person {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private String address;
    private Integer age;

    public Person() {
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Person(Long id, String name, String address, Integer age) {
        this.id = id;
        this.name = name;
        this.address = address;
        this.age = age;
    }
}

建立實體類的Repository

public interface PersonRepository extends JpaRepository<Person,Long> {
}

建立業務類

業務介面

public interface DemoService {
    public Person save(Person person);

    public void remove(Long id);

    public Person findOne(Person person);
}

實現類

@Service
public class DemoServiceImpl implements DemoService {
    @Autowired
    PersonRepository personRepository;

    @CachePut(value = "people", key = "#person.id")
    @Override
    public Person save(Person person) {
        Person p = personRepository.save(person);
        System.out.println("為id、key為" + p.getId() + "資料做了快取");
        return p;
    }

    @CacheEvict(value = "people")
    @Override
    public void remove(Long id) {
        System.out.println("刪除了id、key為" + id + "的資料快取");
        personRepository.delete(id);
    }

    @Cacheable(value = "people", key = "#person.id")
    @Override
    public Person findOne(Person person) {
        Person p = personRepository.findOne(person.getId());
        System.out.println("為id、key為" + p.getId() + "資料做了快取");
        return p;
    }
}@Service
public class DemoServiceImpl implements DemoService {
    @Autowired
    PersonRepository personRepository;

    @CachePut(value = "people", key = "#person.id")
    @Override
    public Person save(Person person) {
        Person p = personRepository.save(person);
        System.out.println("為id、key為" + p.getId() + "資料做了快取");
        return p;
    }

    @CacheEvict(value = "people")
    @Override
    public void remove(Long id) {
        System.out.println("刪除了id、key為" + id + "的資料快取");
        personRepository.delete(id);
    }

    @Cacheable(value = "people", key = "#person.id")
    @Override
    public Person findOne(Person person) {
        Person p = personRepository.findOne(person.getId());
        System.out.println("為id、key為" + p.getId() + "資料做了快取");
        return p;
    }
}

關於這個實現類我說如下幾點:

[email protected]表示快取新新增的資料或者更新的資料到快取中,兩個引數value表示快取的名稱為people,key表示快取的key為person的id
[email protected]表示從快取people中刪除key為id的資料
[email protected]表示新增資料到快取中,快取名稱為people,快取key為person的id屬性。

建立Controller

@RestController
public class CacheController {
    @Autowired
    DemoService demoService;

    @RequestMapping("/put")
    public Person put(Person person) {
        return demoService.save(person);
    }

    @RequestMapping("/able")
    public Person cacheable(Person person) {
        return demoService.findOne(person);
    }

    @RequestMapping("/evit")
    public String evit(Long id) {
        demoService.remove(id);
        return "ok";
    }
}

OK ,做完這一切我們就可以來測試我們剛剛寫的快取了。

測試

看我們的Controller,我們有三個地址要測試,一個一個來。當然,在 測試之前,我們先來看看初始狀態下的資料庫是什麼樣子的:
這裡寫圖片描述

首先我們在瀏覽器中訪問http://localhost:8080/able?id=1,得到如下訪問結果:
這裡寫圖片描述
這個時候檢視控制檯,輸出內容如下:
這裡寫圖片描述
說是資料已經被快取了,這個時候我們再繼續在瀏覽器中重新整理繼續請求id為1的資料,會發現控制檯不會繼續列印日誌出來,就是因為資料已被存於快取之中了。

接下來我們向瀏覽器中輸入http://localhost:8080/put?age=47&name=奧巴牛&address=米國,訪問結果如下:
這裡寫圖片描述
這個時候檢視控制檯列印的日誌如下:
這裡寫圖片描述
再檢視資料表,資料已插入成功:
這裡寫圖片描述
此時,我們在瀏覽器中輸入http://localhost:8080/able?id=106,訪問剛剛插入的這條資料,結果如下:
這裡寫圖片描述
這個時候檢視控制檯,發現並沒有資料資料,就是因為資料已經處於快取中了。

最後我們在瀏覽器中輸入http://localhost:8080/evit?id=106,將資料從快取中移除,訪問結果如下:
這裡寫圖片描述
這個時候檢視控制檯,已經提示快取移除掉了:
這裡寫圖片描述
同時資料也從資料庫刪除掉了,這個時候如果還需要該資料則需要我們繼續向表中新增資料。

快取技術切換

Spring Boot預設情況下使用ConcurrentMapCacheManager作為快取技術,有的時候你可能想替換為其他的快取方式,在Spring Boot中進行快取的切換非常簡單,我這裡以Google提供的Guava為例,如果要使用這種快取策略,只需要新增相應的依賴即可,如下:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>20.0</version>
</dependency>

就這樣就可以了。實際上在Spring Boot中,底層使用哪一種快取我們並不必做過多考慮,切換的方式也很簡單,如上文引入相應的依賴即可,我們只需要把上層的邏輯寫好即可。

以上。

參考資料:

《JavaEE開發的顛覆者 Spring Boot實戰》第八章