2015-01-14 44 views
9

sto usando la primavera-cache per migliorare le query di database, che funziona bene come segue:Come caricare @Cache all'avvio in primavera?

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

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

Ma ora voglio precompilare il libro-cache piena all'avvio. Il che significa che voglio chiamare dao.findAll() e inserire tutti i valori nella cache. Questa routine deve essere pianificata solo periodicamente.

Ma come è possibile popolare compilare una cache quando si utilizza @Cacheable?

risposta

7

Basta usare la cache come prima, aggiungere uno scheduler per aggiornare la cache, lo snippet di codice è sotto.

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

Assicurati che il tuo KeyGenerator restituirà l'oggetto per un parametro (per default). Oppure, esporre il metodo putToCache in BookService per evitare di utilizzare direttamente cacheManager.

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

Se avere tutte le istanze di Book in memoria all'avvio è il tuo requisito, allora dovresti memorizzarle in qualche buffer. Inserirli nella cache con il metodo findAll() significa che è necessario annotare findAll() con @Cacheable. Quindi dovresti chiamare findAll() all'avvio. Ma ciò non significa che la chiamata a getByIsbn (String isbn) accederà alla cache anche se l'istanza corrispondente è stata inserita nella cache quando chiama findAll(). In realtà non sarà perché ehcache memorizzerà il valore restituito dal metodo come coppia chiave/valore in cui la chiave viene calcolata quando viene chiamato il metodo. Pertanto non vedo come si possa confrontare il valore di ritorno di findAll() e restituire il valore di getByIsbn (String) perché i tipi restituiti non sono gli stessi e inoltre la chiave non corrisponderà mai a tutte le istanze.

0

Aggiungere un altro bean BookCacheInitialzer

Autowire il Bookservice fagiolo attuale BookCacheInitialzer

nel metodo PostConstruct di BookCacheInitialzer pseudo codice

Poi si può fare qualcosa di simile

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

Sì, questa sarebbe un'opzione, MA molto pessima per le prestazioni poiché sto colpendo i n-tempi del DB durante l'avvio per ogni voce. Inoltre, è in qualche modo ridondante poiché ho già tutti i miei libri di findAll(). Quindi sto cercando un modo per ottenere questi libri nella cache senza un altro round di db. – membersound

+0

Quindi può fare qualcosa come –

+0

Modificato il soln di conseguenza. –

1

Come Olivier ha specificato, poiché le cache molla uscita di funzione come un singolo oggetto, utilizzando la notazione @cacheable con findAll non consente di caricare tutti gli oggetti nella cache tali da poter successivamente accedere singolarmente .

Un possibile modo per caricare tutti gli oggetti nella cache è se la soluzione di caching in uso fornisce un modo per caricare tutti gli oggetti all'avvio. Le soluzioni E.g come NCache/TayzGrid forniscono funzionalità di avvio della cache di Cache, che consente di caricare la cache all'avvio con oggetti utilizzando un caricatore di avvio della cache configurabile.

2

Un'opzione sarebbe quella di utilizzare il CommandLineRunner per popolare la cache all'avvio.

Dalla documentazione ufficiale CommandLineRunner, si tratta di un:

interfaccia utilizzato per indicare che un fagiolo dovrebbe corsa quando è contenuta all'interno di un SpringApplication.

Quindi, abbiamo solo bisogno di recuperare l'elenco di tutti i libri disponibili e quindi, usando CacheManager, compiliamo la cache del libro.

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