빌드를 하던중 다음과 같은 로그를 마주했는데요.
2024-01-03 14:43:12 [main] DEBUG o.s.d.j.repository.query.NamedQuery - Did not find named query ServiceType.findByNameEquals
2024-01-03 14:43:12 [main] DEBUG o.s.o.j.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler - Creating new EntityManager for shared EntityManager invocation
2024-01-03 14:43:12 [main] DEBUG o.s.d.j.repository.query.NamedQuery - Looking up named query ServiceType.existsByNameEquals
2024-01-03 14:43:12 [main] DEBUG o.h.r.t.b.j.i.JdbcResourceLocalTransactionCoordinatorImpl - JDBC transaction marked for rollback-only (exception provided for stack trace)
java.lang.Exception: exception just for purpose of providing stack trace
Exception 이긴하지만 exception just for purpose of providing stack trace 라고 쓰여진만큼 스프링에서 권장을 하되 실행에 영향은 없는 Exception 이었습니다.
하지만 이 Exception이 Repository 내 모든 메서드마다 발생을 하니.. 해당 문제를 해결하긴 해야하는 상황이 생겼습니다.
Named Query 가 뭐야?
jpa 에서 지원하는 Query 사용방법 중 하나입니다.
첫번째 방법은 엔티티에 @NamedQuery 를 사용하는 방법입니다.
@Entity
@Getter
@Table(name = "mirr")
@NamedQuery(
name = "Mirr.findByName",
query = "select a from Mirr m where m.name = :name"
)
public class Mirr {
@Id
@Column(name = "mirr_pk")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer mirrtPk;
.
.
.
}
이렇게 엔티티에 쿼리들을 적어놓으면 추후 Service 단에서 EntityManager 를 통해 다음과같이 사용이 가능합니다.
@Service
public class MirrService {
@PersistenceContext
private final EntityManager entityManager;
public ResponseEntity<?> test(String name) {
entityManager.createNamedQuery("Mirr.findByName", Mirr.class);
.
.
return ...;
}
}
하지만 쿼리를 엔티티안에 작성한다는 점과 Repository를 거치지 않고, 직접 EntityManager 를 호출한다는것에 좋은 방법은 아닌걸로 보입니다.
로그 추적해보기
다시 본론으로 돌아와서 이저껏 발생하지 않다고 버전을 올리고나서 발생한 이유가 무엇인지 추적해보죠.
JDBC transaction marked for rollback-only (exception provided for stack trace) 라는 로그가 눈에 띕니다. 검색을 해보니 JdbcResourceLocalTransactionCoordinatorImpl 구현체에 정의가 되어있었습니다.

해당 메서드는 Transaction 의 상태가 RollBack 이 아니면 아까 저희가 보았던 로그를 띄워주는 메서드입니다.
이 메서드를 어디서 호출하는지 조금만 올라가 보겠습니다.
다음은 AbstractSharedSessionContract 클래스의 buildNamedQuery 메서드입니다.

markRollbackOnly 를 호출하는 상황이 UnknownNamedQueryException 이 발생하는 상황이네요.
그럼 try 구문의 어디서 UnknownNamedQueryException 을 발생시킬까요

메서드를 분석해본 결과 NamedQuery 가 아니거나 NativeQuery 가 아니면 해당 Exception 을 터뜨려 저희에게 로그를 보여주었던 것입니다.
그러면 왜 버전이 올라가고 나서 이런 에러가 보이는걸까요?
그 이유는 gradle 버전을 7.2 에서 8.5로 올렸기 때문입니다.
지금까지 보신 코드들은 gradle 8.5에 대한 코드입니다. 그럼 7.3.3 에 대한 buildNamedQuery 를 보겠습니다.

여기에도 NamedQuery 인지 NativeQuery 인지 검사하는 로직은 존재하는데요.
다만 마지막에 날리는 Exception의 형태가 다릅니다. IllegalArgumentException 을 발생시키는데요.
그 밑의 catch 구문에서 아무런 console 을 찍어주지 않아 그동안 발견하지 못한것입니다.
실제로 디버깅을 하면 아래 사진처럼 Exception 을 타는것을 볼 수 있습니다.

해결방안
1. 로깅 레벨 낮추기
먼저 관련되어 있는 gradle class 의 로깅레벨을 DEBUG 에서 INFO 로 바꾸는 방법이 있습니다.
다음과 같이 원하는 클래스의 경로를 찾아 yml 파일에 적용시키면 해당 로그는 출력되지 않습니다.

2. @NamedQuery 등록하기


3. @NamedNativeQuery 등록하기


Tip) 위 2, 3번은 AnnotationBinder 클래스에서 처음 바인딩합니다.

4. Gradle 버전 낮추기
실행에 영향을 미치는 Exception 이 아님에도 불구하고 로그 때문에, 원하는 gradle 버전을 선택하지 못하는건 바람직하지 않습니다.
'Spring' 카테고리의 다른 글
[Spring] Annotation Processor (0) | 2024.04.11 |
---|---|
[Spring] WebSocket (0) | 2023.08.23 |
[Spring] AOP (0) | 2023.06.08 |
[Spring] JPA? Hibernate? Persistence? (0) | 2023.04.30 |
[Spring] Session (feat. 프로젝트 경험) (0) | 2023.04.28 |