트랜잭션과 락
1. 트랜잭션과 격리 수준
- 트랜잭션은 ACID라 하는 원자성, 일관성, 격리성 그리고 지속성을 보장해야 함
- 원자성 (Atomicity): 트랜잭션 낸 실행한 작업들은 마치 하나의 작업인 것처럼 모두 성공하든가 모두 실패해야 함
- 일관성 (Consistency): 모든 트랜잭션은 일관성 있는 데이터베이스 상태를 유지해야 함 i.g. DB에서 정한 무결성 제약 조건을 항상 만족해야 함
- 격리성 (Isolation): 동시에 실행되는 트랜잭션들이 서로에게 영향을 미치지 않도록 격리해야 함, 격리성은 동시성과 관련된 성능 이슈로 인해 격리 수준을 선택할 수 있음
- 지속성 (Durability): 트랜잭션을 성공적으로 끝내면 해당 결과가 항상 기록되어 있어야 함, 중간에 시스템 문제가 발생하더라도 DB 로그 등을 사용해서 성공한 트랜잭션 내용을 복구해야 함
- 트랜잭션은 기본적으로 원자성, 일관성 그리고 지속성을 보장함
- 문제는 격리성인데 트랜잭션 간에 격리성을 완벽히 보장하기 위해서는 트랜잭션을 거의 차례대로 실행해야 함
- 트랜잭션을 차례대로 실행하면 성능이 매우 나빠짐
- 이런 문제로 인해 ANSI 표준은 트랜잭션의 격리 수준을 네 단계로 나누어 정의함
- 트랜잭션 격리 수준은 다음과 같음
- READ UNCOMMITTED (커밋되지 않은 읽기)
- READ COMMITTED (커밋된 읽기)
- REPEATABLE READ (반복 가능한 읽기)
- SERIALIZABLE (직렬화 가능)
- 순서대로 READ UNCOMMITED 격리 수준이 가장 낮고 SERIALIZABLLE의 격리 수준이 가장 높음
- 격리 수준이 낮을수록 동시성은 증가하지만
- 격리 수준에 따른 다양한 문제가 발생함
- 격리 수준에 따른 문제점은 다음과 같음
- DIRTY READ
- NON-REPEATABLE READ
- PHANTOM READ
- 격리 수준이 낮을수록 더 많은 문제가 발생하며 트랜잭션 격리 수준에 따른 문제점은 다음과 같음
- READ UNCOMMITTED: 커밋하지 않은 데이터를 읽을 수 있음 i.g. 트랜잭션1이 데이터를 수정하고 있는데 커밋하지 않아도 트랜잭션2가 수정 중인 데이터를 조회할 수 있으며 이를 DIRTY READ라 함, 트랜잭션2가 DIRTY READ 한 데이터를 사용하는데 트랜잭션1을 롤백하면 데이터 정합성에 심각한 문제가 발생할 수 있음
- READ COMMITTED: 커밋한 데이터만 읽을 수 있으므로 DIRTY READ가 발생하지 않지만 NON-REPEATABLE READ는 발생할 수 있음 i.g. 트랜잭션1이 회원A를 조회 중인데 갑자기 트랜잭션2가 회원A를 수정하고 커밋하면 트랜잭션1이 다시 회원A를 조회했을 때 수정된 데이터가 조회됨, 이처럼 반복해서 같은 데이터를 읽을 수 없는 상태를 NON-REPEATABLE READ라 함
- REPEATABLE READ: 한 번 조회한 데이터를 반복해서 조회해도 같은 데이터가 조회되지만 PHANTOM READ가 발생할 수 있음 i.g. 트랜잭션1이 10살 이하의 회원을 조회했는데 트랜잭션2가 5살 회원을 추가하고 커밋하면 트랜잭션1이 다시 10살 이하의 회원을 조회했을 때 회원 하나가 추가된 상태로 조회됨, 이처럼 반복 조회 시 결과 집합이 달라지는 것을 PHANTOM READ라 함
- SERIALIZABLE: 가장 엄격한 트랜잭션 격리 수준으로 PHANTOM_READ 조차 발생하지 않지만 동시성 처리 성능이 급격히 떨어질 수 있음

- 애플리케이션 대부분은 동시성 처리가 중요하므로 DB들은 보통 READ COMMITTED 격리 수준을 디폴트로 사용함
- 일부 중요한 비즈니스 로직에 더 높은 격리 수준이 필요할 경우 DB 트랜잭션이 제공하는 Lock 기능을 사용하면 됨
2. 낙관적 락과 비관적 락 기초
- JPA의 1차 캐시인 영속성 컨텍스트를 적절히 활용하면 DB 트랜잭션이 READ COMMITTED 겨리 수준이어도 애플리케이션 레벨에서 REPEATABLE READ가 가능함
- 물론 엔티티가 아닌 스칼라 값을 직접 조회할 경우 영속성 컨텍스트의 관리를 받지 못하므로 반복 가능한 읽기를 할 수 없음
- JPA는 DB 트랜잭션 격리 수준을 READ COMMITTED 정도로 가정함
- 만약 일부 로직에 더 높은 격리 수준이 필요할 경우 낙관적 락과 비관적 락 중 하나를 사용하면 됨
- 낙관적 락: DB가 제공하는 락 기능을 사용하는 것이 아니라 JPA가 제공하는 버전 관리 기능을 사용하며 트랜잭션을 커밋하기 전까지는 트랜잭션의 충돌을 알 수 없다는 특징이 있음
- 비관적 락: 트랜잭션의 충돌이 발생한다고 가정하고 우선 락을 걸고 보는 방법으로 DB가 제공하는 락 기능을 사용 (SELECT FOR UPDATE)
- 추가로 `두 번의 갱신 분실 문제 (second lost updates problem)`과 같이 DB 트랜잭션 범위를 넘어서는 문제도 있음
- i.g. 사용자 A와 B가 동시에 수정 화면을 열어서 내용을 수정하는 중 사용자 A가 먼저 수정완료 버튼을 눌렀고 사용자 B가 수정완료 버튼을 눌렀다고 가정했을 때 결과적으로 먼저 완료한 사용자 A의 수정사항은 사라지고 나중에 완료한 사용자 B의 수정사항만 남게 되는 문제
- 두 번의 갱신 분실 문제는 DB 트랜잭션의 범위를 넘어서므로 트랜잭션만으로는 문제를 해결할 수 없고 다음 세 가지 방법으로 해결해야 함
- 마지막 커밋만 인정하기: 사용자 A의 내용은 무시하고 마지막에 커밋한 사용자 B의 내용만 인정 (default)
- 최초 커밋만 인정하기: 사용자 A가 이미 수정을 완료했으므로 사용자 B가 수정을 완료할 때 오류가 발생함
- 충돌하는 갱신 내용 병합하기: 사용자 A와 사용자 B의 수정사항을 병합
- 기본은 `마지막 커밋만 인정하기`이지만 상황에 따라 `최초 커밋만 인정하기`가 더 합리적일 수 있고 JPA가 제공하는 버전 관리 기능을 사용하면 손쉽게 최초 커밋만 인정하기를 구현 가능
- `충돌하는 갱신 내용 병합하기`는 `최초 커밋만 인정하기`를 조금 더 우아하게 처리하는 방법이고 애플리케이션 개발자가 직접 사용자를 위해 병합 방법을 제공해야 함
3. @Version
- JPA가 제공하는 낙관적 락을 사용하려면 @Version 어노테이션을 사용해서 버전 관리 기능을 추가해야 함
- @Version 적용 가능 타입은 다음과 같음
- Long (long)
- Integer (int)
- Short (short)
- Timestamp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Entity | |
public class Board { | |
@Id | |
private String id; | |
private String title; | |
@Version | |
private Integer version; | |
} |
부연 설명
- 버전 관리 기능을 적용하려면 엔티티에 버전 관리용 필드를 하나 추가하고 @Version을 붙이면 됨
- 이제부터 엔티티를 수정할 때마다 버전이 하나씩 자동으로 증가하고 엔티티를 수정할 때 조회 시점의 버전과 수정 시점의 버전이 다르면 예외가 발생함
- ex) 트랜잭션1이 조회한 엔티티를 수정하고 있는데 트랜잭션2에서 같은 엔티티를 수정하고 커밋해서 버전이 증가해 버리면 트랜잭션1이 커밋할 때 버전 정보가 다르므로 예외가 발생함
- 버전 정보를 사용하면 `최초 커밋만 인정하기`가 적용됨
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 트랜잭션 1 조회 title="제목A", version=1 | |
Board board = em.find(Board.class, id); | |
// 이 시점에 트랜잭션 2에서 해당 게시물을 수정해서 title="제목C", version=2로 증가 | |
board.setTitle("제목B"); // 트랜잭션1 데이터 수정 | |
save(board); | |
tx.commit(); // 예외 발생, 데이터베이스 version=2, 엔티티 version=1 |

3.1 버전 정보 비교 방법
- 엔티티를 수정하고 트랜잭션을 커밋하면 영속성 컨텍스트를 flush 하면서 UPDATE 쿼리를 실행하고 이때 버전을 사용하는 엔티티면 검색 조건에 엔티티의 버전 정보를 추가함
- DB 버전과 엔티티 버전이 같은면 데이터를 수정하면서 동시에 버전도 하나 증가시킴
- 만약 DB 버전이 이미 증가해서 수정 중인 엔티티의 버전과 다르면 UPDATE 쿼리의 WHERE 문에서 VERSION 값이 다르므로 수정할 대상이 없어져 JPA에서 예외를 발생시킴
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
UPDATE | |
BOARD | |
SET | |
TITLE = ?, | |
VERSION = [버전 + 1 증가] | |
WHERE | |
ID = ? | |
AND | |
VERSION = [버전 비교] |
- 다시 한 번 강조하자면 버전은 엔티티의 값을 변경하면 증가함
- 값 타입인 임베디드 타입과 값 타입 컬렉션은 노리적인 개념상 해당 엔티티의 값이므로 수정하면 엔티티의 버전이 증가함
- 단, 연관관계 필드는 외래 키를 관리하는 연관관계의 주인 필드를 수정할 때만 버전이 증가함
- JPA가 자동으로 관리하는 @Version 애노테이션 필드는 벌크 연산의 경우를 제외하고, 개발자가 임의로 수정해서는 안 됨
- 만약 버전 값을 강제로 증가하려면 특별한 락 옵션을 선택하면 됨
4. JPA 락 사용
- 락은 다음 위치에 적용할 수 있음
- EntityManager.lock(), EntityManager.find(), EntityManager.refresh()
- Query.setLockMode()
- @NamedQuery
- 조회하면서 즉시 락을 걸 수도 있고 필요할 때 락을 걸 수도 있음
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 조회하면서 즉시 락을 거는 코드 | |
Board board = em.find(Board.class, id, LockModeType.OPTIMISTIC); | |
// 필요할 때 락을 거는 코드 | |
Board board = em.find(Board.class, id); | |
em.lock(board, LockModeType.OPTIMISTIC); |
- JPA가 제공하는 락 옵션은 javax.persistence.LockModeType에 정의되어 있음

5. JPA 낙관적 락
- JPA가 제공하는 낙관적 락은 @Version을 사용함
- 낙관적 락을 사용하려면 버전이 있어야 함
- 낙관적 락은 트랜잭션을 커밋하는 시점에 충돌을 알 수 있다는 특징이 있음
- 낙관적 락에서 발생하는 예외는 다음과 같음
- JPA 예외: javax.persistence.OptimisticLockException
- 하이버네이트 예외: org.hibernate.StaleObjectStateException
- 스프링 예외 추상화: org.springframework.orm.ObjectOptimisticLockingFailureException
5.1 낙관적 락 옵션: NONE
- 락 옵션을 적용하지 않아도 엔티티에 @Version이 적용된 필드만 있으면 낙관적 락이 적용됨
- 용도: 조회한 엔티티를 수정할 때 다른 트랜잭션에 의해 변경되지 않아야 하며 조회 시점부터 수정 시점까지를 보장함
- 동작: 엔티티를 수정할 때 버전을 체크하면서 버전을 증가시키며 이때 DB의 버전 값이 현재 버전이 아니면 예외 발생시킴
- 이점: 두 번의 갱신 분실 문제를 예방함
5.2 낙관적 락 옵션: OPTIMISTIC
- @Version만 적용했을 때는 엔티티를 수정해야 버전을 체크하지만 해당 옵션을 추가하면 엔티티를 조회만 해도 버전을 체크함
- 용도: 조회한 엔티티는 트랜잭션이 끝날 때까지 다른 트랜잭션에 의해 변경되지 않아야 하며 조회 시점부터 트랜잭션이 끝날 때까지 조회한 엔티티가 변경되지 않음을 보장
- 동작: 트랜잭션을 커밋할 때 버전 정보를 조회해서 현재 엔티티의 버전과 같은지 검증하며 만약 같지 않을 경우 예외를 발생시킴
- 이점: OPTIMISTIC 옵션은 DIRTY READ와 NON_REPEATABLE READ를 방지함
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 트랜잭션 1 조회 title="제목A", version=1 | |
Board board = em.find(Board.class, id, LockModeType.OPTIMISTIC); | |
// 증간에 트랜잭션 2에서 해당 게시물을 수정해서 title="제목C", version=2로 증가 | |
// 트랜잭션 1 커밋 시점에 버전 정보 검증, 예외 발생 | |
// 데이터베이스 version=2, 엔티티 version=1 | |
tx.commit(); |

5.3 낙관적 락 옵션: OPTIMISTIC_FORCE_INCREMENT
- 낙관적 락을 사용하면서 버전 정보를 강제로 증가시킴
- 용도: 논리적 단위의 엔티티 묶음을 관리할 때, 예를 들어 게시물과 첨부파일이 일대다 및 다대일 양방향 연관관계에서 첨부파일이 관계의 주인인 경우 게시물을 수정하면서 첨부파일만 추가하면 물리적으로는 변경되지 않아 게시물 버전이 증가하지 않지만 논리적으로는 변경된 것으로 간주되므로 게시물의 버전을 강제로 증가시키려면 OPTIMISTIC_FORCE_INCREMENT를 사용하면 됨
- 동작: 엔티티를 수정하지 않아도 트랜잭션을 커밋할 때 UPDATE 쿼리를 사용해서 버전 정보를 강제로 증가시키며 이때 DB의 버전이 엔티티의 버전과 다르면 예외가 발생함
- 엔티티를 수정할 때도 UPDATE가 발생하므로 총 두 번의 버전 증가가 발생할 수 있음
- 이점: 강제로 버전을 증가시켜 논리적인 단위의 엔티티 묶음을 대상으로 버전 관리 가능
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 트랜잭션 1 조회 title="제목A", version=1 | |
Board board = em.find(Board.class, id, LockModeType.OPTIMISTIC_FORCE_INCREMENT); | |
// 트랜잭션 1 커밋 시점에 버전 강제 증가 | |
tx.commit(); |

6. JPA 비관적 락
- JPA가 제공하는 비관적 락은 DB 트랜잭션 락 메커니즘에 의존하는 방법
- 주로 SQL 쿼리에 SELECT FOR UPDATE 구문을 사용하면서 시작하고 버전 정보는 사용하지 않음
- 비관적 락은 주로 PESSIMISTIC_WRITE 모드를 사용
- 비관적 락은 다음과 같은 특징이 있음
- 엔티티가 아닌 스칼라 타입을 조회할 때도 사용 가능
- 데이터를 수정하는 즉시 트랜잭션 충돌 감지 가능
- 비관적 락에서 발생하는 예외는 다음과 같음
- JPA 예외: javax.persistence.PessimisticLockException
- 스프링 예외 추상화: org.springframework.dao.PessimisticLockingFailureException
6.1 비관적 락 옵션: PESSIMISTIC_WRITE
- 일반적으로 해당 옵션을 사용하며 DB에 쓰기 락을 걸 때 사용
- 용도: DB에 쓰기 락을 부여
- 동작: 데이터베이스 SELECT FOR UPDATE를 사용해서 락을 검
- 이점: NON-REPEATABLE READ를 방지하며 락이 걸린 로우는 다른 트랜잭션이 수정할 수 없음
6.2 비관적 락 옵션: PESSIMISTIC_READ
- 데이터를 반복 읽기만 하고 수정하지 않는 용도로 락을 걸 때 사용
- 일반적으로 사용하지 않음
6.3 비관적 락 옵션: PESSIMISTIC_FORCE_INCREMENT
- 비관적 락 중 유이할게 버전 정보를 사용
- 비관적 락이지만 버전 정보를 강제로 증가시킴
- 하이버네이트는 nowait를 지원하는 DB에 대해서 FOR UPDATE NOWAIT 옵션을 적용
7. 비관적 락과 타임아웃
- 비관적 락을 사용하면 락을 획득할 때까지 트랜잭션이 대기하지만 무한정 기다릴 수는 없으므로 타임아웃 시간을 줄 수 있음
- 타임아웃이 발생하면 javax.persistence.LockTimeoutException 예외가 발생함
- 단, 타임아웃은 DB 특성에 따라 동작하지 않을 수 있음
2차 캐시
1. 1차 캐시와 2차 캐시
- 네트워크를 통해 DB에 접근하는 시간 비용은 애플리케이션 서버에서 내부 메모리에 접근하는 시간 비용보다 수만에서 수십만 배 이상 비쌈
- 따라서 조회한 데이터를 메모리에 캐싱해서DB 접근 횟수를 줄이면 애플리케이션 성능을 획기적으로 개선할 수 있음
- 영속성 컨텍스트 내부에는 엔티티를 보관하는 저장소가 있는데 이것을 1차 캐시라 지칭
- 일반적인 웹 애플리케이션 환경은 트랜잭션을 시작하고 종료할 때까지만 1차 캐시가 유효함
- OSIV를 사용해도 클라이언트의 요청이 들어올 때부터 끝날 때까지만 1차 캐시가 유효함
- 따라서 애플리케이션 전체 라이프 사이클 관점에서 보면 DB 접근 횟수를 획기적으로 줄이지는 못 함
- 하이버네이트를 포함한 대부분의 JPA 구현체들은 애플리케이션 범위의 캐시를 지원하는데 이것을 공유 캐시 또는 2차 캐시라 지칭
- 2차 캐시를 활용하면 애플리케이션 조회 성능을 향상할 수 있음

1.1 1차 캐시
- 1차 캐시는 영속성 컨텍스트 내부에 있으며 엔티티 매니저로 조회하거나 변경한느 모든 엔티티는 1차 캐시에 저장됨
- 트랜잭션을 커밋하거나 flush를 호출하면 1차 캐시에 있는 엔티티의 변경 내역을 DB에 동기화 시킴
- JPA를 스프링 프레임워크 같은 컨테이너 위에서 실행하면 트랜잭션을 시작할 때 영속성 컨텍스트를 생성하고 트랜잭션을 종료할 때 영속성 컨텍스트도 종료시킴
- OSIV를 사용하면 HTTP 요청의 시작부터 끝까지 같은 영속성 컨텍스트를 유지함

- 정리하면 1차 캐시의 특징은 다음과 같음
- 1차 캐시는 같은 엔티티가 있으면 해당 엔티티를 그대로 반환함 (1차 캐시는 객체 동일성을 보장)
- 1차 캐시는 기본적으로 영속성 컨텍스트 범위의 캐시, OSIV를 적용하면 HTTP 요청 범위의 캐시
1.2 2차 캐시
- 애플리케이션에서 공유하는 캐시를 JPA는 공유 캐시 (Shared Cache)라 하는데 일반적으로 2차 캐시 (Second Level Cache, L2 Cache)라 부름
- 2차 캐시는 애플리케이션 범위의 캐시이며 애플리케이션을 종료할 때까지 캐시가 유지됨
- 분산 캐시나 클러스터 환경의 캐시는 애플리케이션보다 더 오래 유지될 수도 있음
- 2차 캐시를 적용하면 엔티티 매니저를 통해 데이터를 조회할 때 우선 2차 캐시에서 조회하고 없으면 DB에서 찾기 때문에 적절히 활용하면 DB 조회 횟수를 획기적으로 줄일 수 있음

- 2차 캐시는 동시성을 극대화하기 위해 캐싱한 객체를 직접 반환하지 않고 복사본을 생성해서 반환시킴
- 만약 캐싱한 객체를 그대로 반환하면 여러 곳에서 같은 객체를 동시에 수정하는 문제가 발생할 수 있음
- 위 문제를 해결하기 위해 락을 걸 수도 있지만 동시성이 떨어지므로 객체 복사본을 반환시킴
- 정리하면 2차 캐시의 특징은 다음과 같음
- 2차 캐시는 영속성 유닛 범위의 캐시
- 2차 캐시는 조회한 객체를 그대로 반환하는 것이 아니라 복사본을 생성해서 반환
- 2차 캐시는 DB 기본 키를 기준으로 캐싱하지만 영속성 컨텍스트가 다르면 객체 동일성을 보장하지 않음
2. JPA 2차 캐시 기능
- JPA 구현체 대부분은 캐시 기능을 각자 지원했는데 JPA는 2.0에 와서야 캐시 표준을 정의했음
- JPA 캐시 표준은 여러 구현체가 공통으로 사용하는 부분만 표준화해서 세밀한 설정을 하려면 구현체에 의존적인 기능을 사용해야 함
2.1 캐시 모드 설정
- 2차 캐시를 사용하려면 엔티티에 javax.persistence.Cacheable 어노테이션을 사용하면 됨
- @Cacheable은 @Cacheable(true), @Cacheable(false)를 설정할 수 있으며 default 값은 true
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// application.properties에 다음과 같이 설정 | |
spring.jpa.properties.jakarta.persistence.sharedCache.mode=ENABLE_SELECTIVE | |
@Entity | |
@Cacheable | |
public class Member { | |
@Id | |
@GeneratedValue | |
private Long id; | |
// 중략 | |
} |

2.2 캐시 조회, 저장 방식 설정
- 캐시를 무시하고 DB를 직접 조회하거나 캐시를 갱신하려면 캐시 조회 모드와 캐시 보관 모드를 사용하면 됨
- em.setProperty("javax.persistence.cache.retrieveMode", CacheRetrieveMode.BYPASS);
- 캐시 조회 모드나 보관 모드에 따라 사용할 프로퍼티와 옵션이 상이함
- 프로퍼티 이름은 다음과 같음
- 캐시 조회 모드 프로퍼티명: javax.persistence.cache.retrieveMode
- 캐시 보관 모드 프로퍼티명: javax.persistence.cache.storeMode
- 옵션은 다음과 같음
- 캐시 조회 모드 설정 옵션: javax.persistence.CacheRetrieveMode
- 캐시 보관 모드 설정 옵션: javax.persistence.CacheStoreMode
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 캐시 조회 모드 | |
public enum CacheRetrieveMode { | |
USE, // 캐시에서 조회 (default) | |
BYPASS // 캐시를 무시하고 DB에 직접 접근 | |
} | |
// 캐시 보관 모드 | |
public enum CacheStoreMode { | |
// 조회한 데이터를 캐시에 저장하며 이미 캐시에 있을 경우 캐시 데이터를 최신 상태로 갱신하지 않음 | |
// 트랜잭션을 커밋하면 등록 수정한 엔티티도 캐시에 저장함 (default) | |
USE, | |
// 캐시에 저장하지 않음 | |
BYPASS, | |
// 전략에 추가로 DB에서 조회한 엔티티르 최신 상태로 다시 캐싱 | |
REFRESH | |
} | |
// JPQL | |
em.createQuery("SELECT e FROM TestEntity e WHERE e.id = :id", TestEntity.class) | |
.setParameter("id", id) | |
.setHint("javax.persistence.cache.retrieveMode", CacheRetrieveMode.BYPASS) | |
.setHint("javax.persistence.cache.storeMode", CacheStoreMode.BYPASS) | |
.getSingleResult(); |
2.3 JPA 캐시 관리 API
- JPA는 캐시를 관리하기 위한 javax.persistence.Cache 인터페이스를 제공하며 이것은 EntityManagerFactory에서 구할 수 있음
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Cache 관리 객체 조회 | |
Cache cache = emf.getCache(); | |
boolean contains = cache.contains(TestEntity.class, testEntity.getId()); | |
log.info("contains = {}", contains); | |
// Cache 인터페이스 | |
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); | |
} |
참고
자바 ORM 표준 JPA 프로그래밍 - 김영한 저
반응형
'DB > 자바 ORM 표준 JPA 프로그래밍' 카테고리의 다른 글
[15장] 고급 주제와 성능 최적화 (0) | 2025.03.22 |
---|---|
[14장] 컬렉션과 부가 기능 (0) | 2025.03.19 |
[13장] 웹 애플리케이션과 영속성 관리 (0) | 2025.03.18 |
[12장] 스프링 데이터 JPA (0) | 2025.03.15 |
[10장] 객체지향 쿼리 언어 (0) | 2025.03.09 |