Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- TestContainers
- 낙관적 락 재시도
- spring DI
- 백준
- redissonlock aop
- netty
- AccessToken
- java
- kotest testcontainers
- 멀티모듈 테스트컨테이너
- spring aop
- 알고리즘
- S3
- jpa
- 우아한 테크러닝
- OptimisticLock
- 소수찾기 java
- springsecurity
- 형상관리
- Invalid property 'principal.username' of bean class
- 낙관적 락 롤백
- multimodule testcontainers
- interface
- @transactional
- RefreshToken
- aop
- ObjectOptimisticLockingFailureException
- Spring Cloud Gateway
- ObjectOptimisticLockingFailureException 처리
- DI
Archives
- Today
- Total
조급하면 모래성이 될뿐
JPQL like에 _ 포함시키기 본문
상황
- 설계한 Entity의 id는 2개의 fk 조합으로 {id}_{id} 형태의 String 타입으로 저장된다.
- {특정 스케줄}_{좌석 번호}를 의미한다.
- ex) 1_0, 1_1, 1_2...
서비스에서 특정 스케줄에 등록된 좌석번호를 찾기 위해 like {특정 스케줄}_%로 검색하려 한다.
AS-IS
- service에서 {id}_ 를 만들어서 전달했다.
@Service
@Transactional(readOnly = true)
public class ReservedSeatService {
...
public List<ResponseFindSeat> findByScheduleId(Long scheduleId) {
return reservedSeatRepository.searchByScheduleIdStartsWith(makeFindByScheduleParam(scheduleId)).stream()
.map((reservedSeat -> seatConverter.convertFromSeatToResponseFindSeat(reservedSeat.getSeat())))
.collect(toList());
}
private String makeFindByScheduleParam(Long scheduleId) {
return new StringBuilder()
.append(scheduleId)
.append(ID_SEPARATOR)
.toString();
}
}
public interface ReservedSeatRepository extends JpaRepository<ReservedSeat, String> {
@Query("SELECT rs FROM ReservedSeat rs join fetch rs.seat WHERE rs.id LIKE :scheduleId%")
List<ReservedSeat> searchByScheduleIdStartsWith(@Param("scheduleId") String scheduleId);
}
문제는 외부에서 scheduleId 포맷을 만들어서 전달해야 한다는 것..
- Repository 테스트 코드 작성할 때도 scheduleId를 만들어서 보내는 게 비효율적이라고 느꼈다..
- 직접 service와 규칙을 맞춰줘야 한다..
목표
- @Query에 _를 포함시키고 싶었다.
- 아래의 테스트 결과는 1이 나와야 한다.
@Test
@DisplayName("만약 1_1과 11_1이 있을 때, 1_로 조회하면 1건만 조회되어야 한다.")
void testSearchByIdStartsWithBadParam() {
// 1_1, 2_2, ... , 10_10, 11_11, 12_12 의 데이터가 저장 될 것이다.
for (int i = 0; i < 12; i++) {
saveReservedSeatMultiSeat(1);
}
// 1_0만 조회되기를 기대한다.
List<ReservedSeat> reservedSeats = reservedSeatRepository.searchByScheduleIdStartsWith(1L);
assertThat(reservedSeats).hasSize(1);
}
실패 Case
public interface ReservedSeatRepository extends JpaRepository<ReservedSeat, String> {
@Query("SELECT rs FROM ReservedSeat rs join fetch rs.seat WHERE rs.id LIKE CONCAT(:scheduleId, '_', '%')")
List<ReservedSeat> searchByScheduleIdStartsWith(@Param("scheduleId") Long scheduleId);
}
- 4가 나왔다.
- 실패 원인
- _가 와일드카드이기 때문에 인식하지 못했다.
- %% 는 ‘모든 문자’라는 의미고, _는 ‘한 글자’라는 의미.
- 참조
- 따라서 위 쿼리에서 1_%에는 1로 시작하는 id가 모두 포함된 것이었다.
- 1_1, 10_10, 11_11, 12_12
성공
public interface ReservedSeatRepository extends JpaRepository<ReservedSeat, String> {
@Query("SELECT rs FROM ReservedSeat rs join fetch rs.seat WHERE rs.id LIKE CONCAT(:scheduleId, '\\_', '%')")
List<ReservedSeat> searchByScheduleIdStartsWith(@Param("scheduleId") Long scheduleId);
}
- 와일드카드를 검색하기 위해 ESCAPE를 사용해야 한다.
- _앞에 ESCAPE문자를 적어주면, _가 '한 글자'가 아닌 문자로 해석된다.
- default ESACPE문자는 역 슬래쉬이다 |
- java에서도 |를 문자로 인식하기 위해 한번 더 적어주어야 한다. (||로)
- 이런 식으로 ESCAPE문자를 변경해서 사용할 수도 있다.
public interface ReservedSeatRepository extends JpaRepository<ReservedSeat, String> {
@Query("SELECT rs FROM ReservedSeat rs join fetch rs.seat WHERE rs.id LIKE CONCAT(:scheduleId, 'K_', '%') escape 'K'")
List<ReservedSeat> searchByScheduleIdStartsWith(@Param("scheduleId") Long scheduleId);
}
ESCAPE 관련해서 참고하면 좋을 링크를 첨부한다
반응형
'TroubleShooting > 데브코스' 카테고리의 다른 글
Service에서 DataIntegrityViolationException을 Catch 못함 (0) | 2022.07.14 |
---|---|
S3 파일 업로드 (0) | 2022.07.11 |
Service에서 다른 Service를 의존하게 된다면 ? (0) | 2022.07.06 |
N + 1 직접 만났다 (0) | 2022.07.01 |
Spring Data Jpa Insert 할 때 Select가 나가네.. (0) | 2022.07.01 |