使用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;