HibernateでPostgreSQLに接続する際の初期化時エラー



HibernateでPostgreSQLを利用する場合に、アプリケーションの初期化時に以下のようなエラーが出たので調査した結果をまとめます。

結論から言うと、無視して良さそうなエラーでした。


```
java.sql.SQLFeatureNotSupportedException: org.postgresql.jdbc.PgConnection.createClob() メソッドはまだ実装されていません。
        at org.postgresql.Driver.notImplemented(Driver.java:646) ~[postgresql-9.4.1208.jar:9.4.1208]
        at org.postgresql.jdbc.PgConnection.createClob(PgConnection.java:1281) ~[postgresql-9.4.1208.jar:9.4.1208]
        at org.apache.commons.dbcp2.DelegatingConnection.createClob(DelegatingConnection.java:868) ~[commons-dbcp2-2.1.1.jar:2.1.1]
        at org.apache.commons.dbcp2.DelegatingConnection.createClob(DelegatingConnection.java:868) ~[commons-dbcp2-2.1.1.jar:2.1.1]
        at net.sf.log4jdbc.ConnectionSpy.createClob(ConnectionSpy.java:496) ~[log4jdbc-remix-0.2.7.jar:na]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_77]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_77]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_77]
        at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_77]
        at org.hibernate.engine.jdbc.internal.LobCreatorBuilder.useContextualLobCreation(LobCreatorBuilder.java:112) [hibernate-core-4.3.11.Final.jar:4.3.11.Final]
        at org.hibernate.engine.jdbc.internal.LobCreatorBuilder.<init>(LobCreatorBuilder.java:63) [hibernate-core-4.3.11.Final.jar:4.3.11.Final]
        at org.hibernate.engine.jdbc.internal.JdbcServicesImpl.configure(JdbcServicesImpl.java:192) [hibernate-core-4.3.11.Final.jar:4.3.11.Final]
        at org.hibernate.boot.registry.internal.StandardServiceRegistryImpl.configureService(StandardServiceRegistryImpl.java:111) [hibernate-core-4.3.11.Final.jar:4.3.11.Final]
        at org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:234) [hibernate-core-4.3.11.Final.jar:4.3.11.Final]
        at org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:206) [hibernate-core-4.3.11.Final.jar:4.3.11.Final]
        at org.hibernate.cfg.Configuration.buildTypeRegistrations(Configuration.java:1887) [hibernate-core-4.3.11.Final.jar:4.3.11.Final]
        at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1845) [hibernate-core-4.3.11.Final.jar:4.3.11.Final]
        at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:857) [hibernate-entitymanager-4.3.11.Final.jar:4.3.11.Final]
        at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:850) [hibernate-entitymanager-4.3.11.Final.jar:4.3.11.Final]
        at org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl.withTccl(ClassLoaderServiceImpl.java:425) [hibernate-core-4.3.11.Final.jar:4.3.11.Final]
        at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:849) [hibernate-entitymanager-4.3.11.Final.jar:4.3.11.Final]
        at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60) [spring-orm-4.2.4.RELEASE.jar:4.2.4.RELEASE]
        at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:343) [spring-orm-4.2.4.RELEASE.jar:4.2.4.RELEASE]
        at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:318) [spring-orm-4.2.4.RELEASE.jar:4.2.4.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637) [spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574) [spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545) [spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) [spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) [spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) [spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) [spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) [spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
        at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1054) [spring-context-4.2.4.RELEASE.jar:4.2.4.RELEASE]
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:829) [spring-context-4.2.4.RELEASE.jar:4.2.4.RELEASE]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538) [spring-context-4.2.4.RELEASE.jar:4.2.4.RELEASE]
        at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:446) [spring-web-4.2.4.RELEASE.jar:4.2.4.RELEASE]
        at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:328) [spring-web-4.2.4.RELEASE.jar:4.2.4.RELEASE]
        at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:107) [spring-web-4.2.4.RELEASE.jar:4.2.4.RELEASE]
        at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4813) [catalina.jar:8.0.35]
        at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5272) [catalina.jar:8.0.35]
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:147) [catalina.jar:8.0.35]
        at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:725) [catalina.jar:8.0.35]
        at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:701) [catalina.jar:8.0.35]
        at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:717) [catalina.jar:8.0.35]
        at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:940) [catalina.jar:8.0.35]
        at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1816) [catalina.jar:8.0.35]
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_77]
        at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_77]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_77]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_77]
        at java.lang.Thread.run(Thread.java:745) [na:1.8.0_77]
```


# 環境

* PostgreSQL 9.4.5
* PostgreSQL JDBC Driver postgresql-9.4.1208.jar
* Hibernate 4.3.11
* OpenJDK 1.8.0.91

# スタックトレースからわかること

JDBCのcreateClob APIを実行してSQLFeatureNotSupportedExceptionが発生しています。
PostgreSQLにはCLOB型は無いので、妥当といえば妥当です。

# PostgreSQL JDBC Driverのソースコード

以下のように、createClog()が呼ばれるとSQLFeatureNotSupportedExceptionが投げられるようになっています。

```org.postgresql.jdbc.PgConnection.java
  public Clob createClob() throws SQLException {
    checkClosed();
    throw org.postgresql.Driver.notImplemented(this.getClass(), "createClob()");
  }
```

```org.postgresql.Driver.java
  public static SQLFeatureNotSupportedException notImplemented(Class<?> callClass,
      String functionName) {
    return new SQLFeatureNotSupportedException(
        GT.tr("Method {0} is not yet implemented.", callClass.getName() + "." + functionName),
        PSQLState.NOT_IMPLEMENTED.getState());
  }
```

# Hibernateのソースコード

どうやらJDBC接続の初期化時に、JDBC APIが実装されているかをチェックしてフラグを立てているだけの様子。ということで、エラーになってもそのJDBC APIは使わないようになるはず。

```org/hibernate/engine/jdbc/internal/LobCreatorBuilder.java
 69         /**                             
 70          * Basically here we are simply checking whether we can call the {@link Connection} methods for
 71          * LOB creation added in JDBC 4.  We not only check whether the {@link Connection} declares these methods,
 72          * but also whether the actual {@link Connection} instance implements them (i.e. can be called without simply
 73          * throwing an exception).
 74          *                      
 75          * @param jdbcConnection The connection which can be used in level-of-support testing.
 76          *              
 77          * @return True if the connection can be used to create LOBs; false otherwise.
 78          */                     
 79         @SuppressWarnings("unchecked")
 80         private static boolean useContextualLobCreation(Map configValues, Connection jdbcConnection) {
 81                 final boolean isNonContextualLobCreationRequired =
 82                                 ConfigurationHelper.getBoolean( Environment.NON_CONTEXTUAL_LOB_CREATION, configValues );
 83                 if ( isNonContextualLobCreationRequired ) {
 84                         LOG.disablingContextualLOBCreation( Environment.NON_CONTEXTUAL_LOB_CREATION );
 85                         return false;
 86                 }
 87                 if ( jdbcConnection == null ) {
 88                         LOG.disablingContextualLOBCreationSinceConnectionNull();
 89                         return false;
 90                 }
<<略>>
107                         if ( createClobMethod.getDeclaringClass().equals( Connection.class ) ) {
108                                 // If we get here we are running in a jdk 1.6 (jdbc 4) environment...
109                                 // Further check to make sure the driver actually implements the LOB creation methods.  We
110                                 // check against createClob() as indicative of all; should we check against all 3 explicitly?
111                                 try {
112                                         final Object clob = createClobMethod.invoke( jdbcConnection, NO_ARGS );
113                                         try {
114                                                 final Method freeMethod = clob.getClass().getMethod( "free", NO_ARG_SIG );
115                                                 freeMethod.invoke( clob, NO_ARGS );
116                                         }
117                                         catch ( Throwable ignore ) {
118                                                 LOG.tracef( "Unable to free CLOB created to test createClob() implementation : %s",     ignore );
119                                         }
120                                         return true;
121                                 }
122                                 catch ( Throwable t ) {
123                                         LOG.disablingContextualLOBCreationSinceCreateClobFailed( t );
124                                 }
125                         }
126                 }
127                 catch ( NoSuchMethodException ignore ) {
128                 }
129 
130                 return false;
131         }
<<略>>
```

```org/hibernate/engine/jdbc/internal/LobCreatorBuilder.java
 56          * The public factory method for obtaining the appropriate (according to given JDBC {@link java.sql.Connection}.
 57          *                              
 58          * @param configValues The map of settings
 59          * @param jdbcConnection A JDBC {@link java.sql.Connection} which can be used to gauge the drivers level of support,
 60          * specifically for creating LOB references.
 61          */                             
 62         public LobCreatorBuilder(Map configValues, Connection jdbcConnection) {
 63                 this.useContextualLobCreation = useContextualLobCreation( configValues, jdbcConnection );
 64         } 
```

```org/hibernate/engine/jdbc/internal/JdbcServicesImpl.java
 70 /**
 71  * Standard implementation of the {@link JdbcServices} contract
 72  *
 73  * @author Steve Ebersole
 74  */
 75 public class JdbcServicesImpl implements JdbcServices, ServiceRegistryAwareService, Configurable {
 <<略>>
 91 
 92         @Override
 93         public void configure(Map configValues) {
<<略>>
192                                         lobCreatorBuilder = new LobCreatorBuilder( configValues, connection );
```


```org/hibernate/cfg/AvailableSettings.java
480         /**
481          * Should we not use contextual LOB creation (aka based on {@link java.sql.Connection#createBlob()} et al).
482          */                     
483         String NON_CONTEXTUAL_LOB_CREATION = "hibernate.jdbc.lob.non_contextual_creation";
```
ここまで読んでググったところ以下のページを発見！

Spring Boot でメール送信する Web アプリケーションを作る ( その３ )( Project の作成 )[かんがるーさんの日記]
http://ksby.hatenablog.com/entry/2015/04/12/113912

上記のリンク先に書かれているように
hibernate.propertiesに
```
hibernate.jdbc.lob.non_contextual_creation = true
```
と設定すると当初のエラーが出なくなりました。


以上！