일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- Spring
- 배포
- vm
- 스프링 시큐리티
- CS
- Spring Security
- 스프링 부트
- ORM
- spring boot
- Java
- JPA
- HTTP
- 영속성 컨텍스트
- spring batch
- 자바
- computer science
- 스프링
- spring cloud
- 데이터베이스
- CI/CD
- 도커
- 가상화
- 웹 서버
- mysql
- web server
- 스프링 배치
- 컨테이너
- Container
- virtualization
- 백엔드
- Today
- Total
개발 일기
[섹션3] 영속성 관리 - 내부 동작 방식 본문
영속성 컨텍스트
엔티티 매니저 팩토리와 엔티티 매니저
위의 그림과 같이 EntityManagerFactory가 유저의 요청이 들어올때마다 EntityManager를 생성한고 EntityManger들은 내부적으로 데이터베이스 커넥션을 사용해서 DB를 사용하게 된다.
영속성 컨텍스트
JPA를 이해하는데 가장 중요한 용어 중 하나로 EntityManager.persist(entity);는 DB에 저장한다는 뜻이아니라 사실 영속성 컨텍스트에 저장한다는 의미이다. 영속성 컨텍스트는 논리적인 개념으로 눈에 보이지 않는다.
엔티티의 생명주기
//객체를 생성한 상태(비영속(new/trasient): 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태)
Member member = new Member();
member.setId("member1");
member.setUsername(“회원1”);
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
//객체를 저장한 상태(영속(managed): 영속성 컨텍스트에 관리되는 상태)
em.persist(member);
//회원 엔티티를 영속성 컨텍스트에서 분리(준영속 상태(detached): 영속성 컨텍스트에 저장되었다가 분리된 상태)
em.detach(member);
//객체를 삭제한 상태(삭제(removed): 삭제된 상태)
em.remove(member);
영속성 컨텍스트의 이점
• 1차 캐시
: 트랜잭션이 시작되고 em.persist();를 통해 A 객체를 저장하게 되면 바로 DB가 아닌 영속성 컨텍스트 1차 캐시에 저장이 된다. 그렇기 때문에 해당 객체를 em.find()를 통해 조회하면 JPA는 우선 DB가 아닌 영속성 컨텍스트의 1차 캐시를 먼저 조회한다는 특징이 있기 때문에 1차 캐시에 저장되어 있던 객체가 그대로 반환된다. 이 경우에 commit()시점에 추가적인 SELECT 쿼리는 발생하지 않는다.
그러나 이번엔 B 객체를 조회 하면 1차 캐시를 먼저 조회하고 없으면 그제서야 DB에서 조회하고 찾은 객체를 1차 캐시에 저장한 후에 반환한다. 이러한 경우에 DB 조회를 했기 때문에 SELECT 쿼리는 발생하게 된다.
그러나 한 트랜잭션 안에서 동작하는 캐싱이기 때문에 비즈니스 로직이 복잡하지 않은 이상은 큰 효과는 없다고 한다.
• 동일성(identity) 보장
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b); //동일성 비교 true
• 트랜잭션을 지원하는 쓰기 지연 (transactional write-behind)
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋
위와 같이 persist()하는 족족 바로 INSERT 쿼리가 날라가는 것이 아니라 1차 캐시에 저장됨고 동시에 쓰기 지연 SQL 저장소에 쌓이다가 commit()하는 시점에서야 실제 쿼리가 나가게 된다.
이런 쓰기 지연이 장점인 이유는 원래 INSERT 쿼리가 나갈때 마다 데이터베이스와의 네트워크 통신이 발생하지만 만약 쓰기 지연 SQL 저장소에 쿼리를 모았다가 hibernate.jdbc.batch_size라는 속성을 통해 설정 값 만큼의 쿼리를 한번의 네트워크 통신으로 처리함으로써 버퍼링 같은 기능을 제공해주기 때문에 성능을 잡을 수 있다.
• 변경 감지(Dirty Checking)
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [트랜잭션] 시작
// 영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");
// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);
//em.update(member) 이런 코드가 있어야 하지 않을까?
transaction.commit(); // [트랜잭션] 커밋
위 코드와 같이 setter를 통해 객체의 속성값을 수정했을 때 다시 persist()나 update()를 통해 저장해줘야한다는 생각을 할 수 있다. 그러나 이 JPA는 관계형 데이터베이스를 객체처럼 다루는 것이 목적이기 때문에 자동으로 처리된다. 어떻게 가능한 것인지 알아보자.
JPA는 commit()하는 시점에 엔티티와 스냅샷을 비교한다. 사실 1차 캐시에는 id, Entity, SnapShot을 들고 있는다. 여기서 Entity가 실제 객체고 스냅샷은 처음 영속성 컨텍스트에 들어왔을때의 처음 상태를 찍어 놓는 것이다. 그래서 추후에 로직이 진행된 후 커밋 시점에 최종 객체와 해당 스냅샷을 비교하고 뭔가 수정이 발생했다면 쓰기 지연 SQL 저장소에 UPDATE 쿼리를 만들어두고 이것을 반영하게 되는 것이다.
• 지연 로딩(Lazy Loading)
플러시(Flush)
플러시란?
영속성 컨텍스트의 변경내용을 데이터베이스에 반영하는 것이다.
그래서 플러시가 발생하면
1. 변경감지
2. 수정된 엔티티 쓰기 지연 SQL 저장소에 등록
3. 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송(등록, 수정, 삭제 쿼리)
플러시하는 방법
em.flush()를 통해 직접 호출하거나 트랜잭션 커밋 또는 JPQL 쿼리 실행을 통해 플러시를 자동 호출도 가능하다.
em.flush()는 테스트할때 말고는 잘 사용하지 않을 것 같다.
플러시 모드 옵션
em.setFlushMode(FlushModeType.COMMIT)
FlushModeType.AUTO: 커밋이나 쿼리를 실행할 때 플러시 (기본값)
FlushModeType.COMMIT: 커밋할 때만 플러시
거의 설정할일이 잘 없다. 기본값으로 쓰자.
위에서 언급했듯이 영속성 컨텍스트를 비우는 것이 아니라 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화 해주고 트랜잭션이라는 작업단위가 중요하기 때문에 커밋 직전에만 동기화하면 된다.
준영속 상태
영속 상태로 만드는 방법으로는 em.persist()와 1차 캐시에 없는 데이트를 em.find()를 통해 조회 했을 때 1차 캐시에 등록되는 경우 크게 두 가지가 있다. 준영속 상태는 영속 상태의 엔티티가 영속성 컨텍스트에서 분리(detached)되는 것이다. 그렇기 때문에 영속성 컨텍스트가 제공하는 Dirty Checking 등의 기능을 사용하지 못한다.
즉, 영속성 켄텍스트(JPA)에서 관리하기 싫을때 사용한다.
준영속 상태로 만드는 법
- em.detach(entity) : 특정 entity만 준영속 상태로 전환
- em.clear() : 영속성 컨텍스트 통으로 초기화
- em.close() : 영속성 컨텍스트 종료
'Back-End > 자바 ORM 표준 JPA 프로그래밍' 카테고리의 다른 글
[섹션 7] 고급 매핑 (0) | 2024.04.02 |
---|---|
[섹션 6] 다양한 연관관계 매핑 (0) | 2024.04.01 |
[섹션5] 연관관계 매핑 기초 (0) | 2024.04.01 |
[섹션4] 엔티티 매핑 (1) | 2024.03.25 |
[섹션1] JPA 소개 (2) | 2024.03.24 |