JPA에서 영속성 관리는 정확하게 알아야합니다.
트랜잭션 내에서 persist, commit은 정확하게 알지 못하면 제대로된 메서드를 관리하기 어렵기 때문입니다.
영속성과 관련해서 공부하면서 의문을 가지게 되었습니다.
- 스프링 프레임워크에서 EntityManager 주입받아서 사용하면, 같은 트랜잭션 범위에 있는 EntityManager는 동일 영속성 컨텍스트에 접근한다.
- 따라서 동일한 @Transactional (같은 트랜잭션 범위 전파되는 경우에도) 이면 같은 영속성 컨텍스트에 접근한다.
그럼 Id가 GenerationType.IDENTITY인 경우에는 어떻게 구성되는 걸까?
먼저 용어에 대해 알아보겠습니다.
비영속 (new/transient)
영속성 컨텍스트와 전혀 관계가 없는 새로운 상태입니다.
주로 객체를 생성하고 초기화 하면 비영속 상태입니다.
영속 (managed)
영속성 컨텍스트에 관리되는 상태입니다.
엔티티 매니저에 의해 관리되는 상태이며, flush() 하지 않은 persist() 상태를 의미합니다.
준영속 (detached)
영속성 컨텍스트에 저장되었다가 분리된 상태입니다.
엔티티 매니저에 의해 떼어졌을 때 상태이며, detach() 했을때 되는 상태를 의미합니다.
삭제 (removed)
삭제된 상태입니다.
즉, persist()를 계속 했을 때, flush()를 하지 않는 다면 영속성 컨텍스트를 통해 1차 캐시에서 쿼리들을 저장해둡니다.
그리고 flush()하게 되는 순간 모든 쿼리들이 실제 DB로 보내지게 됩니다.
이 말은 persist()만 하고 flush()를 하지 않으면 실제로 저장되지 않는 다는 것을 의미합니다.
또한 persist()하고 setter를 통해 엔티티를 수정한다면, commit()하는 당시에 1차 캐시를 통해 값을 스냅샷으로 저장해두는데 저장되는 값이 기대값과 다르면 update 쿼리를 만들어서 DBMS에 저장하게 됩니다. 따라서 쿼리를 쉽고 간단하게 관리할 수 있게 만드는 로직이라고 할 수 있습니다.
@Entity
public class Manager {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String email;
private String password;
}
@Service
public class ManagerService {
@PersistenceContext
private EntityManager em;
private final ManagerDao managerDao;
@Autowired
public ManagerService(ManagerDao managerDao) {
this.managerDao = managerDao;
}
@Transactional
public void signUpManager(ManagerSignUpRequestDto request) {
Manager manager = Manager.builder()
.email(request.getEmail())
.password(encodePassword)
.build();
em.persist(manager);
tx.commit();
}
}
위처럼 persist를 하게되면 ID 생성전략이 IDENTITY이기 때문에 persist를 할때마다 insert 쿼리를 통해 DBMS를 통해 데이터를 저장하며 commit을 하면 실제 DBMS에서 저장한 데이터 값을 찾을 수 있게 만듭니다.
즉, 여러개의 쿼리를 한번에 보내는 버퍼링 처리가 되지 않는다는 것을 의미합니다.
@Transactional
public void signUpManager(ManagerSignUpRequestDto request) {
Manager manager = Manager.builder()
.email(request.getEmail())
.password(encodePassword)
.build();
em.persist(manager);
em.clear();
Manager manager1 = managerDao.findById(manager.getId());
tx.commit();
}
하지만 위 처럼 commit()을 하기 전에 em을 clear하면 id값을 통해 찾을 수 없으며, commit()을 한 후에도 DBMS에 저장되지 않은 것을 확인할 수 있습니다.
이러한 요소를 통해 commit()을 하기전에 data를 올리지만 em에서 저장된 persist()요소를 매번 Insert 쿼리를 보내지만, commit()을 할때야 비로소 em에 저장된 캐시 내역들이 실제로 저장되는 것을 알 수 있었습니다.
혹시 조금 더 깊게 알고 계신분이 있다면 알려주시면 감사하겠습니다.
참고
플러시가 발동되는 조건
플러시 발생
- 변경감지
- 수정된 엔티티 쓰기 지연 SQL 저장소에 등록
- 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송 (등록, 수정, 삭제 쿼리)
영속성 컨텍스트를 플러시하는 방법
- em.flush() : 직접 호출
- 트랜잭션 커밋 : 플러시 자동 호출
- JPQL 쿼리 실행 : 플러시 자동 호출
참고 : https://velog.io/@syleemk/JPA-%EC%98%81%EC%86%8D%EC%84%B1-%EA%B4%80%EB%A6%AC
'기술 스텍 > Spring' 카테고리의 다른 글
IntelliJ 단축키(mac) (0) | 2024.11.28 |
---|---|
Spring Cloud (0) | 2023.10.11 |
SpringBoot(1) DI (0) | 2023.01.24 |