28 Temmuz 2020 Salı

SpringCache @Cacheable Anotasyonu - Select, Find Metodları İçin Kullanılır

Giriş
Şu satırı dahil ederiz.
import org.springframework.cache.annotation.Cacheable;
Kullanım
- Repository sınıflarına eklenebilir.
- Cache'lenmesini istediğimiz nesne veya metodlara @Cacheable anotasyonu eklenir.
- Eğer nesnenin belli bir isme sahip cache alanına eklenmesini istersek @CacheConfig anotasyonu ile cache ismi belirtilir.

Örnek - Repository
Şöyle yaparız.
public interface FooRepository extends JpaRepository<Foo, BigInteger> {

  @Query("from foo where active=true and expiryDate > CURRENT_DATE order by priority")
  @Cacheable(cacheNames = "CACHE_NAME_FOO_TYPES")
  List<Foo> getFoos();

}
Örnek - Service Sınıfı
Eğer service sınıfımız bir hesaplama yapıyorsa bazen service koduna da eklenebilir. Şöyle yaparız
// BookService.java
@Service
public class BookService {
  @Cacheable("books")
  public String getBookNameByIsbn(String isbn) {
    return findBookInSlowSource(isbn);
  }

  private String findBookInSlowSource(String isbn) {
    // some long processing
    try {
      Thread.sleep(3000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    return "Sample Book Name";
  }
} 
@Cacheable sadece public getter metodlara Eklenir
@Cacheable sadece public getter() metodlara uygulanır. Açıklaması şöyle
When using proxies, you should apply the @Cache* annotations only to methods with public visibility. If you do annotate protected, private or package-visible methods with these annotations, no error is raised, but the annotated method does not exhibit the configured caching settings.
getter() metodu bir bean içinden tetiklenmelidir. Örnekte myOtherMethod() çağrısı getString()'i çağırsa bile sonuç cachelenmez.
public class MyBean {

  @Cacheable
  public String getString(int i) {
    return Integer.toString(i);
  }
  public void myOtherMethod() {
    String myString = getString(2);
  }
}
TTL Yoktur
Bu anotasyon TTL değerini desteklemez. Altta kullanılan Cache Provider'ın sağladığı değerler kullanılır. Açıklaması şöyle.
Spring @Cacheable does not have any configurable option to set TTL for the cache though you can build it using @CacheEvict and @Scheduled,
Açıklaması şöyle.
Spring is pretty clear about TTL/TTI (Expiration) and Eviction policies as explained in the core Spring Framework Reference Guide here. In other words, the "defaults" depend entirely on the underlying data store (a.k.a. caching provider) used with the Spring Boot app via the Spring Cache Abstraction.
SpringCache Redis Kullanımı yazısına TTL örnekleri var

cacheNames Alanı
İsmi belirtilen cache'e yazar ve okur

Örnek
Şöyle yaparız. value aslında cacheNames için alias olarak tanımlanmıştır. Yani şu iki kod denktir.
@Cacheable(value="employees")
public Employee findById(int id) {
  // some code
}

@Cacheable(cachNames="employees")
public Employee findById(int id) {
  // some code
}
cacheManager Alanı
Açıklaması şöyle. Birden fazla cache gerçekleştirimi varsa birini belirtmek için kullanılır
It specifies the name of cache manager. It is used define your own cache manager and do not want to use spring’s default cache manager.
Örnek
Şöyle yaparız
@Cacheable(value="employees", cacheManager="customCacheManager")
public Employee findByName(String name) {
  // some code
}
cacheResolver Alanı
Birden fazla cache gerçekleştirimi varsa birini belirtmek için kullanılır
Örnek
Şöyle yaparız
@Configuration
class MyCachingConfiguration {

  @Bean
  CacheResolver customCacheResolver() {
    // return your custom CacheResolver implementation
  }
}

@Service
class CustomerService {

  @Cacheable(cacheNames = "Customers", cacheResolver="customCacheResolver")
  public Customer findBy(String name) {
    // ...
  }
}
condition Alanı
Açıklaması şöyle.. Koşul true ise nesne cachlenir.
We can apply a condition in the attribute by using the condition attribute. We can call it as conditional caching.

For example, the following method will be cached if the argument name has length less than 20.
Örnek
Şöyle yaparız
@Cacheable(value="employees", condition="#name.length < 20")
public Employee findByName(String name) {
  // some code
}
Örnek
Şöyle yaparız
@Cacheable(value="students", condition="#student.age < 24")
public Student findStudent(Student student)

key Alanı

CacheManager içinde indeks olarak kullanılacak değeri belirtir. İki şekilde kullanılabilir.
1. Spring key değerini kendi hesaplar.
2. Spring Expression Language ile kullanılır.

Açıklaması şöyle
This is the key with which object will be cached. It uniquely identifies each entry in the cache. If we do not specify the key then Spring uses the default mechanism to create the key.

Örnek  - SPEL
Şöyle yaparız.
Cacheable(key = "#pageable.pageNumber")
public Person getPersons(Pageable pageable) 
Örnek - SPEL
Şöyle yaparız.
@Cacheable(value="passwords", key="#params.id")//any unique identifier
public Collection<Password> getPasswords(PasswordSearchParameters params) {
  ...
  return em.createQuery(hql, Password.class);  
}
Örnek - SPEL
Şöyle yaparız.
@Cacheable(value="ceepCache", key = "#assignee")
public String getEntitlement(String assignee) throws IOException {
  ...
}
Örnek - SPEL
Şöyle yaparız
@Override
@Cacheable(value = "usersList", key = "#page")
public List<User> allUsers(Integer page) {
  Page<User> users = userRepo.findAll(PageRequest.of(page, 5));
  if (users.isEmpty()) {
    throw new CustomException("Users not found", 400);
  }
  return users.getContent();
}
Örnek - 
SPEL key + condition
Spring Expression Language ve condition ile şöyle yaparız.
@Service
public class BootService {

  @Cacheable(value="prod",key="#listing_id",condition = "#listing_id < 3")
  public List<BootModel> getprodList(Long listing_id) {
    List <BootModel> list = ...;
    return list;
  }
keyGenerator Alanı
Açıklaması şöyle
It is used to define your own key generation mechanism. We need to create custom key generator class.
Örnek
Şöyle yaparız.
@RequestMapping(value = "/search")
@Cacheable(value = "halfHourCache", keyGenerator = "haodfKeyGenerator")
public ResponseEntity<String> search(BizData bizData, Page page, String sourceType)  {
  return new ResponseEntity<>(..., HttpStatus.OK);
}
unless Alanı
#result ile birlikte kullanılır.  Açıklaması şöyle
It specifies the object to to cached if it matches certain condition. SpEL provides a context variable #result which refers to the object that is fetched and we can apply condition on on its value.
Örnek
Şöyle yaparız
@Cacheable(value="employees", unless="#result.length < 20") public Employee findByName(String name) { // some code }
Örnek
Şöyle yaparız
@Cacheable(cacheNames = "customer", key = "#id", unless = "#result == null")
public Customer getCustomerById(long id) {
  ...
}
Örnek
Şöyle yaparız
@Cacheable(value = "users", key = "#userId", unless = "#result.followers < 12000")
@RequestMapping(value = "/{userId}", method = RequestMethod.GET)
public User getUser(@PathVariable String userId) {
  ...
}
value Alanı
Birden fazla cache ismi belirtilebilir

Örnek
Şöyle yaparız
@Cacheable({"addresses", "directory"})
public String getAddress() {
  return "Adresses";
}

Örnek
Elimizde şöyle bir kod olsun.
@Bean
CacheManager cacheManager() {
  return new ConcurrentMapCacheManager("keyCache");
}
Şöyle yaparız.
@Cacheable("keyCache")
public String getKey(String param) {
  return "...";
}