본문 바로가기

전체 글

(21)
N+1 문제와 해결 JPA의 성능 문제 중 70~80% 가 N+1 문제라고 한다. N+1 문제를 해결할 수 있는 방법은 크게 3가지 (Fetch Join, @EntityGraph, @BatchSize) 정도가 있다. @EntityGraph, @BatchSize는 JPA 옵션 같은 느낌이고 사실 Fetch Join만 잘 알아도 나머지를 이해하는데 큰 어려움이 없다. 하지만 Fetch Join을 사용하기 위해서는 여러 주의사항을 알고 있어야 하기 때문에 이를 정리하고자 한다. N+1 문제 N+1 문제는 연관관계가 매핑된 엔티티를 조회할 때 발생하는 문제로 처음 조회된 쿼리 결과를 바탕으로 N번 이상의 쿼리가 더 실행되는 문제를 말한다. 결과만 본다면 Fetch Join을 사용한 쿼리와 동일한 데이터를 갖고 있어 정상으로 보일 ..
Lazy Loading과 Dirty Checking 응용 Lazy Loading(지연 로딩)은 연관관계가 매핑된 엔티티를 조회했을 때 조회한 해당 엔티티의 데이터만 가져오는 걸 뜻한다. 조회하지 않은 엔티티는 get 메서드 호출 시 영속성 컨텍스트를 비교 후 값이 없다고 판단하면 그때서야 데이터를 가져온다. Dirty Checking(변경 감지)은 영속성 컨텍스트에서 관리하는 엔티티의 값이 변경되었을 경우 1차 캐시 안의 스냅샷과 비교 후 변경을 감지하고, Update 쿼리를 만들어 트랜잭션 커밋 시점에 update 처리를 해준다. 이론적으론 알고 있지만 언제 사용하면 좋을지 고민하였는데, JPA를 사용해보지 않은 나에게 좋은 예제가 있어 글을 남겨본다. 엔티티 구현 각 엔티티 간의 간략한 관계도를 그려봤다. Order를 중심으로 Delivery와 1:1 양방..
엔티티 생명주기와 영속성 컨텍스트 엔티티 생명주기 먼저 엔티티의 생명주기를 알아야 영속성 컨텍스트를 이해할 수 있고 JPA의 특징을 살려 개발을 할 수 있게 된다. @Entity를 적용한 클래스만이 엔티티가 될 수 있으며, 엔티티의 생명주기는 비영속(New), 영속(Managed), 준영속(Detached), 삭제(Removed)로 구분된다. 이 중에서 영속 상태에서만 영속성 컨텍스트의 관리 대상이 된다. 비영속 //비영속 상태 Member member = new Member(); member.setName("member"); member.setAddress(new Address("한국", "서울", "12345")); //준영속 상태로 변경 //실제 DB에 들어갈 수 있는 식별자를 사용해야 영속으로 변경 가능 member.setid(1..
엔티티 매니저 팩토리와 엔티티 매니저 EntityManagerFactory 엔티티 매니저 팩토리의 주요 역할은 엔티티 매니저가 인스턴스화 되도록 지원하는 것이다. 엔티티 매니저 팩토리는 하나의 데이터베이스를 위해 구성되며 리소스를 효율적으로 관리(커넥션 풀 관리 등)함으로써 해당 데이터베이스에 대해 여러 엔티티 매니저를 구성하는 효율적인 방법을 제공한다. 생성 비용이 크기 때문에 한 번 생성 후 재사용되며 Thread-Safe 하게 구현되어 있어 구성된 전체 애플리케이션을 지원할 수 있다. 애플리케이션을 종료할 때는 반드시 종료해주어야 하며 엔티티 매니저 팩토리가 종료되면서 엔티티 매니저들도 함께 종료된다. 생성 시점에는 persistence.xml 파일에 설정된 정보를 읽어온다. persistence.xml 설정 정보 설정 정보는 버전에 ..
@Transactional 적용 스프링 프레임워크는 @Transactional 을 제공해 비즈니스 로직에 들어있는 JDBC 기술을 걷어내 서비스 계층을 특정 기술에 종속되지 않도록 하였고 다양한 옵션을 통해 트랜잭션을 제어할 수 있다. @Transactional 적용 전 @Slf4j @RequiredArgsConstructor public class MemberService { private final DataSource dataSource; private final MemberRepository memberRepository; public void transfer(String fromId, String toId, int money) throws SQLException { Connection con = dataSource.getConn..