web-dev-qa-db-ja.com

春の起動時に@キャッシュをロードするにはどうすればよいですか?

Spring-cacheを使用してデータベースクエリを改善しています。これは次のように正常に機能します。

_@Bean
public CacheManager cacheManager() {
    return new ConcurrentMapCacheManager("books");
}

@Cacheable("books")
public Book getByIsbn(String isbn) {
    return dao.findByIsbn(isbn);
}
_

しかし今、私は起動時に本のキャッシュ全体を事前投入したいと思います。つまり、dao.findAll()を呼び出して、すべての値をキャッシュに入れます。このルーチンは定期的にのみスケジュールされます。

しかし、_@Cacheable_を使用する場合、どのように明示的にキャッシュにデータを入力できますか?

14
membersound

前と同じようにキャッシュを使用し、スケジューラを追加してキャッシュを更新します。コードスニペットは以下のとおりです。

@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つのパラメーターのオブジェクトを返すことを確認してください(デフォルト)。または、putToCacheBookServiceメソッドを公開して、cacheManagerを直接使用しないようにします。

@CachePut(value = "books", key = "#book.isbn")
public Book putToCache(Book book) {
    return book;
}
18
Loki

@PostConstructを使用すると、次の問題が発生しました。もう一度呼び出した後でのみ。

これは、@ PostConstructが何かをキャッシュするには早すぎるためです。 (少なくとも私はそれが問題だったと思います)

起動プロセスの後半で使用していますが、問題なく動作します。

@Component
public class CacheInit implements ApplicationListener<ApplicationReadyEvent> {

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
       //call service method
    }

}
7
Andrea Calin

起動時にBookのすべてのインスタンスをメモリに保持する必要がある場合は、それらをいくつかのバッファに自分で保存する必要があります。 findAll()メソッドを使用してそれらをキャッシュに入れると、findAll()に@Cacheableアノテーションを付ける必要があります。次に、起動時にfindAll()を呼び出す必要があります。ただし、これは、getAllIsbn(String isbn)を呼び出すと、findAll()を呼び出すときに対応するインスタンスがキャッシュに入れられていても、キャッシュにアクセスするという意味ではありません。実際には、ehcacheはメソッドの戻り値を、メソッドが呼び出されたときにキーが計算されるキー/値のペアとしてキャッシュするためです。したがって、findAll()の戻り値とgetByIsbn(String)の戻り値をどのように一致させることができるかはわかりません。なぜなら、返される型が同じではなく、さらにすべてのインスタンスでキーが一致することはないからです。

3
Olivier Meurice

オプションは、起動時にキャッシュを生成するために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));
    }
}
2
mladzo

Olivierが指定したように、Springは関数の出力を単一のオブジェクトとしてキャッシュするため、findAllで@cacheable表記を使用すると、後で個別にアクセスできるようにすべてのオブジェクトをキャッシュにロードできません。

キャッシュ内のすべてのオブジェクトをロードする1つの可能な方法は、使用されているキャッシングソリューションが起動時にすべてのオブジェクトをロードする方法を提供する場合です。たとえば、 NCache / TayzGrid のようなソリューションは、キャッシュスタートアップローダー機能を提供します。これにより、構成可能なキャッシュスタートアップローダーを使用して、起動時にオブジェクトにキャッシュをロードできます。

1
Sameer Shah

別の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());
    }

}   

}

0