JPA 2차 캐시 기능
JPA 구현체 대부분은 2차 캐시 기능을 각자 지원했는데 JPA는 2.0에 와서야 2차 캐시 표준을 정의했다.
JPA 2차 캐시 표준은 여러 구현체가 공통으로 사용하는 부분만 표준화해서 세밀한 설정을 하려면 구현체에 의존적인 기능을 사용해야 한다.
캐시 모드 설정
2차 캐시를 사용하려면 엔티티에 javax.persistence.Cacheable 어노테이션을 사용하면 된다. @Cacheable은 @Cacheable(true), @Cacheable(false)로 설정할 수 있는데 기본값은 true다.
@Cacheable // true
@Entity
public class Member {
...
}
다음과 같이 설정해서 원하는 패키지에만 캐시를 적용할 수 도 있다.
@Primary
@Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, DataSource dataSource) {
LocalContainerEntityManagerFactoryBean bean = builder.dataSource(dataSource).build();
bean.setPackagesToScan("패키지 경로");
bean.setPersistenceUnitName("영속성 컨텍스트 이름");
bean.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE);
return bean;
}
보통 ENABLE_SELECTIVE를 사용한다.
- ALL : 모든 엔티티를 캐시한다.
- NONE : 캐시를 사용하지 않는다.
- ENABLE_SELECTIVE : @Cacheable(true)로 설정한 엔티티만 캐시를 적용한다.
- DISABLE_SELECTIVE : 모든 엔티티를 캐시하는데 @Cacheable(false)로 명시된 엔티티는 제외한다.
- UNSPECIFIED : JPA 구현체가 정의한 설정을 따른다.
캐시 조회, 저장 방식 설정
캐시를 무시하고 데이터베이스를 직접 조회하거나 캐시를 갱신하려면 캐시 조회 모드와 캐시 보관 모드를 사용하면 된다.
캐시 조회 모드
public enum CacheRetrieveMode {
USE, // 캐시에서 조회한다. 기본값
BYPASS // 캐시를 무시하고 데이터베이스에 직접 접근한다.
}
캐시 보관 모드
public enum CacheStoreMode {
USE, // 조회한 데이터를 캐시에 저장
BYPASS, // 캐시에 저장하지 않는다.
REFRESH // USE 전략에 추가로 데이터베이스에서 조회한 엔티티를 최신화 한다.
}
CacheStoreMode.USE 사용 시 조회한 데이터가 이미 캐시에 있으면 캐시 데이터를 최신 상태로 갱신하지는 않는다.
트랜잭션을 커밋하면 등록 수정한 엔티티도 캐시에 저장된다.
// 캐시 조회 모드 설정
em.setProperty("javax.persistence.cache.retrieveMode", CacheRetrieveMode.BYPASS);
// 캐시 보관 모드 설정
em.setProperty("javax.persistence.cache.storeMode", CacheStoreMode.BYPASS);
EntityManager.setProperty()로 엔티티 매니저 단위로 설정하거나, 더 세밀하게 EntityManager.find(), EntityManager.refresh()에 설정할 수 도 있고 Query.setHint()에도 사용할 수 있다. (TypeQuery 포함)
// find()
Map<String, Object> param = new HashMap<>();
param.put("javax.persistence.cache.retrieveMode", CacheRetrieveMode.BYPASS);
param.put("javax.persistence.cache.storeMode", CacheStoreMode.BYPASS);
em.find(Member.class, id, param);
// Query hint
em.createQuery("select m from Member m where m.id = :id", Member.class)
.setParameter("id", id)
.setHint("javax.persistence.cache.retrieveMode", CacheRetrieveMode.BYPASS)
.setHint("javax.persistence.cache.storeMode", CacheStoreMode.BYPASS)
.getSingleResult();
JPA 캐시 관리 API
JPA는 캐시를 관리하기 위한 javax.persistence.Cache 인터페이스를 제공한다.
Cache cache = emf.getCache();
boolean contains = cache.contains(Member.class, member.getId());
public interface Cache {
// 해당 엔티티가 캐시에 존재 여부 반환
public boolean contains(Class cls, Object primaryKey);
// 해당 엔티티중 특정 식별자를 가진 엔티티를 캐시에서 제거
public void evict(Class cls, Object primaryKey);
// 해당 엔티티 전체를 캐시에서 제거
public void evict(Class cls);
// 모든 캐시 데이터 제거
public void evictAll();
// JPA Cache 구현체 조회
public <T> T unwrap(Class<T> cls);
}
JPA가 표준화한 캐시 기능은 여기까지다. 실제 캐시를 적용하려면 구현체의 설명서를 읽어보아야 한다.