반응형

JAVA/JPA 학습기록 22

[JPA] unique 동시성 이슈 해결 및 CountDownLatch 테스트 작성 (Feat. Unique Index)

사이드 프로젝트를 하면서 동시성 이슈를 만났다. 모바일 프론트 측에서 api 테스트를 하다가, 즐겨찾기 엔티티가 연달아 두 번 등록되는 이슈가 있었던 것이다. 그리고 즐겨찾기 엔티티는 repository에서 Optional과 같은 꼴로 반환되게 했기 때문에, 두 개 이상의 결과가 반환되면 NonUniqueResultException 가 발생한다. 이러한 동시성 이슈가 발생하는 데에는, 두 스레드가 거의 동시에 진행됐기 때문이다. 사실 트래픽이 조금이라도 많으면 이러한 동시성 이슈는 흔히 만날 수 있다. nGrinder로 save 관련 메서드에 수십개의 스레드로 테스트를 돌려보자. 아래 사진은 nGrinder로 회원가입 메서드를 수십개의 스레드로 5초동안 실행시킨 결과이다. 원래대로라면 같은 이메일, 같은..

[230404] 커버링 인덱스를 활용한 소규모 사이드프젝 쿼리튜닝 일지

해당 글에서는 spring data jpa에서의 left outer join, fetch join, 커버링 인덱스에 대해 다룹니다. 사이드 프로젝트 `모카콩`의 Wiki에 작성한 글에 해당된다. 해당 프로젝트 github: https://github.com/mocacong/Mocacong-Backend GitHub - mocacong/Mocacong-Backend: 모카콩 백엔드 모카콩 백엔드. Contribute to mocacong/Mocacong-Backend development by creating an account on GitHub. github.com 들어가며 모카콩에서는 카페에 대한 리뷰를 작성할 때마다 다양한 작업을 해주고 있습니다. 리뷰에는 평점, 해당 카페에 대한 스터디 타입(혼코딩..

[ERROR] JPA initializationError 해결 방법 모음 및 대처법

스프링 프레임워크에서 JPA ORM을 이용하면 반드시 한 번쯤은 만날 수밖에 없는 에러가 있다. could not prepare statement; SQL; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement org.springframework.dao.InvalidDataAccessResourceUsageException: could not prepare statement; nested exception is org.hibernate.exception.SQLGrammarException: could not prepare statement at org.springframework.orm.jpa..

[JPA] 프로젝트 동시성 이슈 해결을 위해 낙관적 락을 걸어보았다

롤링페이퍼 기반 서비스 플랫폼을 개발하던 중 아래 버그를 발견했다. ??? 대체 어떤 일이 일어난다는 거야... 하는 마음으로 같은 메시지를 막 광클했을 때 위와 같이 `에러가 발생했어요` 페이지가 뜨는 것을 발견했다. 해당 메시지에 좋아요를 광클하거나, 같은 계정으로 PC/모바일 등 다중 환경 접속 후 좋아요를 여러번 동시에 누르면 발생하는 버그였다. 즉, 동시성 이슈로 발생한 문제였다! 우리 서비스에서는 한 명의 회원이 같은 메시지에 좋아요를 2개 이상 누를 수 없다. 인스타나 페이스북처럼 좋아요가 눌러진 상태에서 좋아요를 또 누르면 좋아요 취소가 작동돼야 한다. 하지만 위와 같이 광클하는 경우에는 그 찰나에 같은 회원의 메시지 좋아요가 2개 들어가버린 것을 확인할 수 있었다. 상황을 그림으로 정리해..

[QueryDSL] queryDSL 프로젝트 적용 후기 및 트러블슈팅

우테코 프로젝트에서는 Spring Data JPA를 사용하고 있었다. Spring Data JPA를 이용하면 인터페이스 생성 만으로도 SimpleJpaRepository에 내장된 다양한 CRUD 메서드를 활용할 수 있고, 추가로 커스텀 메서드도 편리하게 만들 수 있다는 장점이 존재한다. 그러나 Spring Data JPA는 동적 쿼리를 사용하기 어렵고 복잡한 쿼리는 @Query 어노테이션을 이용하여 JPQL을 직접 작성해주어야 한다는 문제점이 존재한다. 따라서 이번 기회에 queryDSL을 적용하여 컴파일 시점에 문법 오류를 잡아낼 수 있게 하고, 복잡한 쿼리를 리팩터링하는 데에 도전해보았다. 특히 위처럼 DTO를 반환하는 repository method의 경우 @Query로 작성하면 DTO 패키지명을 ..

[JPA] 벌크 Update, Delete 연산과 영속성 컨텍스트

벌크 수정연산과 벌크 삭제연산을 각각 수행한 후에 별도로 영속성 컨텍스트 초기화 작업을 하지 않았다고 가정하자. 이후에 전체조회 작업을 시행하면 update는 연산이 적용되지 않은 영속성 컨텍스트 데이터, delete는 연산이 적용된 DB 데이터 결과가 조회되는 것을 확인할 수 있다. 왜 그런 것일까? 이를 이해하기 위해선 영속성 컨텍스트를 제대로 이해하고 있어야 한다. 아래 예시와 함께 살펴보도록 하자. Update 벌크연산 상황 영속성 컨텍스트와 DB에 아래 데이터가 있다고 하자. 위 상황에서 아래와 같이 28살 미만은 이름을 전부 비회원으로 바꾸라는 bulk update 연산을 수행해보자. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test public void bulkUpdate..

[JPA] 쓰기 지연으로 인한 쿼리 실행 시점과 예외처리 및 기본키 생성 전략

최근에 흥미로운 글을 봤다. JPA 쓰기지연 기능 때문에 커스터마이징한 예외가 발생하지 않고 DataIntegrityViolationException 예외가 발생한다는 글이었다. service에서 repository의 delete 메서드를 실행하고, 만약 외래키 설정으로 인한 DataIntegrityViolationException이 발생하면 catch로 잡아서 커스텀 예외를 던지려 하는 상황이라 가정하자. 해당 서비스 메서드를 테스트할 때, 예외 상황으로 커스텀 예외가 발생할 줄 알았는데 catch로 잡지 못하고 DataIntegrityViolationException가 발생했다는 글이었다. https://velog.io/@giantim/5 @Transactional 과 JPA 사용 시 주의점 @Tra..

[JPA] Spring Data JPA 페이징 기법을 적용해보자

조회 쿼리로 결과가 나올 때, 데이터가 너무 많으면 사용자가 보기에도 불편하고 로딩에도 오랜 시간이 걸린다. 그렇기 때문에 많은 사이트들에선 많은 데이터들을 페이지로 나누어서 관리한다. 스크롤을 내릴 때마다 데이터를 추가로 로드하는 무한 스크롤 방식을 사용하거나, 이전/다음 페이지로 이동하는 방식이 주로 사용된다. 이렇게 페이징 기법을 적용하려면 전체 데이터 개수와 요구되는 페이지넘버에 따른 인덱싱 계산을 해주어야 하는데, 실수할 여지도 많고 꽤나 번거롭다. 다행히 Spring Data JPA에서는 스스로 페이지 관련 인덱싱을 계산해준 결과를 반환해주는 기능을 제공해준다. 이번 포스팅에선 Spring Data JPA의 Pageable, Page에 대해 알아보고 MVC 패턴에서 Pageable이 어디까지 ..

[JPA] Spring Data JPA로 생성일자, 수정일자 컬럼을 간단히 추가하자

Spring Data JPA에는 Auditing 기능을 편하게 다룰 수 있도록 @CreatedDate, @LastModifiedDate 어노테이션을 제공해준다. 그리고 생성일자, 수정일자 컬럼은 웬만한 모든 도메인에 있으면 로깅 및 사용자 입장에서도 간편하다. 따라서 보통 BaseEntity라는 생성일자, 수정일자가 들어있는 상위 엔티티를 만들고 상속 구조를 이용하는 방법을 진행한다. 이번 포스팅에서는 이 방법에 대해 기록해보려 한다. 개발환경 Spring Boot 2 버전 Spring Data JPA lombok 프로덕션 코드 BaseEntity @EntityListeners(AuditingEntityListener.class) @MappedSuperclass @Getter abstract public..

[JPA] 양방향 연관관계에서 JPA 내부 작동 원리_영속성 컨텍스트의 이해

JPA 작동 원리를 이해하지 못해 영속성 컨텍스트의 흐름을 알지 못할 경우 발생할 수 있는 문제이다. 블로그에 따로 포스팅이 돼있지 않아서 JPA 스터디를 하는 겸 기록해보려고 한다. 들어가기 전에 스프링 환경에서 JPA ORM을 이용하고 있다. Member : Team이 N:1 양방향 연관관계를 가지고 있다. 'wooteco' 팀에 'kth990303'을 넣으려고 하는 예제를 이용할 것이다. 결론부터 말하자면? JPA의 내부 작동 흐름에 대해 이해하는 것이 정말 중요하다. (영속성 컨텍스트의 흐름에 대한 이해가 부족하면 문제 발생 확률이 높아지고, 문제 원인도 못찾을 수 있다.) 양방향 연관관계에서는 객체의 값을 INSERT할 때, 안전하게 두 객체 모두에게 반영해주자. 두 객체의 메서드를 사용하거나,..

반응형