본문 바로가기

IT

MySQL DELETE만 No database selected? Hibernate 버전업과 JDBC 기본 DB 해결법

728x90
반응형

스프링부트를 업그레이드했더니 조회는 되는데 삭제만 실패한다. 로그에는 1046 No database selected. 그런데 SQL에는 app_db.sample처럼 DB 이름까지 적혀 있다. “방금 그 DB에서 조회했잖아요?”라는 말이 절로 나온다.

 

실제로 MySQL에는 기본 DB가 선택되지 않은 연결에서 별칭을 사용하는 특정 DELETE가 실패하는 버그가 있다. 다만 “Hibernate 6은 전부 문제”라거나 “JDBC URL에 DB가 없으면 모든 SQL이 실패한다”는 설명은 정확하지 않다.

1. DB 이름을 썼는데 왜 DELETE만 실패할까?

-- 단일 테이블 DELETE
DELETE FROM app_db.sample WHERE id = 7;

-- multi-table DELETE 문법
DELETE s FROM app_db.sample AS s WHERE s.id = 7;

두 번째는 실제 테이블이 하나여도 MySQL의 multi-table DELETE 문법이다. 여기서 s는 별칭이며, 실제 테이블에는 DB 이름이 붙어 있다.

 

그런데 이 형태가 기본 DB 없는 세션에서 ERROR 1046 (3D000): No database selected로 실패할 수 있다. 테이블명 앞에 DB 이름을 빠뜨린 일반적인 1046 오류와 구분해야 한다.

 

“별칭이 있으면 전부 문제”도 아니다. MySQL 8.0.16부터 지원하는 DELETE FROM app_db.sample AS s WHERE s.id = 7은 별칭을 사용하는 단일 테이블 문법이다. DELETE s FROM ...과 혼동하지 말자.

2. 의도된 동작일까? 아직 미수정 버그일까?

2026년 10월 8일 확인한 MySQL Bug #103544의 공개 상태는 다음과 같다.

항목 확인 내용
상태 Verified
분류 MySQL Server: DML
영향 버전란 8.0.22, 8.0.24, 5.7.34
수정 완료 기록 해당 페이지에서 확인되지 않음

이는 공식적으로 검증된 버그다. 재현 기록에서는 별칭을 제거하거나 기본 DB를 선택하면 실행된다. 삭제 대상과 무관한 DB를 기본값으로 선택해도 성공한 사례가 있다.

 

MySQL 8.4.7 소스에도 대상 별칭 처리 과정에서 기본 DB를 복사하는 경로와, 기본 DB가 없으면 오류를 내는 코드가 남아 있다.

 

다만 공개 상태와 소스 검토를 ‘모든 최신 버전에서 직접 재현한 결과’로 확대하면 안 된다. 정확한 결론은 ‘공개적으로 수정 완료가 확인되지 않았으며, 도입할 서버 버전에서 재현 테스트가 필요하다’이다.

3. Hibernate·Spring Boot·Java 버전업과의 관계

핵심은 Java 문법이 아니라 Hibernate가 만들어 보내는 SQL이다.

Hibernate 6.4.2의 기본 DELETE 변환 경로와 달리, 6.5.0·6.6.0의 MySQLSqlAstTranslator에는 renderDeleteClause()에서 대상 별칭을 출력하는 구현이 있다. SQL이 DELETE s FROM ... 형태로 바뀌면 기존에 드러나지 않았던 MySQL 문제가 나타날 수 있다.

 

그렇다고 “Hibernate 5는 안전하고 6부터 무조건 실패”하는 것은 아니다. 정확한 버전, Dialect, 삭제 방식, 연결 상태를 함께 봐야 한다.

Spring Boot 2.7.18은 기본적으로 Hibernate 5.6.15.Final을 관리하고, Boot 3.0은 Hibernate 6.1을 사용하며 Java 17 이상을 요구한다. 함께 바뀌는 항목이 많으므로 Java 업그레이드 직후 발견됐다는 이유만으로 Java 자체를 원인으로 지목하면 곤란하다.

 

업그레이드 전후의 실제 의존성과 삭제 SQL을 비교하자. 버전 번호보다 결정적인 증거는 로그에 찍힌 SQL이다.

4. 우선 권하는 해결법: JDBC URL에 기본 DB 지정

일반적인 Spring Boot 자동 설정은 다음과 같다.

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/app_db
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}

핵심은 /app_db다. 기존 타임존·TLS 등 연결 옵션은 유지한다.

기본 DB를 생략한 URL 자체가 잘못된 것은 아니다. Connector/J는 그런 연결도 허용한다. 다만 이 버그 때문에 SQL에 DB 이름을 붙이는 것만으로 충분하지 않을 수 있다.

 

기본 DB를 정해도 같은 서버의 다른 DB에 권한이 있다면 other_db.table_name으로 접근할 수 있다. 기본 DB 선택은 접근 권한 제한과 다르다.

 

설정명도 주의하자. Boot 표준 자동 설정은 spring.datasource.url, HikariDataSource에 직접 바인딩하는 구성은 해당 설정 경로의 jdbc-url을 확인한다.

5. default_schema나 default_catalog만 설정하면 될까?

‘테이블명에 DB 이름을 붙이기’와 ‘연결의 기본 DB 선택’은 다른 작업이다.

설정 역할
JDBC URL의 /app_db 연결 시 기본 DB 선택
spring.datasource.hikari.catalog 풀 연결의 기본 catalog 설정
hibernate.default_catalog SQL 객체명의 기본 catalog 지정
@Table(catalog = "app_db") 해당 테이블의 catalog 매핑

Hibernate 설정이나 엔티티 매핑으로 SQL에 DB 이름이 붙어도, 연결의 DATABASE()가 자동으로 설정되는 것은 아니다.

Connector/J의 기본값인 databaseTerm=CATALOG에서는 setSchema()가 동작하지 않는다. 따라서 default_schema나 Hikari의 schema를 무작정 적용하는 것도 피하자.

 

URL을 바꾸기 어려운 일반적인 HikariCP 구성에서는 다음을 검토할 수 있다.

spring:
  datasource:
    hikari:
      catalog: app_db

이는 Connector/J의 기본 catalog 모드를 전제로 한다. databaseTerm=SCHEMA를 별도로 지정했다면 설정과 API를 다시 확인해야 한다.

 

기본 catalog 모드에서 직접 DB를 전환할 때는 Connector/J가 권고하는 Connection.setCatalog()를 사용한다. DB 콘솔에서 실행한 USE app_db는 애플리케이션의 다른 연결까지 바꾸지 않는다. 커넥션 풀·멀티테넌트 환경에서는 연결별 선택과 반환 시 상태 복구도 관리해야 한다.

6. 기본 DB를 비워야 한다면 SQL 우회 검토

직접 관리하는 단순 삭제라면 DB 이름을 명시한 별칭 없는 native SQL을 검토할 수 있다.

DELETE FROM app_db.sample WHERE id = ?;

조인 삭제를 무조건 이 형태로 바꿀 수는 없으며, JPA 영속성 컨텍스트와 삭제 부수효과도 점검해야 한다. 생성 SQL을 정규식으로 고치거나 Hibernate를 무작정 다운그레이드하기보다는 연결 설정을 먼저 바로잡는 편을 권한다.

7. 배포 전에는 같은 연결에서 확인하자

문제가 난 애플리케이션 연결에서 다음 값을 확인한다.

SELECT VERSION(), DATABASE(), CONNECTION_ID();

DATABASE()가 NULL인지, 실패 SQL이 DELETE 별칭 FROM ...인지 비교한다. 테스트 환경에서는 logging.level.org.hibernate.SQL=DEBUG로 SQL을 확인할 수 있다.

 

회귀 테스트에는 개별 엔티티 삭제와 JPQL 벌크 삭제를 모두 포함하자. 테스트용 테이블에서 기본 DB 미선택·선택 조건을 나누고, 운영과 같은 MySQL 버전으로 검증하는 것이 좋다.

 

결국 이 문제는 ‘Hibernate가 생성한 SQL 형태’와 ‘MySQL 연결의 기본 DB 상태’가 만날 때 발생한다. 업그레이드를 피할 일이 아니라, JDBC URL과 실제 DELETE SQL을 함께 점검할 일이다.

참고 자료

 

728x90
반응형