JPA를 공부하면서 영속성이라는 단어를 너무 많이 접했고 JPA를 잘 사용하기 위해서는
영속성을 찾아보는 게 중요할 것 같아서 공부해보자
영속성 컨텍스트를 이해하면 JPA가 내부적으로 어떻게 돌아가는지 이해하는데 도움이 된다.
클라이언트의 요청이 올 때마다 EntitiyManagerFactory에서 EntityManager를 개별적으로 만들어준다.
예를 들어서 (여기서 em은 entityManager)
클라1 요청 → emFactory em1 생성 → 클라1 em1 사용 → connection 이용 → DB 접근
클라2 요청 → emFactory em2 생성 → 클라2 em2 사용 → connection 이용 → DB 접근
위와 같은 과정으로 클라이언트들은 각각 다른 entityManager 를 사용한다.
그래서 영속성 컨텍스트가 뭔데?
먼저 영속석 컨텍스트는 Entity를 영구 저장하는 환경 이라는 뜻이다.
EntityManager.persist() 메서드를 호출해서 인자로 entity를 넣어주면 DB에 저장해주는구나! 라고 생각했다. 하지만 이것은 DB에 저장하는 것이 아니라 영속성 컨텍스트에 저장해주는 것이다.
그래서 EntityManager는 하나의 PersistenceContext를 가지고 있는데, 이 PersistenceContext에 관리되고 있는 객체라면 영속 상태이고 관리되고 있지 않다면 비영속 상태로 구분할 수 있다.
영속과 비영속
User user = new User();
user.setName("ho"); 기존 자바 세상과 동일하기 때문에 만들고 애플리케이션 재실행하면 다시 찾을 수 없음User user = new User();
user.setName("ho");
EntityManager em = em.createEntityManager();
//객체를 영속상태로 만드는 메소드
em.persist(user); EntityManager의 persist 메소드를 사용해서 영속성 컨텍스트에서 관리하여 영속상태로 만든다. 이후 트랜잭션 commit 명령어가 실행되면 영속성 컨텍스트에 있는 객체들은 DB에 저장된다.영속성 컨텍스트라는 곳을 거치지 않고 바로 DB에 저장하면 좋지 않을까? 라는 의문이 생긴다.
왜 사용해야 하는지 알아보자
em.persist() 메서드를 통해서 영속성 컨텍스트에 저장하는 작업을 수행한다고 했다.
이렇게 캐싱되어 User1을 조회하는 경우 DB까지 접근하지 않고 빠르게 제공할 수 있다.💡 캐시로 효과적인 성능향상이 가능할까?
큰 성능향상을 기대하기는 어렵다. entityManager는 한 번에 트랜잭션에 사용되고 종료될 때 해당 entityManager 객체도 사라지기 때문에 누적해서 캐싱되는 것은 아니다.
비즈니스 로직이 매우 복잡한 경우는 장점이 될 수 있다.
em.persist() 메소드를 호출하면 캐시에 저장 뿐 아니라 영속 컨텍스트 내부에 있는 쓰기 지연 SQL 저장소에 insert 쿼리문을 생성해서 쌓아둔다. 트랜잭션이 끝나고 commit() 이 호출되면 저장소에 있는 쿼리들은 DB로 전송된다.
매 작업마다 DB에 접근해서 반영한다면 성능저하가 발생할 수 있는데 이를 최소화 할 수 있도록 한다.User foundUser = em.find(User.class, 1L); 라는 명령어를 통해서foundUser.setName(”이름 바꾸기”) 엔티티매니저를 사용하지 않고 update쿼리를 하지 않더라도 setter 호출 이후 커밋이 된다면 JPA가 update 쿼리를 대신 작성해주어이름 바꾸기 라는 변경된 이름으로 DB에 저장된다.영속성 컨텍스트를 설명하면서 commit 시점에 DB에 저장된다고 예시를 많이 들었지만 커밋뿐만 아니라 다른 상황에서도 쓰기 지연 SQL 저장에 누적된 쿼리들이 DB로 반영할 수 있다.
em.flush() - 직접 호출em.persist(entity) 이렇게 persist에 등록하더라도 바로 DB에 반영되지 않는다고 했다.em.persist(userA);
em.persist(userB);
// JPQL 실행
query = em.createQuery("select u from User u", User.class);
//query 실행해서 User 조회하기
List<User> users = query.getResultList(); 현재 위 코드를 보면 userA, userB를 persist()하고 트랜잭션 커밋, flush() 작업은 발생하지 않았다.flush() 를 수행하고 쿼리를 실행하기 때문에 조회할 수 있다.🚨flush 특징🚨
- 영속성 컨텍스트를 비우지 않음
- 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화