Spring Boot 2系にバージョンアップしたらH2がエラーを吐き出したときの対処


概要

Spring Boot 1.5系からSpring Boot 2系にバージョンアップしたときにH2がエラーを吐き出したので調査しました。
ここでは調査して分かったこととエラーをどう対処したかを書きます。

問題が起きたときの各ライブラリのバージョン

ライブラリ バージョン
Spring Boot 2.1.6
H2 1.4.192

問題

エラーの内容は以下。
isWrapperForがサポートされていないため例外を吐いている模様。

2020-01-16 00:09:52 JDBCX: exception
org.h2.jdbc.JdbcSQLException: 機能はサポートされていません: "isWrapperFor"
Feature not supported: "isWrapperFor" [50100-192]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
    at org.h2.message.DbException.get(DbException.java:179)
    at org.h2.message.DbException.get(DbException.java:155)
    at org.h2.message.DbException.getUnsupportedException(DbException.java:216)
    at org.h2.message.TraceObject.unsupported(TraceObject.java:375)
    at org.h2.jdbcx.JdbcDataSource.isWrapperFor(JdbcDataSource.java:422)
    at org.springframework.boot.jdbc.DataSourceUnwrapper.safeUnwrap(DataSourceUnwrapper.java:77)
    at org.springframework.boot.jdbc.DataSourceUnwrapper.unwrap(DataSourceUnwrapper.java:56)
    at org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration$HikariPoolDataSourceMetadataProviderConfiguration.lambda$hikariPoolDataSourceMetadataProvider$0(DataSourcePoolMetadataProvidersConfiguration.java:66)
    at org.springframework.boot.jdbc.metadata.CompositeDataSourcePoolMetadataProvider.getDataSourcePoolMetadata(CompositeDataSourcePoolMetadataProvider.java:50)
   ...

調査

エラーログから、SpringのコードのDataSourceUnwrapper#safeUnwrapメソッドで例外が起きているようなので見てみます。
https://github.com/spring-projects/spring-boot/blob/2.1.x/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/jdbc/DataSourceUnwrapper.java#L77

private static <S> S safeUnwrap(Wrapper wrapper, Class<S> target) {
    try {
        if (wrapper.isWrapperFor(target)) {
            return wrapper.unwrap(target);
        }
    }
    catch (Exception ex) {
        // Continue
    }
    return null;
}

isWrapperというメソッドが使われてました。isWrapperForとはメソッドのことですね。H2ではこのメソッドのサポートがないみたいです。
そしてH2のchangelogを確認してみたところ、それを裏付ける記述がありました。

PR #844: Add simple implementations of isWrapperFor() and unwrap() to JdbcDataSource

バージョン1.4.197でisWrapperForメソッドの実装がJdbcDataSourceに追加されたみたいです。どうやらSpring Boot 2.1.6ではisWrapperForメソッドありきの実装になっていますが、H2のバージョンが1.4.192のため、このメソッドがなく例外となっていたようでした。

一応Spring Boot 2.1.6はH2のどのバージョンに依存しているのか確認します。
Maven Repositoryを見ると、まずSpring Boot(Spring Boot JDBC Starter) 2.1.6Spring JDBC 5.1.8に依存しており、そしてSpring JDBC 5.1.8がH2 1.4.199に依存しています。つまり、Spring Boot 2.1.6はH2 1.4.199に依存しているのでこれで辻褄が合いました。

ちなみに、Spring Boot 1.5系でエラーとなっていなかった理由はそもそもDataSourceUnwrapper#safeUnwrapメソッドがなく、isWrapperForメソッドが呼ばれないためでした。

解決策

H2のバージョンを1.4.199に上げました。

pom.xml
    ...
    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <version>1.4.199</version>
    </dependency>
    ...

これでエラーを吐き出さなくなりました。

まとめ

今回はSpring Boot 2系にバージョンアップした際のH2のエラーの原因調査と解決を行いました。
最終的な各ライブラリのバージョンは以下になります。

ライブラリ バージョン
Spring Boot 2.1.6
H2 1.4.199

教訓としては、フレームワークやライブラリをバージョンアップした際は依存ライブラリのバージョンと前のバージョンからの変更点をchangelogやリリースノートで確認しましょう。これでどんな変更によって挙動が変わったかどうかをある程度チェックできると思います。
maven等を利用してライブラリ管理をしている場合はMavenRepositoryを確認して依存ライブラリのバージョンをチェックできます。MavenRepositoryであればリンクを辿ってすぐに調べられます。
changelogやリリースノートも公式URLやGithubに載っていることが多いので見てみると良いと思います。