JPA 의 구조
JPA 는 기본적으로 데이터베이스에 있는 테이블들을 객체로 가져오는 ORM 이다.
ORM 이란? 객체와 관계형 데이터베이스의 데이터를 자동으로 매핑(연결)해주는 것을 말한다.
하지만 좀더 깊게 공부해보고 1차캐시 2차캐시 라는것을 알게 되었다.
JPA의 1차캐시, 2차캐시
먼저 1차캐시에 대한 설명을 하자면, 1차캐시는 영속성 컨텍스트 에서 엔티티를 자동으로 관리하게 된다.
영속성 컨텍스트가 엔티티를 메모리에 보관하는 공간으로 같은 객체가 있다면 동일성이 보장 된다.
좀 더 찾아보니 우리가 별도의 설정을 안해도 EntityManager 인터페이스에서 관리를 하고 있다.
2차캐시에 대한 설명이다.
1차 캐시와는 다르게 우리가 별도로 설정을 해줘여 된다. ( 캐시 전략, 수명, 공유 범위 등 ) 만약 DB 에 A 가 있다면 조회를 처음할 때 2차 캐시에 저장을 하고 가져오고 다음에 똑같은 A 를 조회하게 되면 2차캐시에서 가져오게 된다.
즉, 조회할때 2차캐시에 없으면 DB 에서 가져오고 2차캐시에 조회한 결과가 있다면 DB 가 아닌 2차캐시에 가져오게 된다.
+ 문득 드는 생각
만약 2차 캐시에 저장되어 있는데 데이터가 바뀌어서 조회 데이터가 다르지 않을까? 라고 생각이 들어서 찾아보았다.
먼저 데이터가 바뀌게 되면 2차캐시에 있는 데이터도 업데이트를 해야 한다고 한다.
그 방법은
1. DB 에서 데이터를 변경하는 모든 작업을 캐시로 전파하는 방식이 있다. 또한, 변경 작업이 발생되면 2차 캐시에 저장되어 있는 데이터를 제거하거나 업데이트를 하여 2차캐시와 DB 의 데이터 일관성을 유지 시킨다.
2. JPA 구현체가 제공하는 캐시 동기화 기능을 사용하는 방식이 있다. Hibernate 의 경우 @Cache 라는 어노테이션으로 캐시 설정을 지정 하면 데이터가 변경시 자동으로 2차캐시를 업데이트 해준다.
2차 캐시 주의 사항
2차 캐시는 데이터베이스의 변경 작업이 수행되기 전까지는 최신 데이터를 보장하지 못한다고 한다.. 따라서 데이터의 실시간 업데이트가 중요한 경우에는 2차 캐시를 사용하는 대신 캐시를 비활성화하거나 적절한 캐시 전략을 선택하는 것이
좋을것 같다.
이를 통해
이렇게 찾아보니 @Transactional 트랜잭션에 대해 좀더 알게 되었다. 바로 1차캐시에서 대기 하고 있다가 만약 잘못될 경우 모두 다 취소 시키는것을 처리하는곳이 1차 캐시 이다.
좀더 찾아보니 영속성 컨텍스트 ?
영속성 컨텍스트라고 찾아보니 지속성을 관리하는 논리적인 작업 영역이라고 한다. 즉 Entity 를 관리하는 것 같다.
DB 에 등록하기전 영속성 컨텍스트 즉 1차캐시에 저장되는 쪽되고 @Transactional 도 1차캐시에서 처리 하는것 같다.
주요특징은
엔티티 관리, 지연 로딩( Lazy ), 변경 감지, 캐싱, 트랜잭션 관리 가 있다.
중요한것은 JpaRepository 에 있는 save() 를 쓰게 되면 바로 DB 에 저장이 되는것이 아닌 1차캐시에 저장이 되고
commit 이나 플러시 호출 하는 구간이 오면 그때 DB 에 저장되는것이다.
좀좀 더더 찾아보니?
여기서 살짝 의문이 들었다. 그렇다면 JpaRepository 를 쓰지않고 영속성 컨텍스트로 커스텀 해서 쓰면 더 좋지 않을까? 라는 생각을 들지만 JpaRepository 에서 제공해주는 매서드로도 기본적인것들을 해결할수 있으니 상관 없겠지만 복잡한 쿼리문을 짜야 한다면 영속성 컨텍스트로 커스텀해서 써야 될것 같다. 라고 생각하여 찾아보니 이렇게 쓴다고 한다.
영속성 컨텍스트에 접근을 할려면?
@PersistenceContext
private EntityManager entityManager;
이런식으로 코드를 추가하여 접근하여 쓸수 있다 아래는 인터페이스의 매서드들이다.
persist() | 주어진 엔티티를 영속성 컨텍스트에 등록하고, 데이터베이스에 저장하기 위해 대기 상태로 만듭니다. |
find() | 주어진 엔티티 클래스와 기본 키를 사용하여 영속성 컨텍스트 또는 데이터베이스에서 엔티티를 조회합니다. |
merge() | 주어진 엔티티를 영속성 컨텍스트에 병합하고, 변경 내용을 추적하여 데이터베이스에 반영합니다. |
remove() | 주어진 엔티티를 영속성 컨텍스트와 데이터베이스에서 삭제합니다. 만약 엔티티가 영속성 컨텍스트에 없으면, 데이터베이스에서 삭제합니다. |
이제 1차캐시에 있는 엔티티들을 DB 에 저장 하고 싶다면 밑에 매서드를 통해 DB 에 저장 할수 있다.
entityManager.getTransaction().begin(); // 트랜잭션 시작
entityManager.persist(entity); // 엔티티를 영속성 컨텍스트에 등록
entityManager.getTransaction().commit(); // 트랜잭션 커밋
여기서 의문점..
왜 트랜잭션을 시작하고 저장을 할까? 라는 의문이 들었다. 그래서 찾아보니 안정 보장하기 위해서 트랜잭션을 쓴다고 한다. 아래는 찾아본 내용이다. 글을 보니 완전 이해가 된다.
JPA에서는 데이터베이스 작업을 보다 일괄적이고 안전한 방식으로 처리하기 위해 트랜잭션을 사용하는 것을
권장합니다. 트랜잭션은 데이터베이스 작업의 원자성, 일관성, 격리성, 지속성을 보장하기 위한 핵심 개념입니다.
트랜잭션을 사용하지 않고 엔티티를 바로 저장하는 것은 가능하지만, 이는 일관성과 지속성을 보장하기
어렵게 만들 수 있습니다. 트랜잭션을 사용하지 않으면 여러 개의 데이터베이스 작업이 개별적으로 실행되고
커밋되므로, 이러한 작업들이 일관성을 유지하지 못하거나 중간에 오류가 발생할 경우 롤백이 어렵게 됩니다.
또한, 트랜잭션은 동시성 제어를 위한 격리 수준을 설정할 수 있어 데이터의 일관성과 동시성을
관리할 수 있습니다. 트랜잭션을 사용하면 여러 개의 작업이 동시에 실행될 때, 데이터의 일관성을 유지하고
충돌을 방지할 수 있습니다.
따라서, JPA에서는 일반적으로 엔티티를 저장하려면 트랜잭션을 시작하고 엔티티를 영속성 컨텍스트에 등록한 후, 트랜잭션을 커밋하여 데이터베이스에 저장하는 방식을 권장합니다. 이를 통해 데이터의 일관성과 지속성을
보장할 수 있습니다.
'Spring' 카테고리의 다른 글
JPA Dirty Checking (0) | 2023.06.17 |
---|---|
Hibernate (0) | 2023.06.13 |
@Component / @ComponentScan (0) | 2023.06.13 |
Request 와 Response (4) | 2023.06.09 |
Spring Security (2) (0) | 2023.06.08 |