如何在spring中启动时加载@Cache ?

时间:2022-09-11 19:51:59

I'm using spring-cache to improve database queries, which works fine as follows:

我正在使用spring-cache来改进数据库查询,其工作原理如下:

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

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

But now I want to prepopulate the full book-cache on startup. Which means I want to call dao.findAll() and put all values into the cache. This routine shall than only be scheduled periodically.

但是现在我想在启动时预填充整个图书缓存。这意味着我要调用dao.findAll()并将所有值放入缓存中。此例程应定期安排。

But how can I explicit populate a cache when using @Cacheable?

但是,如何在使用@Cacheable时显式填充缓存呢?

5 个解决方案

#1


8  

Just use the cache as before, add a scheduler to update cache, code snippet is below.

就像以前一样使用缓存,添加一个调度器来更新缓存,代码片段如下。

@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);
        }
    }
}

Make sure your KeyGenerator will return the object for one parameter (as default). Or else, expose the putToCache method in BookService to avoid using cacheManager directly.

确保KeyGenerator返回一个参数的对象(默认值)。或者,在BookService中公开putToCache方法,以避免直接使用cacheManager。

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

#2


3  

If having all instances of Book in memory at startup is your requirement than you should store them in some buffer yourself. Putting them in the cache with the findAll() method means that you must annotate findAll() with @Cacheable. Then you would have to call findAll() at startup. But that does not mean that calling getByIsbn(String isbn) will access the cache even if the corresponding instance has been put in the cache when calling findAll(). Actually it won't because ehcache will cache method return value as a key/value pair where key is computed when method is called. Therefore I don't see how you could match the return value of findAll() and return value of getByIsbn(String) because returned types are not the same and moreover key won't never match for all your instances.

如果在启动时在内存中有所有的书的实例,那么您的要求就是要将它们存储在一些缓冲区中。使用findAll()方法将它们放到缓存中意味着必须使用@Cacheable对findAll()进行注释。然后必须在startup中调用findAll()。但这并不意味着调用getByIsbn(String isbn)将访问缓存,即使在调用findAll()时将相应的实例放入缓存中。实际上它不会,因为ehcache会将方法返回值缓存为一个键/值对,在调用方法时计算键。因此,我不知道如何匹配findAll()的返回值和getByIsbn(String)的返回值,因为返回的类型不相同,而且键永远不会匹配所有实例。

#3


2  

An option would be to use the CommandLineRunner for populating the cache on startup.

一个选项是使用CommandLineRunner在启动时填充缓存。

From official CommandLineRunner documentation, it is an:

从正式的CommandLineRunner文件来看,它是:

Interface used to indicate that a bean should run when it is contained within a SpringApplication.

接口,用于指示当bean包含在SpringApplication中时应该运行它。

Hence, we just need to retrieve the list of all available books and then, using CacheManager, we populate the book cache.

因此,我们只需要检索所有可用图书的列表,然后使用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));
    }
}

#4


1  

As Olivier has specified, since spring caches output of function as a single object, using @cacheable notation with findAll will not allow you to load all objects in cache such that they can later be accessed individually.

正如Olivier所指定的,由于spring将函数的输出缓存为单个对象,使用带有findAll的@cacheable表示法将不允许您加载缓存中的所有对象,以便以后可以单独访问它们。

One possible way you can load all objects in cache is if caching solution being used provides you a way to load all objects at startup. E.g solutions like NCache / TayzGrid provides Cache startup loader feature, that allows you to load cache at startup with objects using a configurable cache startup loader.

可以在缓存中加载所有对象的一种可能方式是,如果使用的缓存解决方案提供了在启动时加载所有对象的方法。E。像NCache / TayzGrid这样的g解决方案提供了缓存启动加载程序特性,允许您在启动时使用可配置的缓存启动加载程序加载对象。

#5


0  

Add another bean BookCacheInitialzer

添加另一个bean BookCacheInitialzer

Autowire the current bean BookService in BookCacheInitialzer

在BookCacheInitialzer中自动连接当前的bean BookService

in PostConstruct method of BookCacheInitialzer pseudo code

在BookCacheInitialzer伪码的后构造方法中

Then can do something like

然后可以做一些类似的事情

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

}   

}

}

#1


8  

Just use the cache as before, add a scheduler to update cache, code snippet is below.

就像以前一样使用缓存,添加一个调度器来更新缓存,代码片段如下。

@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);
        }
    }
}

Make sure your KeyGenerator will return the object for one parameter (as default). Or else, expose the putToCache method in BookService to avoid using cacheManager directly.

确保KeyGenerator返回一个参数的对象(默认值)。或者,在BookService中公开putToCache方法,以避免直接使用cacheManager。

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

#2


3  

If having all instances of Book in memory at startup is your requirement than you should store them in some buffer yourself. Putting them in the cache with the findAll() method means that you must annotate findAll() with @Cacheable. Then you would have to call findAll() at startup. But that does not mean that calling getByIsbn(String isbn) will access the cache even if the corresponding instance has been put in the cache when calling findAll(). Actually it won't because ehcache will cache method return value as a key/value pair where key is computed when method is called. Therefore I don't see how you could match the return value of findAll() and return value of getByIsbn(String) because returned types are not the same and moreover key won't never match for all your instances.

如果在启动时在内存中有所有的书的实例,那么您的要求就是要将它们存储在一些缓冲区中。使用findAll()方法将它们放到缓存中意味着必须使用@Cacheable对findAll()进行注释。然后必须在startup中调用findAll()。但这并不意味着调用getByIsbn(String isbn)将访问缓存,即使在调用findAll()时将相应的实例放入缓存中。实际上它不会,因为ehcache会将方法返回值缓存为一个键/值对,在调用方法时计算键。因此,我不知道如何匹配findAll()的返回值和getByIsbn(String)的返回值,因为返回的类型不相同,而且键永远不会匹配所有实例。

#3


2  

An option would be to use the CommandLineRunner for populating the cache on startup.

一个选项是使用CommandLineRunner在启动时填充缓存。

From official CommandLineRunner documentation, it is an:

从正式的CommandLineRunner文件来看,它是:

Interface used to indicate that a bean should run when it is contained within a SpringApplication.

接口,用于指示当bean包含在SpringApplication中时应该运行它。

Hence, we just need to retrieve the list of all available books and then, using CacheManager, we populate the book cache.

因此,我们只需要检索所有可用图书的列表,然后使用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));
    }
}

#4


1  

As Olivier has specified, since spring caches output of function as a single object, using @cacheable notation with findAll will not allow you to load all objects in cache such that they can later be accessed individually.

正如Olivier所指定的,由于spring将函数的输出缓存为单个对象,使用带有findAll的@cacheable表示法将不允许您加载缓存中的所有对象,以便以后可以单独访问它们。

One possible way you can load all objects in cache is if caching solution being used provides you a way to load all objects at startup. E.g solutions like NCache / TayzGrid provides Cache startup loader feature, that allows you to load cache at startup with objects using a configurable cache startup loader.

可以在缓存中加载所有对象的一种可能方式是,如果使用的缓存解决方案提供了在启动时加载所有对象的方法。E。像NCache / TayzGrid这样的g解决方案提供了缓存启动加载程序特性,允许您在启动时使用可配置的缓存启动加载程序加载对象。

#5


0  

Add another bean BookCacheInitialzer

添加另一个bean BookCacheInitialzer

Autowire the current bean BookService in BookCacheInitialzer

在BookCacheInitialzer中自动连接当前的bean BookService

in PostConstruct method of BookCacheInitialzer pseudo code

在BookCacheInitialzer伪码的后构造方法中

Then can do something like

然后可以做一些类似的事情

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

}   

}

}