jpa

    Spring Data JPA @Query 사용시 주의점(JPA 버그)

    이전 회사에서 새벽에 돌아야할 배치가 안돌아서 난리가 났던 적이 한번 있었는데 알고보니 쿼리 하나가 몇 시간째 돌고 있었다. 어쨌든 원인을 빨리 찾아서 고치긴 했는데 분석을 해보니 Spring Data JPA(2.6.2 기준)에 문제가 있었다. @Query annotaion을 쓰면서 Paging 처리를 할 때 발생할 수 있는 이슈인데, 다음 번에 깊게 파볼 생각으로 남겨두었던게 갑자기 생각이 나서 좀 들여다보았다. 상황을 재현해보면 아래와 같다. public interface ProductJpaRepository extends JpaRepository { @Query(value = "select id, name from product where name = :productName", nativeQuery..

    Spring Data JPA의 saveAll() 사용시 주의점

    현재 개발 중인 배치 프로그램이 있는데 처리 과정이 모두 끝나고 마지막에 몇 만건의 데이터를 DB에 insert하는 과정이 있다. 대량의 데이터를 한꺼번에 처리해야되니 Spring Data에서 제공하는 saveAll()을 사용할 생각이었는데.. 얼마전 DBA께서 ERD를 보며 하신 말씀이 떠올랐다. "이 테이블은 commit 단위를 만 건 이하로 끊어주세요." saveAll을 호출하게 되면 몇 만건의 데이터가 한꺼번에 bulk insert 되기 때문에 데이터를 적절한 chunk로 나누어줄 필요가 있다는 것인데, 그 전에 saveAll의 내부 구현을 확인해보았다. saveAll 내부에서는 save를 반복 호출하는데, 두 메서드에 모두 @Transactional이 걸려있다. 이 경우 우선순위는 어디에 있을까..

    [삽질로그] JPA 연관관계 외래키 매핑시 주의점

    귀중한 준칙 두 가지를 배운 삽질로그에 대해 기록하고자 한다. 여러 컬럼의 조합을 기본키로 갖는 테이블들의 JPA 연관 관계를 설정해야하는 상황이었다. 대략적으로 테이블 관계를 그리면 이렇게 된다. 각 스토어는 매일 새로운 프로모션을 생성할 수 있고, 생성할 수 있는 프로모션의 종류는 promotion_group_no로 정의하여 관리된다. 일단 스토어가 백오피스에서 프로모션을 생성하면 백그라운드에서 프로모션 생성 API가 호출되고, promotion no가 응답값으로 넘어와 스토어 프로모션 생성현황 테이블에 기록되게 된다. 또한 매일 promotion no를 key로 프로모션 API를 호출하여 해당 프로모션이 종료됐는지를 확인한뒤, 결과를 스토어 프로모션 참여현황에 집계하고 집계상태코드(aggregati..

    JPA의 핵심 - 영속성 컨텍스트 훑어보기

    JPA를 처음 접했던 몇 년전에는 ORM이라는 개념 자체가 낯설었다. 엔티티 정의후 연관 관계만 잘 매핑하면 소스에 직접 쿼리를 쓸 일이 현저히 줄어들어 그것만으로도 훌륭하다고 생각했는데, 알고보니 주인공은 영속성 컨텍스트였다. 그만큼 영속성 컨텍스트는 JPA를 사용한다고 하면 반드시 알아야하기 때문에 핵심 개념을 중심으로 정리해보았다. Entity Manager 엔티티는 말그대로 엔티티의 CRUD에 관여하며 엔티티와 관련된 모든 일을 처리한다. 자바 ORM 표준 JPA 프로그래밍이라는 책에서 김영한님은 엔티티 매니저를 가상의 데이터베이스로 생각하자고 하셨다. 실제 DB에 쿼리가 날아가기 전에, 엔티티와 관련된 모든 생성, 조회, 수정, 제거 작업은 엔티티 매니저를 거치게 된다. 스프링에 익숙하신 분이라..

    DataJpaTest를 활용한 테스트

    지난 포스팅에서는 Mockito를 활용하여 Service 객체가 적절하게 내부 객체를 호출하는지를 알아보는 테스트를 진행했었다. 그 과정에서 Mock 객체를 활용한 이유는 테스트의 주 관심 대상이 아닌 객체는 올바르게 동작한다는 가정을 하기 위해서였는데, 이제 해당 Mock 객체가 올바르게 동작했는지 테스트해보자. 마찬가지로 회의실 예약 예제를 활용해보자. 이번 테스트의 중점은 Spring Data JPA의 동작이다. 즉, 데이터가 실제로 올바르게 들어가고 나오는지를 보면 된다. 코드를 보자. 간단하다. testRetrieveRooms 메서드를 보면, 먼저 "A"라는 회의실을 생성해 roomRepository에 저장한다. 저장된 모든 회의실 중 첫번째 회의실을 꺼내온 다음, 회의실의 이름이 위에서 저장했..