天天看点

使用SAP HANA库启动Spring Boot Jpa应用异常处理

使用SAP HANA库启动Spring Boot Jpa应用异常处理

  • 异常一 com.sap.db.jdbc.exceptions.SQLFeatureNotSupportedExceptionSapDB: Method createClob() of Connection is not supported.

    源码跟踪:

    类com.sap.db.jdbc.ConnectionSapDB

public synchronized Clob createClob() throws SQLException {
    boolean on = this._tracer.on();
    boolean pon = this._tracer.pon();
    TraceRecord r = pon ? this._newTraceRecord("createClob") : null;

    try {
      // JDBC Trace 默认值为false,开启需要数据库系统用户在数据库服务端进行命令化设置,或通过
      // jdbc连接参数中traceOptions及traceFile设置开启,参数介绍详见:
      // https://help.sap.com/viewer/f1b440ded6144a54ada97ff95dac7adf/2.5/en-US/109397c2206a4ab2a5386d494f4cf75e.html					
      if (on) {
        this._tracer.printCall(this, "createClob", new Object[0]);
      }
	  // 未开启trace的场景下直接抛异常
      throw _getUnsupportedMethodException("createClob()");
    } catch (SQLException var8) {
      if (on) {
        this._tracer.printException(var8);
      }
      if (pon) {
        r.setException(var8);
      }
      throw var8;
    } finally {
      if (pon) {
        this._publish(r);
      }
    }
  }
           

类org.hibernate.engine.jdbc.env.internal.LobCreatorBuilderImpl

private static boolean useContextualLobCreation(Map configValues, Connection jdbcConnection) {
		// 是否上下文无关Lob创建,由选项hibernate.jdbc.lob.non_contextual_creation控制
		// 默认为false,JDBC4及以上版本通过反射调用Connection的实现类HanaConnectionFinalize
		// 继承自ConnectionSapDB的createClob方法实现
		// 所以此处有一个解决思路就是设置hibernate.jdbc.lob.non_contextual_creation=true,跳过jdbc元数据检查。
		final boolean isNonContextualLobCreationRequired =
				ConfigurationHelper.getBoolean( Environment.NON_CONTEXTUAL_LOB_CREATION, configValues );
		if ( isNonContextualLobCreationRequired ) {
			LOG.disablingContextualLOBCreation( Environment.NON_CONTEXTUAL_LOB_CREATION );
			return false;
		}
		if ( jdbcConnection == null ) {
			LOG.disablingContextualLOBCreationSinceConnectionNull();
			return false;
		}
		try {
			try {
				final DatabaseMetaData meta = jdbcConnection.getMetaData();
				// if the jdbc driver version is less than 4, it shouldn't have createClob
				if ( meta.getJDBCMajorVersion() < 4 ) {
					LOG.disablingContextualLOBCreationSinceOldJdbcVersion( meta.getJDBCMajorVersion() );
					return false;
				}
			}
			catch ( SQLException ignore ) {
				// ignore exception and continue
			}
			final Class connectionClass = Connection.class;
			final Method createClobMethod = connectionClass.getMethod( "createClob", NO_ARG_SIG );
			if ( createClobMethod.getDeclaringClass().equals( Connection.class ) ) {
				// If we get here we are running in a jdk 1.6 (jdbc 4) environment...
				// Further check to make sure the driver actually implements the LOB creation methods.  We
				// check against createClob() as indicative of all; should we check against all 3 explicitly?
				try {
					final Object clob = createClobMethod.invoke( jdbcConnection, NO_ARGS );
					try {
						final Method freeMethod = clob.getClass().getMethod( "free", NO_ARG_SIG );
						freeMethod.invoke( clob, NO_ARGS );
					}
					catch ( Throwable ignore ) {
						LOG.tracef( "Unable to free CLOB created to test createClob() implementation : %s", ignore );
					}
					return true;
				}
				catch ( Throwable t ) {
					LOG.disablingContextualLOBCreationSinceCreateClobFailed( t );
				}
			}
		}
		catch ( NoSuchMethodException ignore ) {
		}
		return false;
	}
           

类org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator

public JdbcEnvironment initiateService(Map configurationValues, ServiceRegistryImplementor registry) {
		final DialectFactory dialectFactory = registry.getService( DialectFactory.class );
		// 'hibernate.temp.use_jdbc_metadata_defaults' is a temporary magic value.
		// The need for it is intended to be alleviated with future development, thus it is
		// not defined as an Environment constant...
		//
		// it is used to control whether we should consult the JDBC metadata to determine
		// certain Settings default values; it is useful to *not* do this when the database
		// may not be available (mainly in tools usage).
		// hibernate 默认进行jdbc 元数据检查,如采用连接池,可屏蔽此检查
		// 设置hibernate.temp.use_jdbc_metadata_defaults = false为此问题第二种解决方案
		boolean useJdbcMetadata = ConfigurationHelper.getBoolean(
				"hibernate.temp.use_jdbc_metadata_defaults",
				configurationValues,
				true
		);
		if ( useJdbcMetadata ) {
			final JdbcConnectionAccess jdbcConnectionAccess = buildJdbcConnectionAccess( configurationValues, registry );
			try {
				final Connection connection = jdbcConnectionAccess.obtainConnection();
				try {
					final DatabaseMetaData dbmd = connection.getMetaData();
					if ( log.isDebugEnabled() ) {
						log.debugf(
								"Database ->\n"
										+ "       name : %s\n"
										+ "    version : %s\n"
										+ "      major : %s\n"
										+ "      minor : %s",
								dbmd.getDatabaseProductName(),
								dbmd.getDatabaseProductVersion(),
								dbmd.getDatabaseMajorVersion(),
								dbmd.getDatabaseMinorVersion()
						);
						log.debugf(
								"Driver ->\n"
										+ "       name : %s\n"
										+ "    version : %s\n"
										+ "      major : %s\n"
										+ "      minor : %s",
								dbmd.getDriverName(),
								dbmd.getDriverVersion(),
								dbmd.getDriverMajorVersion(),
								dbmd.getDriverMinorVersion()
						);
						log.debugf( "JDBC version : %s.%s", dbmd.getJDBCMajorVersion(), dbmd.getJDBCMinorVersion() );
					}

					Dialect dialect = dialectFactory.buildDialect(
							configurationValues,
							new DialectResolutionInfoSource() {
								@Override
								public DialectResolutionInfo getDialectResolutionInfo() {
									try {
										return new DatabaseMetaDataDialectResolutionInfoAdapter( connection.getMetaData() );
									}
									catch ( SQLException sqlException ) {
										throw new HibernateException(
												"Unable to access java.sql.DatabaseMetaData to determine appropriate Dialect to use",
												sqlException
										);
									}
								}
							}
					);
					return new JdbcEnvironmentImpl(
							registry,
							dialect,
							dbmd
					);
				}
				catch (SQLException e) {
					log.unableToObtainConnectionMetadata( e.getMessage() );
				}
				finally {
					try {
						jdbcConnectionAccess.releaseConnection( connection );
					}
					catch (SQLException ignore) {
					}
				}
			}
			catch (Exception e) {
				log.unableToObtainConnectionToQueryMetadata( e.getMessage() );
			}
		}

		// if we get here, either we were asked to not use JDBC metadata or accessing the JDBC metadata failed.
		return new JdbcEnvironmentImpl( registry, dialectFactory.buildDialect( configurationValues, null ) );
	}
           

解决方案:

spring:
  jpa:
    # 控制台显示SQL
    show-sql: true
    # 自动生成表结构
    generate-ddl: true
    hibernate:
      ddl-auto: update
    # hibernate数据库方言设置 spring.jpa.database-platform,值不允许为""
    #database-platform
    # HANA数据库下需关闭jdbc metadata 检查,避免因数据库未设置trace出现createClob检查异常
    properties:
      hibernate:
        jdbc:
          lob:
            non_contextual_creation: true
           
  • 异常二 boolean类型映射,Oracle,MySQL等数据库下对Java中boolean类型字段映射存储为0和1,SAP HANA中默认映射存储为true和false,如需保持一致表现,需通过hibernate注解实现
import org.hibernate.annotations.Type;

@Type(type = "boolean")
private boolean enabled;