Spring-cacheを使用してデータベースクエリを改善しています。これは次のように正常に機能します。
_@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("books");
}
@Cacheable("books")
public Book getByIsbn(String isbn) {
return dao.findByIsbn(isbn);
}
_
しかし今、私は起動時に本のキャッシュ全体を事前投入したいと思います。つまり、dao.findAll()
を呼び出して、すべての値をキャッシュに入れます。このルーチンは定期的にのみスケジュールされます。
しかし、_@Cacheable
_を使用する場合、どのように明示的にキャッシュにデータを入力できますか?
前と同じようにキャッシュを使用し、スケジューラを追加してキャッシュを更新します。コードスニペットは以下のとおりです。
@Service
public class CacheScheduler {
@Autowired
BookDao bookDao;
@Autowired
CacheManager cacheManager;
@PostConstruct
public void init() {
update();
scheduleUpdateAsync();
}
public void update() {
for (Book book : bookDao.findAll()) {
cacheManager.getCache("books").put(book.getIsbn(), book);
}
}
}
KeyGenerator
が1つのパラメーターのオブジェクトを返すことを確認してください(デフォルト)。または、putToCache
のBookService
メソッドを公開して、cacheManagerを直接使用しないようにします。
@CachePut(value = "books", key = "#book.isbn")
public Book putToCache(Book book) {
return book;
}
@PostConstructを使用すると、次の問題が発生しました。もう一度呼び出した後でのみ。
これは、@ PostConstructが何かをキャッシュするには早すぎるためです。 (少なくとも私はそれが問題だったと思います)
起動プロセスの後半で使用していますが、問題なく動作します。
@Component
public class CacheInit implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
//call service method
}
}
起動時にBookのすべてのインスタンスをメモリに保持する必要がある場合は、それらをいくつかのバッファに自分で保存する必要があります。 findAll()メソッドを使用してそれらをキャッシュに入れると、findAll()に@Cacheableアノテーションを付ける必要があります。次に、起動時にfindAll()を呼び出す必要があります。ただし、これは、getAllIsbn(String isbn)を呼び出すと、findAll()を呼び出すときに対応するインスタンスがキャッシュに入れられていても、キャッシュにアクセスするという意味ではありません。実際には、ehcacheはメソッドの戻り値を、メソッドが呼び出されたときにキーが計算されるキー/値のペアとしてキャッシュするためです。したがって、findAll()の戻り値とgetByIsbn(String)の戻り値をどのように一致させることができるかはわかりません。なぜなら、返される型が同じではなく、さらにすべてのインスタンスでキーが一致することはないからです。
オプションは、起動時にキャッシュを生成するためにCommandLineRunner
を使用することです。
公式のCommandLineRunnerドキュメントから、それは次のとおりです。
BeanがSpringApplicationに含まれている場合、Beanがrunであることを示すために使用されるインターフェイス。
したがって、利用可能なすべての書籍のリストを取得し、CacheManager
を使用して、書籍のキャッシュにデータを入力するだけです。
@Component
public class ApplicationRunner implements CommandLineRunner {
@Autowired
private BookDao dao;
@Autowired
private CacheManager cacheManager;
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("books");
}
@Override
public void run(String... args) throws Exception {
List<Book> results = dao.findAll();
results.forEach(book ->
cacheManager.getCache("books").put(book.getId(), book));
}
}
Olivierが指定したように、Springは関数の出力を単一のオブジェクトとしてキャッシュするため、findAllで@cacheable表記を使用すると、後で個別にアクセスできるようにすべてのオブジェクトをキャッシュにロードできません。
キャッシュ内のすべてのオブジェクトをロードする1つの可能な方法は、使用されているキャッシングソリューションが起動時にすべてのオブジェクトをロードする方法を提供する場合です。たとえば、 NCache / TayzGrid のようなソリューションは、キャッシュスタートアップローダー機能を提供します。これにより、構成可能なキャッシュスタートアップローダーを使用して、起動時にオブジェクトにキャッシュをロードできます。
別のBean BookCacheInitialzerを追加する
BookCacheInitialzerで現在のBean BookServiceを自動配線する
bookCacheInitialzer擬似コードのPostConstructメソッド内
次に、次のようなことができます
class BookService {
@Cacheable("books")
public Book getByIsbn(String isbn) {
return dao.findByIsbn(isbn);
}
public List<Book> books;
@Cacheable("books")
public Book getByIsbnFromExistngBooks(String isbn) {
return searchBook(isbn, books);
}
}
class BookCacheInitialzer {
@Autowired
BookService service
@PostConstruct
public void initialize() {
books = dao.findAll();
service.books = books;
for(Book book:books) {
service.getByIsbnFromExistngBooks(book.getIsbn());
}
}
}