在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實戰》第八章