-
Schema-validation: missing table [name] 에러개발/JPA & Hibernate 2022. 3. 31. 03:43
결론부터 보기
현재 MySQL 5.7, MariaDB JDBC Driver 그리고 Spring Data JPA (Hibernate 5.67) 을 사용하고 있다.
Spring Data JPA 를 사용하다보면 hibernate
ddl-auto
라는 옵션을 사용할 수 있다.옵션에는 validate, create, create-drop, update, none 등이 있다.
그리고 현재 validate 옵션을 사용 중이다.
validate : 서버를 기동할 때 JPA Entity 클래스와 DB 스키마를 비교하여 [테이블, 컬럼, id generator] 가 유효한지 검사하는 옵션이다.
문제 현상
분명 테이블이 있는데, 테이블이 없다고 한다.
validate 옵션을 설정해놓아서 서버를 기동하니 schema validation 이 진행된다.
그런데 다음과 같은 에러가 발생했다.
.... Schema-validation: missing table [players]
아무리 눈을 씻고 보아도
players
테이블은 존재했다.또, ddl-auto 옵션을
update
로 변경해보았더니, 이미 players 테이블이 존재한다는 메시지가 출력되었다.확실히 이상했다.
1차 디버깅 (대소문자 문제)
문제가 발생한 곳으로 이동해보니 다음과 같이
tableInformation
이 null 이라 발생한 예외다.tableInformation 을 추적해보니
tables
를 통해서 가져오고 있었다. (테이블들의 정보를 담고 있는 변수)tables 를 어떻게 가져오는지 알기위해 쭉 따라 들어갔다. (databaseInformation.getTablesInformation(...) 로 이동)
)
extractionContext.getJdbcDatabaseMetaData().getTables(...) 로 이동
DB 테이블의 스키마 정보를 얻기 위해서 catalog(데이터베이스 이름) 을 이용해서 meta 정보를 조회하는 쿼리를 만든다는 사실을 알게되었다.
다음과 같은 url 로 DB에 연결한다고 가정하자.
jdbc:mysql://localhost:5430/football_team
-> football_team 이 데이터베이스 이름그런데 여기서 유의할 점은 catalog 가 upper case (대문자) 로 저장되어 있다는 사실이다.
1차적인 문제점은 파악이 되었다.
- MySQL 기본설정에 따르면 대소문자를 구분해서 disk 에 DB 또는 테이블을 저장하고 비교한다.
(Unix 기준 lower_case_table_names = 0 이 default. 공식문서 참고) - 디버깅 결과 대문자 이름으로 DB를 조회하고 있다.
- 그러나 실제 DB 이름은 소문자로 되어있어서 조회가 안된다.
2차 디버깅 (hibernate 의 대소문자 정책 문제)
여기서 생기는 궁금증은 jdbc url에 소문자로 기입했음에도 왜 catalog에는 대문자로 DB 이름을 저장하는지 이다.
그래서 문제의 catalog 가 어떻게 만들어졌는지 역추적 해보았다.
아까와 비슷한 방법으로 추적하다보니..
unquotedCaseStrategy
에 따라서 데이터베이스 이름의 대소문자를 정한다는 것을 알 수 있었다.주석에도 적혀있듯 default 가 upper case 이다.
하지만 나는 upper case (대문자)를 원하지 않으므로 이 설정을 바꿀 방법을 찾기 위해서 좀 더 들어갔다.
우선 위에 클래스 (
NormalizingIdentifierHelperImpl
) 를 생성하는 곳으로 이동했다.IdentifierHelperBuilder
에서 생성하고 있었다.unqoutedCaseStrategy
값을 역추적해보니,metaData.storesLowerCaseIdentifieres()
에 따라 결정되는 것을 알게 되었다.metaData.storesLowerCaseIdentifiers() 를 타고 들어가서 보니,
커넥션 객체의
getLowercaseTableNames()
에 의해 결정된다는 사실을 알게되었다.(리턴 값이 1이면 내가 원하는대로 소문자를 얻을 수 있다.)
마지막으로, 열쇠가 되는
lowercaseTableNames
라는 값을 어떤 쿼리로 조회하는지 알게되었다.MySQL에 있는
@@lower_case_table_names
변수를 조회하고 있다.저 변수의 값이 1이 되어야 hibernate 가 소문자로 취급하는데, 현재 로컬 MySQL 기본값이 0 으로 되어있어서 대문자로 가져온 것이다.
결론
상황을 정리하자면
- hibernate 설정 (ddl-auto : validate) 때문에 유효성 검사를 한다.
- (Unix기준) MySQL 기본 설정때문에 대소문자를 구분하여 조회하고 있다.
- DB 이름이 소문자인데, Hibernate 가 자꾸 이것을 대문자로 변환한다. (default)
- 그 이유는 Hibernate가 MySQL 의
@@lower_case_table_names
의 설정을 따라가기 때문이다.
Missing Table 문제를 해결하려면
- 데이터베이스에 테이블이 존재하는지 확인한다.
- MySQL
@@lower_case_table_names
설정을 확인한다.@@lower_case_table_names
를 1로 설정하면- DB나 테이블을 생성하면 low case 로 생성된다.
- hibernate 에서도 소문자로 읽어들인다.
@@lower_case_table_names
값 변경 방법설정하는 방법은
/etc/mysql/conf.d
파일을 수정하면 된다.[mysqld] lower_case_table_names = 1
- MySQL 기본설정에 따르면 대소문자를 구분해서 disk 에 DB 또는 테이블을 저장하고 비교한다.