在 Spring Boot 中停用 @Cacheable
一、簡介
快取是一種有效的策略,可以在執行結果在已知時間段內沒有改變(實際上相同)的情況下避免重複執行邏輯,從而提高效能。
Spring Boot提供了@Cacheable
註解,我們在方法上定義它,它會快取方法的結果。在某些場景下,例如在較低環境中進行測試時,我們可能需要停用快取來觀察某些修改後的行為。
在本文中,我們將在 Spring Boot 中配置緩存,並了解如何在需要時停用快取。
2. 快取設定
讓我們設定一個透過 ISBN 查詢書評的簡單用例,並使用@Cacheable
在某些邏輯中快取該方法。
我們的實體類別將是BookReview
類,其中包含rating
、 isbn
等:
@Entity
@Table(name="BOOK_REVIEWS")
public class BookReview {
@Id
@GeneratedValue(strategy= GenerationType.SEQUENCE, generator = "book_reviews_reviews_id_seq")
@SequenceGenerator(name = "book_reviews_reviews_id_seq", sequenceName = "book_reviews_reviews_id_seq", allocationSize = 1)
private Long reviewsId;
private String userId;
private String isbn;
private String bookRating;
// getters & setters
}
我們在BookRepository
中加入一個簡單的findByIsbn()
方法來透過isbn
查詢書評:
public interface BookRepository extends JpaRepository<BookReview, Long> {
List<BookReview> findByIsbn(String isbn);
}
BookReviewsLogic
類別包含一個呼叫 BookRepository 中的findByIsbn()
的方法BookRepository.
我們添加@Cacheable
註釋,它將給定isbn
的結果緩存在book_reviews
快取中:
@Service
public class BookReviewsLogic {
@Autowired
private BookRepository bookRepository;
@Cacheable(value = "book_reviews", key = "#isbn")
public List<BookReview> getBooksByIsbn(String isbn){
return bookRepository.findByIsbn(isbn);
}
}
由於我們在邏輯類別中使用@Cacheable
,因此我們需要配置快取。我們可以透過帶有@Configuration
和@EnableCaching
的註解配置類別來設定快取配置。在這裡,我們返回HashMap
作為我們的快取儲存:
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager();
}
}
我們已準備好快取設定。如果我們在BookReviewsLogic
中執行getBooksByIsbn()
,我們的結果將在第一次執行時被緩存,並且從那時起立即返回,而無需重新計算(即查詢資料庫),從而提高效能。
我們來寫一個簡單的測試來驗證一下:
@Test
public void givenCacheEnabled_whenLogicExecuted2ndTime_thenItDoesntQueriesDB(CapturedOutput output){
BookReview bookReview = insertBookReview();
String target = "Hibernate: select bookreview0_.reviews_id as reviews_1_0_, "
+ "bookreview0_.book_rating as book_rat2_0_, "
+ "bookreview0_.isbn as isbn3_0_, "
+ "bookreview0_.user_id as user_id4_0_ "
+ "from book_reviews bookreview0_ "
+ "where bookreview0_.isbn=?";
// 1st execution
bookReviewsLogic.getBooksByIsbn(bookReview.getIsbn());
String[] logs = output.toString()
.split("\\r?\\n");
assertThat(logs).anyMatch(e -> e.contains(target));
// 2nd execution
bookReviewsLogic.getBooksByIsbn(bookReview.getIsbn());
logs = output.toString()
.split("\\r?\\n");
long count = Arrays.stream(logs)
.filter(e -> e.equals(target))
.count();
// count 1 means the select query log from 1st execution.
assertEquals(1,count);
}
在上面的測試中,我們執行getBooksByIsbn()
兩次,捕獲日誌,並確認select
查詢僅執行一次,因為getBooksByIsbn()
方法在第二次執行時傳回快取結果。
若要為針對資料庫執行的查詢產生 SQL 日誌,我們可以在application.properties
檔案中設定下列屬性:
spring.jpa.show-sql=true
3. 禁用快取
為了停用緩存,我們將在application.properties
檔案中使用額外的自訂屬性(即appconfig.cache.enabled
) :
appconfig.cache.enabled=true
之後,我們可以在快取設定檔中讀取此配置並進行條件檢查:
@Bean
public CacheManager cacheManager(@Value("${appconfig.cache.enabled}") String isCacheEnabled) {
if (isCacheEnabled.equalsIgnoreCase("false")) {
return new NoOpCacheManager();
}
return new ConcurrentMapCacheManager();
}
正如我們在上面看到的,我們的邏輯檢查該屬性是否設定為停用快取。如果是這樣,我們可以傳回一個NoOpCacheManager,
它是一個不執行快取的快取管理器。否則,我們可以返回基於哈希的快取管理器。
透過上面的簡單設置,我們可以在 Spring Boot 應用程式中停用快取。讓我們透過一個簡單的測試來驗證上述設定。
首先,我們需要修改在application.properties.
對於我們的測試設置,我們可以使用@TestPropertySource
來覆蓋該屬性:
@SpringBootTest(classes = BookReviewApplication.class)
@ExtendWith(OutputCaptureExtension.class)
@TestPropertySource(properties = {
"appconfig.cache.enabled=false"
})
public class BookReviewsLogicCacheDisabledUnitTest {
// ...
}
現在,我們的測試將與之前類似,我們執行邏輯兩次。我們檢查 SQL 查詢日誌是否在目前測試中記錄了兩次,因為執行不會被快取:
long count = Arrays.stream(logs)
.filter(e -> e.contains(target))
.count();
// count 2 means the select query log from 1st and 2nd execution.
assertEquals(2, count);
4。結論
在本教程中,我們簡要介紹了 Spring Boot 中的緩存,然後在應用程式中設定緩存。我們還學習了當需要測試程式碼的某些部分時如何停用快取。此外,我們編寫了必要的測試來驗證啟用和停用快取的工作情況。
與往常一樣,範例程式碼可以在 GitHub 上取得。