| En

JPA Dirty Checking 동작

조회한 엔티티의 setter만 호출하고 트랜잭션을 끝내면 DB가 바뀐다. save()나 update()를 부르지 않았는데도 그렇다. 이 자동 UPDATE를 만드는 동작이 Dirty Checking, 즉 변경 감지다. Hibernate가 어떤 필드가 바뀌었는지를 무엇과 비교해 알아내는지가 동작의 핵심이다. 스냅샷 비교의 기준은 스냅샷이다. 엔티티가 영속성 컨텍스트에 처음 등록되는 순간 Hibernate는 모든 필드 값을 복사해 별도로 보관한다. 이게 DB와 일치하는 최초 상태다. 내부적으로는 엔티티 식별자를 키로 하고, 각 필드 값을 순서대로 담은 Object 배열을 값으로 갖는다. 스냅샷은 두 시점에 만들어진다. find()나 JPQL로 DB에서 엔티티를 조회할 때, 그리고 persist()로 새 엔티티를 영속화할 때다. merge()는 조금 다르다. 준영속 엔티티의 값을 영속 엔티티에 복사한 뒤 그 영속 엔티티에 대한 스냅샷을 만들고, 그 영속 엔티티를 반환한다. 원본 준영속 엔티티는 그대로 준영속 상태로 남는다. ...

2024년 6월 8일 · 4 분 · 733 단어 · In-Jun

JPA N+1 쿼리 문제

회원 목록을 한 번 조회했는데 SQL 로그에 SELECT가 101줄 찍힌다. 이것이 N+1 문제다. 연관 엔티티를 가져오는 첫 쿼리 1개에, 그 연관을 건드릴 때마다 추가로 N개가 더 나가 총 N+1개가 된다. 추정 밀리초가 아니라 로그에 찍힌 쿼리 개수가 이 문제의 신호다. 1개가 101개가 되는 과정 Member가 Team을 지연 로딩(FetchType.LAZY)으로 참조한다고 하자. 회원 100명을 조회하고 반복문에서 각 회원의 팀 이름을 출력한다. List<Member> members = memberRepository.findAll(); // 쿼리 1개 for (Member member : members) { System.out.println(member.getTeam().getName()); // 여기서 팀마다 쿼리 1개 } 첫 줄에서 회원을 한 번에 가져온다. ...

2024년 6월 8일 · 4 분 · 659 단어 · In-Jun

Spring Data JPA와 JPA의 차이

JPA는 표준 API이고, Spring Data JPA는 그 위에 얹힌 코드 생성 계층이다. 인터페이스만 선언하면 구현을 만들어 주고, 그 구현이 내부에서 EntityManager를 호출한다. 같은 동작을 두 방식으로 작성하면 경계가 드러난다. JPA JPA의 중심은 영속성 컨텍스트와 그것을 다루는 EntityManager다. 영속성 컨텍스트는 엔티티를 보관하는 논리적 공간으로 1차 캐시, 변경 감지(Dirty Checking), 지연 로딩, 쓰기 지연을 제공한다. 순수 JPA에서는 EntityManager를 직접 잡고 트랜잭션도 직접 열고 닫는다. EntityManager em = emf.createEntityManager(); EntityTransaction tx = em.getTransaction(); tx.begin(); User user = new User("홍길동", "[email protected]"); em.persist(user); // 영속 상태로 전환 → INSERT 예약 User found = em.find(User.class, user.getId()); // 1차 캐시에서 반환 (추가 쿼리 없음) found.setName("김철수"); // 변경 감지 → flush 시 자동 UPDATE tx.commit(); // 예약된 SQL 실행 em.close(); 동작은 하지만, 모든 엔티티의 모든 CRUD마다 createEntityManager, getTransaction, begin, commit, close가 반복된다. Spring Data JPA가 제거하는 것이 이 반복이다. ...

2024년 6월 7일 · 4 분 · 659 단어 · In-Jun
[email protected]