다니는 회사의 주요 고객사가, 업무 중요도가 낮은 시스템에는 PostgreSQL을 사용하는 관계로, 일에서 MySQL은 써 본 적 없지만 PostgreSQL은 썼다. 더불어 실제로도 PostgreSQL(이하 pgsql)이 괜찮은 DBMS라 PC에 깔아 놓고 코딩할 때 사용하고 있다.
증상
Spring Boot도 안 익숙하고, 하는 김에 곧 나올 2.0으로 배우려는 목적으로, ‘스프링부트로 웹서비스 구축하기‘라는 글을 Spring Boot 2.0 기반으로 바꿔 돌려 보고 있다. Hibernate를 구현체로 쓰는 Spring Boot Starter JPA + H2 Database(in-memory db) 기반의 코드를 작성 및 테스트 통과하고 이 코드를 MariaDB에서 돌렸더니 한 방에 테스트를 통과했다. ‘역시 DB 독립적인 코딩을 하려면 ORM이여~’ 어쩌구 하면서 이 코드를 pgsql에서 돌리는 순간, 난생 처음 보는 Exception이 나면서 테스트 통과하는데 실패했다. 정말 해 보기 전엔 모른다.
Caused by: java.sql.SQLFeatureNotSupportedException: Method org.postgresql.jdbc.PgConnection.createClob() is not yet implemented. at org.postgresql.Driver.notImplemented(Driver.java:683) ~[postgresql-42.2.1.jar:42.2.1] at org.postgresql.jdbc.PgConnection.createClob(PgConnection.java:1252) ~[postgresql-42.2.1.jar:42.2.1] ... 91 common frames omitted
멘붕이었다.
‘헐~. 정말 createClob() 메소드를 구현 안하고 JDBC 드라이버를 낸 거야?’
원인
믿을 수가 없어 pgsql의 JDBC Driver(이후 pgJDBC) 소스를 봤다. (버전은 42.2.1)
@Override public Clob createClob() throws SQLException { checkClosed(); throw org.postgresql.Driver.notImplemented(this.getClass(), "createClob()"); }
‘어처구니가 없구먼?’
해결책
9.3-1104-jdbc41 버전의 pgJDBC를 쓰면 된다. 그런데 pgJDBC 버전만 내리고 Hibernate Dialect를 “명시”하지 않을 경우 Hibernate Dialect를 못 찾겠다는 Exception이 터지면서 테스트를 실패한다.
Caused by: org.hibernate.HibernateException: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set at org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl.determineDialect(DialectFactoryImpl.java:100) at org.hibernate.engine.jdbc.dialect.internal.DialectFactoryImpl.buildDialect(DialectFactoryImpl.java:54)
즉 spring boot 애플리케이션 설정 파일 (yml 형식을 썼다)에 다음과 같이 Hibernate Dialect를 명시하는 것도 필요하다.
spring: jpa: database-platform: org.hibernate.dialect.PostgesSQL95Dialect
H2, MariaDB는 Hibernate Dialect를 명시하지 않아도 문제 없다. 사실 Spring Boot의 디자인 철학이 CoC(Convention Over Configuration)이기도 하고.
PostgreSQL Korea 관리자이신 김상기 님 도움으로 취소선 그은 것보다 더 우아한 해결책을 찾아 수정한다. spring boot 애플리케이션 설정에
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false
property를 추가하면, 최신 pgJDBC Driver 사용도 가능하고, hibernate dialect 명시적 선언도 불필요하다. yml 형태의 sping boot 애플리케이션 설정은 아래와 같다.
spring: jpa: properties: hibernate: temp: use_jdbc_metadata_defaults: false
hibernate.temp.use_jdbc_metadata_defaults 프로퍼티 설명 및 디폴트 값은 Hibernate 5.2
User Guide의 Configuration 장(章)을 참고하시라. (한 줄 써 있어서 도움 될 지 모르겠지만…)
유의 사항
Spring Boot 버전 번호 보면 알겠지만 이 버전은 RC1이므로 향후 이 문제가 안 생길 수도 있다. 이후에 이 글 내용이 쓸모 없어질 수 있다. 그런 의미에서 본 글 작성과 관련된 기반 소프트웨어 버전을 명시함으로써 혼란을 방지하고자 한다. & 이 글 쓴 날짜도 꼭 유념해 주셨으면 한다.
- OS: Windows 7
- JDK : 1.8.0_162
- Gradle : 4.5
- Spring Boot : 2.0.0.RC1
- IDE: IntelliJ IDEA Community Edition : 2017.3.4
pgJDBC: 9.3-1104-jdbc41 (최신 버전은 42.2.1)- pgJDBC: 42.2.1
- PostgreSQL DBMS: 10.1.3
다만 이 해결책은 옛날 pgJDBC를 쓰는 것이라 찜찜하긴 하다. 더불어 왜 pgJDBC 제작자는 최신 버전 구현 시 당당하게 해당 메소드를 구현 안했다고 해 놨을 지도 매우 궁금하다. 최신 pgJDBC 적용하면서 위 문제를 피할 수 있는 방법을 아시는 분의 고견, 언제나 환영한다.
개인적 생각이나, Spring Boot 2.0 정식 버전이 나와도 pgJDBC를 쓰려면 상기 property 추가가 필수적일 듯 하다. 더불어 우아한 방법을 알려주신 김상기님께도 다시 한 번 감사 인사를 드린다.
Happy Coding!
감사합니다.
덕분에 잘 해결 했습니다.
그나저나 “드러누울 때” 라니 표현이 재미있네요 ㅋㅋ
어휴 제 IDE도 드러누웠는데 일으켜주셔서 감사합니다