HHH-16321 - Hibernate maps NCLOB to ntext on Sybase

This commit is contained in:
Steve Ebersole 2023-04-25 20:13:13 -05:00
parent 48c59392f6
commit 06381d2dd2
13 changed files with 549 additions and 327 deletions

View File

@ -47,6 +47,7 @@ import jakarta.persistence.TemporalType;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.type.SqlTypes.BOOLEAN; import static org.hibernate.type.SqlTypes.BOOLEAN;
import static org.hibernate.type.SqlTypes.DATE; import static org.hibernate.type.SqlTypes.DATE;
import static org.hibernate.type.SqlTypes.NCLOB;
import static org.hibernate.type.SqlTypes.TIME; import static org.hibernate.type.SqlTypes.TIME;
import static org.hibernate.type.SqlTypes.TIMESTAMP; import static org.hibernate.type.SqlTypes.TIMESTAMP;
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE; import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
@ -96,18 +97,26 @@ public class SybaseASEDialect extends SybaseDialect {
@Override @Override
protected String columnType(int sqlTypeCode) { protected String columnType(int sqlTypeCode) {
switch ( sqlTypeCode ) { switch ( sqlTypeCode ) {
case BOOLEAN: case BOOLEAN: {
// On Sybase ASE, the 'bit' type cannot be null, // On Sybase ASE, the 'bit' type cannot be null,
// and cannot have indexes (while we don't use // and cannot have indexes (while we don't use
// tinyint to store signed bytes, we can use it // tinyint to store signed bytes, we can use it
// to store boolean values) // to store boolean values)
return "tinyint"; return "tinyint";
case DATE: }
case DATE: {
return "date"; return "date";
case TIME: }
case TIME: {
return "time"; return "time";
default: }
case NCLOB: {
// Sybase uses `unitext` instead of the T-SQL `ntext` type name
return "unitext";
}
default: {
return super.columnType( sqlTypeCode ); return super.columnType( sqlTypeCode );
}
} }
} }

View File

@ -179,8 +179,12 @@ public class SybaseDialect extends AbstractTransactSQLDialect {
jdbcTypeRegistry.addDescriptor( Types.NVARCHAR, ClobJdbcType.CLOB_BINDING ); jdbcTypeRegistry.addDescriptor( Types.NVARCHAR, ClobJdbcType.CLOB_BINDING );
} }
else { else {
// Some Sybase drivers cannot support getClob. See HHH-7889 // jConnect driver only conditionally supports getClob/getNClob depending on a server setting. See
// - https://help.sap.com/doc/e3cb6844decf441e85e4670e1cf48c9b/16.0.3.6/en-US/SAP_jConnect_Programmers_Reference_en.pdf
// - https://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.infocenter.dc20155.1570/html/OS_SDK_nf/CIHJFDDH.htm
// - HHH-7889
jdbcTypeRegistry.addDescriptor( Types.CLOB, ClobJdbcType.STREAM_BINDING_EXTRACTING ); jdbcTypeRegistry.addDescriptor( Types.CLOB, ClobJdbcType.STREAM_BINDING_EXTRACTING );
jdbcTypeRegistry.addDescriptor( Types.NCLOB, ClobJdbcType.STREAM_BINDING_EXTRACTING );
} }
jdbcTypeRegistry.addDescriptor( Types.BLOB, BlobJdbcType.PRIMITIVE_ARRAY_BINDING ); jdbcTypeRegistry.addDescriptor( Types.BLOB, BlobJdbcType.PRIMITIVE_ARRAY_BINDING );

View File

@ -17,6 +17,7 @@ public interface LobCreationContext {
/** /**
* The callback contract for making use of the JDBC {@link Connection}. * The callback contract for making use of the JDBC {@link Connection}.
*/ */
@FunctionalInterface
interface Callback<T> { interface Callback<T> {
/** /**
* Perform whatever actions are necessary using the provided JDBC {@link Connection}. * Perform whatever actions are necessary using the provided JDBC {@link Connection}.
@ -28,6 +29,10 @@ public interface LobCreationContext {
* @throws SQLException Indicates trouble accessing the JDBC driver to create the LOB * @throws SQLException Indicates trouble accessing the JDBC driver to create the LOB
*/ */
T executeOnConnection(Connection connection) throws SQLException; T executeOnConnection(Connection connection) throws SQLException;
default T from(Connection connection) throws SQLException {
return executeOnConnection( connection );
}
} }
/** /**
@ -40,4 +45,8 @@ public interface LobCreationContext {
* @return The LOB created by the callback. * @return The LOB created by the callback.
*/ */
<T> T execute(Callback<T> callback); <T> T execute(Callback<T> callback);
default <T> T fromContext(Callback<T> callback) {
return execute( callback );
}
} }

View File

@ -2,9 +2,9 @@
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/ */
package org.hibernate.engine.jdbc; package org.hibernate.engine.jdbc.env.internal;
import java.io.InputStream; import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
@ -15,23 +15,35 @@ import java.sql.NClob;
import java.sql.SQLException; import java.sql.SQLException;
import org.hibernate.JDBCException; import org.hibernate.JDBCException;
import org.hibernate.engine.jdbc.AbstractLobCreator;
import org.hibernate.engine.jdbc.LobCreationContext;
import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.NClobProxy;
import org.hibernate.engine.jdbc.NonContextualLobCreator;
/** /**
* {@link LobCreator} implementation using contextual creation against the JDBC {@link Connection} class's LOB creation * LobCreator which can use {@link Connection#createBlob} and {@link Connection#createClob},
* methods. * but {@link java.sql.NClob} references are created locally.
*
* @see NClobProxy
* *
* @author Steve Ebersole * @author Steve Ebersole
* @author Gail Badner
*/ */
public class ContextualLobCreator extends AbstractLobCreator implements LobCreator { public class BlobAndClobCreator extends AbstractLobCreator implements LobCreator {
private final LobCreationContext lobCreationContext;
/** /**
* Constructs a ContextualLobCreator * Callback for performing contextual BLOB creation
*
* @param lobCreationContext The context for performing LOB creation
*/ */
public ContextualLobCreator(LobCreationContext lobCreationContext) { public static final LobCreationContext.Callback<Blob> CREATE_BLOB_CALLBACK = Connection::createBlob;
/**
* Callback for performing contextual CLOB creation
*/
public static final LobCreationContext.Callback<Clob> CREATE_CLOB_CALLBACK = Connection::createClob;
protected final LobCreationContext lobCreationContext;
public BlobAndClobCreator(LobCreationContext lobCreationContext) {
this.lobCreationContext = lobCreationContext; this.lobCreationContext = lobCreationContext;
} }
@ -41,13 +53,13 @@ public class ContextualLobCreator extends AbstractLobCreator implements LobCreat
* @return The created BLOB reference. * @return The created BLOB reference.
*/ */
public Blob createBlob() { public Blob createBlob() {
return lobCreationContext.execute( CREATE_BLOB_CALLBACK ); return lobCreationContext.fromContext( CREATE_BLOB_CALLBACK );
} }
@Override @Override
public Blob createBlob(byte[] bytes) { public Blob createBlob(byte[] bytes) {
final Blob blob = createBlob();
try { try {
final Blob blob = createBlob();
blob.setBytes( 1, bytes ); blob.setBytes( 1, bytes );
return blob; return blob;
} }
@ -57,10 +69,10 @@ public class ContextualLobCreator extends AbstractLobCreator implements LobCreat
} }
@Override @Override
public Blob createBlob(InputStream inputStream, long length) { public Blob createBlob(InputStream stream, long length) {
// IMPL NOTE : it is inefficient to use JDBC LOB locator creation to create a LOB // IMPL NOTE : it is inefficient to use JDBC LOB locator creation to create a LOB
// backed by a given stream. So just wrap the stream (which is what the NonContextualLobCreator does). // backed by a given stream. So just wrap the stream (which is what the NonContextualLobCreator does).
return NonContextualLobCreator.INSTANCE.createBlob( inputStream, length ); return NonContextualLobCreator.INSTANCE.createBlob( stream, length );
} }
/** /**
@ -69,7 +81,7 @@ public class ContextualLobCreator extends AbstractLobCreator implements LobCreat
* @return The created CLOB reference. * @return The created CLOB reference.
*/ */
public Clob createClob() { public Clob createClob() {
return lobCreationContext.execute( CREATE_CLOB_CALLBACK ); return lobCreationContext.fromContext( CREATE_CLOB_CALLBACK );
} }
@Override @Override
@ -91,46 +103,13 @@ public class ContextualLobCreator extends AbstractLobCreator implements LobCreat
return NonContextualLobCreator.INSTANCE.createClob( reader, length ); return NonContextualLobCreator.INSTANCE.createClob( reader, length );
} }
/**
* Create the basic contextual NCLOB reference.
*
* @return The created NCLOB reference.
*/
public NClob createNClob() {
return lobCreationContext.execute( CREATE_NCLOB_CALLBACK );
}
@Override @Override
public NClob createNClob(String string) { public NClob createNClob(String string) {
try { return NonContextualLobCreator.INSTANCE.createNClob( string );
final NClob nclob = createNClob();
nclob.setString( 1, string );
return nclob;
}
catch ( SQLException e ) {
throw new JDBCException( "Unable to set NCLOB string after creation", e );
}
} }
@Override @Override
public NClob createNClob(Reader reader, long length) { public NClob createNClob(Reader reader, long length) {
// IMPL NOTE : it is inefficient to use JDBC LOB locator creation to create a LOB
// backed by a given stream. So just wrap the stream (which is what the NonContextualLobCreator does).
return NonContextualLobCreator.INSTANCE.createNClob( reader, length ); return NonContextualLobCreator.INSTANCE.createNClob( reader, length );
} }
/**
* Callback for performing contextual BLOB creation
*/
public static final LobCreationContext.Callback<Blob> CREATE_BLOB_CALLBACK = Connection::createBlob;
/**
* Callback for performing contextual CLOB creation
*/
public static final LobCreationContext.Callback<Clob> CREATE_CLOB_CALLBACK = Connection::createClob;
/**
* Callback for performing contextual NCLOB creation
*/
public static final LobCreationContext.Callback<NClob> CREATE_NCLOB_CALLBACK = Connection::createNClob;
} }

View File

@ -0,0 +1,118 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.engine.jdbc.env.internal;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.EnumSet;
import java.util.Map;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect;
import org.hibernate.internal.util.config.ConfigurationHelper;
import static org.hibernate.engine.jdbc.env.internal.LobCreationLogging.LOB_LOGGER;
import static org.hibernate.engine.jdbc.env.internal.LobCreationLogging.LOB_MESSAGE_LOGGER;
/**
* Utilities for LOB creation
*
* @author Steve Ebersole
*/
public class LobCreationHelper {
public static final EnumSet<LobTypes> NONE = EnumSet.noneOf( LobTypes.class );
/**
* Basically here we are simply checking whether we can call the {@link Connection} methods for
* LOB creation added in JDBC 4. We not only check whether the {@link Connection} declares these methods,
* but also whether the actual {@link Connection} instance implements them (i.e. can be called without simply
* throwing an exception).
*
* @param dialect The {@link Dialect} in use
* @param configValues The map of settings
* @param jdbcConnection The connection which can be used in level-of-support testing.
*/
public static EnumSet<LobTypes> getSupportedContextualLobTypes(Dialect dialect, Map<String,Object> configValues, Connection jdbcConnection) {
if ( ConfigurationHelper.getBoolean( Environment.NON_CONTEXTUAL_LOB_CREATION, configValues ) ) {
LOB_MESSAGE_LOGGER.disablingContextualLOBCreation( Environment.NON_CONTEXTUAL_LOB_CREATION );
return NONE;
}
if ( jdbcConnection == null ) {
LOB_MESSAGE_LOGGER.disablingContextualLOBCreationSinceConnectionNull();
return NONE;
}
try {
final DatabaseMetaData meta = jdbcConnection.getMetaData();
// if the jdbc driver version is less than 4, it shouldn't have createClob
if ( meta.getJDBCMajorVersion() < 4 ) {
LOB_MESSAGE_LOGGER.nonContextualLobCreationJdbcVersion( meta.getJDBCMajorVersion() );
return NONE;
}
if ( !dialect.supportsJdbcConnectionLobCreation( meta ) ) {
LOB_MESSAGE_LOGGER.nonContextualLobCreationDialect();
return NONE;
}
}
catch (SQLException ignore) {
// ignore exception and continue
}
// NOTE : for the time being we assume that the ability to call
// `createClob` implies the ability to call `#createBlob`
if ( canCreateClob( jdbcConnection ) ) {
if ( canCreateNClob( jdbcConnection ) ) {
return EnumSet.of( LobTypes.BLOB, LobTypes.CLOB, LobTypes.NCLOB );
}
else {
return EnumSet.of( LobTypes.BLOB, LobTypes.CLOB );
}
}
return NONE;
}
private static boolean canCreateClob(Connection jdbcConnection) {
try {
// we just want to see if the driver can create one. we can immediately free it.
final Clob clob = jdbcConnection.createClob();
try {
clob.free();
}
catch (Throwable e) {
LOB_LOGGER.tracef( "Unable to free CLOB created to test createClob() implementation : %s", e );
}
return true;
}
catch (SQLException e) {
LOB_MESSAGE_LOGGER.contextualClobCreationFailed( e );
return false;
}
}
private static boolean canCreateNClob(Connection jdbcConnection) {
try {
// we just want to see if the driver can create one. we can immediately free it.
final Clob clob = jdbcConnection.createNClob();
try {
clob.free();
}
catch (Throwable e) {
LOB_LOGGER.tracef( "Unable to free NCLOB created to test createNClob() implementation : %s", e );
}
return true;
}
catch (SQLException e) {
LOB_MESSAGE_LOGGER.contextualNClobCreationFailed( e );
return false;
}
}
}

View File

@ -0,0 +1,63 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.engine.jdbc.env.internal;
import org.hibernate.boot.BootLogging;
import org.hibernate.engine.jdbc.JdbcLogging;
import org.hibernate.internal.log.SubSystemLogging;
import org.jboss.logging.BasicLogger;
import org.jboss.logging.Logger;
import org.jboss.logging.annotations.LogMessage;
import org.jboss.logging.annotations.Message;
import org.jboss.logging.annotations.MessageLogger;
import org.jboss.logging.annotations.ValidIdRange;
import static org.jboss.logging.Logger.Level.DEBUG;
/**
* @author Steve Ebersole
*/
@SubSystemLogging(
name = BootLogging.NAME,
description = "Logging related to "
)
@MessageLogger( projectCode = "HHH" )
@ValidIdRange( min = 10010001, max = 10010050 )
public interface LobCreationLogging extends BasicLogger {
String NAME = JdbcLogging.NAME + ".lob";
Logger LOB_LOGGER = Logger.getLogger( NAME );
LobCreationLogging LOB_MESSAGE_LOGGER = Logger.getMessageLogger( LobCreationLogging.class, NAME );
boolean LOB_TRACE_ENABLED = LOB_LOGGER.isTraceEnabled();
boolean LOB_DEBUG_ENABLED = LOB_LOGGER.isDebugEnabled();
@LogMessage(level = DEBUG)
@Message(value = "Disabling contextual LOB creation as %s is true", id = 10010001)
void disablingContextualLOBCreation(String settingName);
@LogMessage(level = DEBUG)
@Message(value = "Disabling contextual LOB creation as connection was null", id = 10010002)
void disablingContextualLOBCreationSinceConnectionNull();
@LogMessage(level = DEBUG)
@Message(value = "Disabling contextual LOB creation as JDBC driver reported JDBC version [%s] less than 4", id = 10010003)
void nonContextualLobCreationJdbcVersion(int jdbcMajorVersion);
@LogMessage(level = DEBUG)
@Message(value = "Disabling contextual LOB creation as Dialect reported it is not supported", id = 10010004)
void nonContextualLobCreationDialect();
@LogMessage(level = DEBUG)
@Message(value = "Disabling contextual LOB creation as createClob() method threw error : %s", id = 10010005)
void contextualClobCreationFailed(Throwable t);
@LogMessage(level = DEBUG)
@Message(value = "Disabling contextual NCLOB creation as createNClob() method threw error : %s", id = 10010006)
void contextualNClobCreationFailed(Throwable t);
}

View File

@ -6,24 +6,20 @@
*/ */
package org.hibernate.engine.jdbc.env.internal; package org.hibernate.engine.jdbc.env.internal;
import java.lang.reflect.Method;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DatabaseMetaData; import java.util.EnumSet;
import java.sql.SQLException;
import java.util.Map; import java.util.Map;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.ContextualLobCreator;
import org.hibernate.engine.jdbc.LobCreationContext; import org.hibernate.engine.jdbc.LobCreationContext;
import org.hibernate.engine.jdbc.LobCreator; import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.NonContextualLobCreator; import org.hibernate.engine.jdbc.NonContextualLobCreator;
import org.hibernate.engine.jdbc.env.spi.LobCreatorBuilder; import org.hibernate.engine.jdbc.env.spi.LobCreatorBuilder;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.jboss.logging.Logger; import static org.hibernate.engine.jdbc.env.internal.LobCreationHelper.NONE;
import static org.hibernate.engine.jdbc.env.internal.LobCreationHelper.getSupportedContextualLobTypes;
import static org.hibernate.engine.jdbc.env.internal.LobCreationLogging.LOB_LOGGER;
import static org.hibernate.engine.jdbc.env.internal.LobCreationLogging.LOB_MESSAGE_LOGGER;
/** /**
* Builds {@link LobCreator} instances based on the capabilities of the environment. * Builds {@link LobCreator} instances based on the capabilities of the environment.
@ -31,15 +27,10 @@ import org.jboss.logging.Logger;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class LobCreatorBuilderImpl implements LobCreatorBuilder { public class LobCreatorBuilderImpl implements LobCreatorBuilder {
private static final CoreMessageLogger LOG = Logger.getMessageLogger( private final EnumSet<LobTypes> supportedContextualLobTypes;
CoreMessageLogger.class,
LobCreatorBuilderImpl.class.getName()
);
private final boolean useContextualLobCreation; public LobCreatorBuilderImpl(EnumSet<LobTypes> supportedContextualLobTypes) {
this.supportedContextualLobTypes = supportedContextualLobTypes;
private LobCreatorBuilderImpl(boolean useContextualLobCreation) {
this.useContextualLobCreation = useContextualLobCreation;
} }
// factory methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // factory methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -53,8 +44,17 @@ public class LobCreatorBuilderImpl implements LobCreatorBuilder {
* @param jdbcConnection A JDBC {@link Connection} which can be used to gauge the drivers level of support, * @param jdbcConnection A JDBC {@link Connection} which can be used to gauge the drivers level of support,
* specifically for creating LOB references. * specifically for creating LOB references.
*/ */
public static LobCreatorBuilderImpl makeLobCreatorBuilder(Dialect dialect, Map<String,Object> configValues, Connection jdbcConnection) { public static LobCreatorBuilderImpl makeLobCreatorBuilder(
return new LobCreatorBuilderImpl( useContextualLobCreation( dialect, configValues, jdbcConnection ) ); Dialect dialect,
Map<String,Object> configValues,
Connection jdbcConnection) {
final EnumSet<LobTypes> supportedContextualLobTypes = getSupportedContextualLobTypes(
dialect,
configValues,
jdbcConnection
);
return new LobCreatorBuilderImpl( supportedContextualLobTypes );
} }
/** /**
@ -63,80 +63,8 @@ public class LobCreatorBuilderImpl implements LobCreatorBuilder {
* @return Appropriate LobCreatorBuilder * @return Appropriate LobCreatorBuilder
*/ */
public static LobCreatorBuilderImpl makeLobCreatorBuilder() { public static LobCreatorBuilderImpl makeLobCreatorBuilder() {
LOG.disablingContextualLOBCreationSinceConnectionNull(); LOB_MESSAGE_LOGGER.disablingContextualLOBCreationSinceConnectionNull();
return new LobCreatorBuilderImpl( false ); return new LobCreatorBuilderImpl( NONE );
}
private static final Class<?>[] NO_ARG_SIG = ArrayHelper.EMPTY_CLASS_ARRAY;
private static final Object[] NO_ARGS = ArrayHelper.EMPTY_OBJECT_ARRAY;
/**
* Basically here we are simply checking whether we can call the {@link Connection} methods for
* LOB creation added in JDBC 4. We not only check whether the {@link Connection} declares these methods,
* but also whether the actual {@link Connection} instance implements them (i.e. can be called without simply
* throwing an exception).
*
* @param dialect The {@link Dialect} in use
* @param configValues The map of settings
* @param jdbcConnection The connection which can be used in level-of-support testing.
*
* @return True if the connection can be used to create LOBs; false otherwise.
*/
private static boolean useContextualLobCreation(Dialect dialect, Map<String,Object> configValues, Connection jdbcConnection) {
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;
}
if ( !dialect.supportsJdbcConnectionLobCreation( meta ) ) {
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 e ) {
LOG.tracef( "Unable to free CLOB created to test createClob() implementation : %s", e );
}
return true;
}
catch ( Throwable t ) {
LOG.disablingContextualLOBCreationSinceCreateClobFailed( t );
}
}
}
catch ( NoSuchMethodException ignore ) {
}
return false;
} }
/** /**
@ -147,8 +75,20 @@ public class LobCreatorBuilderImpl implements LobCreatorBuilder {
* @return The LobCreator * @return The LobCreator
*/ */
public LobCreator buildLobCreator(LobCreationContext lobCreationContext) { public LobCreator buildLobCreator(LobCreationContext lobCreationContext) {
return useContextualLobCreation if ( supportedContextualLobTypes.isEmpty() ) {
? new ContextualLobCreator( lobCreationContext ) return NonContextualLobCreator.INSTANCE;
: NonContextualLobCreator.INSTANCE; }
if ( supportedContextualLobTypes.contains( LobTypes.BLOB )
&& supportedContextualLobTypes.contains( LobTypes.CLOB ) ){
if ( !supportedContextualLobTypes.contains( LobTypes.NCLOB ) ) {
return new BlobAndClobCreator( lobCreationContext );
}
return new StandardLobCreator( lobCreationContext );
}
LOB_LOGGER.debug( "Unexpected condition resolving type of LobCreator to use. Falling back to NonContextualLobCreator" );
return NonContextualLobCreator.INSTANCE;
} }
} }

View File

@ -0,0 +1,40 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.engine.jdbc.env.internal;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.NClob;
import org.hibernate.type.SqlTypes;
/**
* Enumeration of the JDBC LOB locator types
*
* @author Steve Ebersole
*/
public enum LobTypes {
BLOB( SqlTypes.BLOB, Blob.class ),
CLOB( SqlTypes.CLOB, Clob.class ),
NCLOB( SqlTypes.NCLOB, NClob.class );
private final int jdbcTypeCode;
private final Class<?> jdbcTypeClass;
LobTypes(int jdbcTypeCode, Class<?> jdbcTypeClass) {
this.jdbcTypeCode = jdbcTypeCode;
this.jdbcTypeClass = jdbcTypeClass;
}
public int getJdbcTypeCode() {
return jdbcTypeCode;
}
public Class<?> getJdbcTypeClass() {
return jdbcTypeClass;
}
}

View File

@ -0,0 +1,67 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.engine.jdbc.env.internal;
import java.io.InputStream;
import java.io.Reader;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.NClob;
import java.sql.SQLException;
import org.hibernate.JDBCException;
import org.hibernate.engine.jdbc.LobCreationContext;
import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.NonContextualLobCreator;
/**
* {@linkplain LobCreator} implementation using {@linkplain Connection#createBlob},
* {@linkplain Connection#createClob} and {@linkplain Connection#createNClob} to
* create the LOB references.
*
* @author Steve Ebersole
* @author Gail Badner
*/
public class StandardLobCreator extends BlobAndClobCreator {
/**
* Callback for performing contextual NCLOB creation
*/
public static final LobCreationContext.Callback<NClob> CREATE_NCLOB_CALLBACK = Connection::createNClob;
public StandardLobCreator(LobCreationContext lobCreationContext) {
super( lobCreationContext );
}
/**
* Create the basic contextual NCLOB reference.
*
* @return The created NCLOB reference.
*/
public NClob createNClob() {
return lobCreationContext.fromContext( CREATE_NCLOB_CALLBACK );
}
@Override
public NClob createNClob(String string) {
try {
final NClob nclob = createNClob();
nclob.setString( 1, string );
return nclob;
}
catch ( SQLException e ) {
throw new JDBCException( "Unable to set NCLOB string after creation", e );
}
}
@Override
public NClob createNClob(Reader reader, long length) {
// IMPL NOTE : it is inefficient to use JDBC LOB locator creation to create a LOB
// backed by a given stream. So just wrap the stream (which is what the NonContextualLobCreator does).
return NonContextualLobCreator.INSTANCE.createNClob( reader, length );
}
}

View File

@ -1,134 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.engine.jdbc.internal;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Map;
import java.util.Properties;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.jdbc.ContextualLobCreator;
import org.hibernate.engine.jdbc.LobCreationContext;
import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.NonContextualLobCreator;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.PropertiesHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.jboss.logging.Logger;
/**
* Builds {@link LobCreator} instances based on the capabilities of the environment.
*
* @author Steve Ebersole
*/
public class LobCreatorBuilder {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
CoreMessageLogger.class,
LobCreatorBuilder.class.getName()
);
private final boolean useContextualLobCreation;
/**
* The public factory method for obtaining the appropriate according to given JDBC {@link Connection}.
*
* @param configValues The map of settings
* @param jdbcConnection A JDBC {@link Connection} which can be used to gauge the drivers level of support,
* specifically for creating LOB references.
*/
public LobCreatorBuilder(Map<String,Object> configValues, Connection jdbcConnection) {
this.useContextualLobCreation = useContextualLobCreation( configValues, jdbcConnection );
}
public LobCreatorBuilder(Properties configValues, Connection jdbcConnection) {
this( PropertiesHelper.map(configValues), jdbcConnection );
}
private static final Class<?>[] NO_ARG_SIG = ArrayHelper.EMPTY_CLASS_ARRAY;
private static final Object[] NO_ARGS = ArrayHelper.EMPTY_OBJECT_ARRAY;
/**
* Basically here we are simply checking whether we can call the {@link Connection} methods for
* LOB creation added in JDBC 4. We not only check whether the {@link Connection} declares these methods,
* but also whether the actual {@link Connection} instance implements them (i.e. can be called without simply
* throwing an exception).
*
* @param jdbcConnection The connection which can be used in level-of-support testing.
*
* @return True if the connection can be used to create LOBs; false otherwise.
*/
private static boolean useContextualLobCreation(Map<String,Object> configValues, Connection jdbcConnection) {
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<Connection> 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 e ) {
LOG.tracef( "Unable to free CLOB created to test createClob() implementation : %s", e );
}
return true;
}
catch ( Throwable t ) {
LOG.disablingContextualLOBCreationSinceCreateClobFailed( t );
}
}
}
catch ( NoSuchMethodException ignore ) {
}
return false;
}
/**
* Build a LobCreator using the given context
*
* @param lobCreationContext The LOB creation context
*
* @return The LobCreator
*/
public LobCreator buildLobCreator(LobCreationContext lobCreationContext) {
return useContextualLobCreation
? new ContextualLobCreator( lobCreationContext )
: NonContextualLobCreator.INSTANCE;
}
}

View File

@ -26,6 +26,7 @@ import org.hibernate.cache.CacheException;
import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolver; import org.hibernate.engine.jdbc.dialect.spi.DialectResolver;
import org.hibernate.engine.jdbc.env.internal.LobCreationLogging;
import org.hibernate.engine.jndi.JndiException; import org.hibernate.engine.jndi.JndiException;
import org.hibernate.engine.jndi.JndiNameException; import org.hibernate.engine.jndi.JndiNameException;
import org.hibernate.engine.spi.CollectionKey; import org.hibernate.engine.spi.CollectionKey;
@ -1425,21 +1426,40 @@ public interface CoreMessageLogger extends BasicLogger {
@Message(value = "Closing un-released batch", id = 420) @Message(value = "Closing un-released batch", id = 420)
void closingUnreleasedBatch(); void closingUnreleasedBatch();
/**
* @deprecated Use {@link LobCreationLogging#disablingContextualLOBCreation} instead
*/
@LogMessage(level = DEBUG) @LogMessage(level = DEBUG)
@Message(value = "Disabling contextual LOB creation as %s is true", id = 421) @Message(value = "Disabling contextual LOB creation as %s is true", id = 421)
@Deprecated
void disablingContextualLOBCreation(String nonContextualLobCreation); void disablingContextualLOBCreation(String nonContextualLobCreation);
/**
* @deprecated Use {@link LobCreationLogging#disablingContextualLOBCreationSinceConnectionNull} instead
*/
@LogMessage(level = DEBUG) @LogMessage(level = DEBUG)
@Message(value = "Disabling contextual LOB creation as connection was null", id = 422) @Message(value = "Disabling contextual LOB creation as connection was null", id = 422)
@Deprecated
void disablingContextualLOBCreationSinceConnectionNull(); void disablingContextualLOBCreationSinceConnectionNull();
/**
* @deprecated Use {@link LobCreationLogging#nonContextualLobCreationJdbcVersion} instead
*/
@LogMessage(level = DEBUG) @LogMessage(level = DEBUG)
@Message(value = "Disabling contextual LOB creation as JDBC driver reported JDBC version [%s] less than 4", @Message(value = "Disabling contextual LOB creation as JDBC driver reported JDBC version [%s] less than 4", id = 423)
id = 423) @Deprecated
void disablingContextualLOBCreationSinceOldJdbcVersion(int jdbcMajorVersion); void disablingContextualLOBCreationSinceOldJdbcVersion(int jdbcMajorVersion);
/**
* @deprecated Use {@link LobCreationLogging#contextualClobCreationFailed} instead
*
* @see LobCreationLogging#contextualClobCreationFailed
* @see LobCreationLogging#contextualNClobCreationFailed
*/
@LogMessage(level = DEBUG) @LogMessage(level = DEBUG)
@Message(value = "Disabling contextual LOB creation as createClob() method threw error : %s", id = 424) @Message(value = "Disabling contextual LOB creation as createClob() method threw error : %s", id = 424)
@Deprecated
void disablingContextualLOBCreationSinceCreateClobFailed(Throwable t); void disablingContextualLOBCreationSinceCreateClobFailed(Throwable t);
@LogMessage(level = INFO) @LogMessage(level = INFO)

View File

@ -19,80 +19,142 @@ import java.sql.Connection;
import java.sql.DatabaseMetaData; import java.sql.DatabaseMetaData;
import java.sql.NClob; import java.sql.NClob;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Properties; import java.util.Arrays;
import java.util.Collections;
import org.junit.Test; import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.dialect.SybaseDialect;
import org.hibernate.engine.jdbc.BlobImplementer; import org.hibernate.engine.jdbc.BlobImplementer;
import org.hibernate.engine.jdbc.ClobImplementer; import org.hibernate.engine.jdbc.ClobImplementer;
import org.hibernate.engine.jdbc.ContextualLobCreator;
import org.hibernate.engine.jdbc.LobCreationContext; import org.hibernate.engine.jdbc.LobCreationContext;
import org.hibernate.engine.jdbc.LobCreator; import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.NClobImplementer; import org.hibernate.engine.jdbc.NClobImplementer;
import org.hibernate.engine.jdbc.NonContextualLobCreator; import org.hibernate.engine.jdbc.NonContextualLobCreator;
import org.hibernate.engine.jdbc.WrappedBlob; import org.hibernate.engine.jdbc.WrappedBlob;
import org.hibernate.engine.jdbc.WrappedClob; import org.hibernate.engine.jdbc.WrappedClob;
import org.hibernate.engine.jdbc.internal.LobCreatorBuilder; import org.hibernate.engine.jdbc.env.internal.BlobAndClobCreator;
import org.hibernate.engine.jdbc.env.internal.LobCreationHelper;
import org.hibernate.engine.jdbc.env.internal.LobCreatorBuilderImpl;
import org.hibernate.engine.jdbc.env.internal.LobTypes;
import org.hibernate.engine.jdbc.env.internal.StandardLobCreator;
import static org.junit.Assert.assertSame; import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class LobCreatorTest extends org.hibernate.testing.junit4.BaseUnitTestCase { public class LobCreatorTest {
@Test @Test
public void testConnectedLobCreator() throws SQLException { public void testConnectedLobCreator() throws SQLException {
final Connection connection = createConnectionProxy( 4, new JdbcLobBuilderImpl( true ) ); final Connection connection = createConnectionProxy( 4, new JdbcLobBuilderImpl( LobTypes.BLOB, LobTypes.CLOB, LobTypes.NCLOB ) );
LobCreationContext lobCreationContext = new LobCreationContextImpl( connection ); final H2Dialect dialect = new H2Dialect();
final EnumSet<LobTypes> supportedContextualLobTypes = LobCreationHelper.getSupportedContextualLobTypes(
dialect,
Collections.emptyMap(),
connection
);
final LobCreatorBuilderImpl creatorBuilder = new LobCreatorBuilderImpl( supportedContextualLobTypes );
final LobCreationContext lobCreationContext = new LobCreationContextImpl( connection );
final LobCreator lobCreator = creatorBuilder.buildLobCreator( lobCreationContext );
assertThat( lobCreator ).isInstanceOf( StandardLobCreator.class );
LobCreator lobCreator =
new LobCreatorBuilder( new Properties(), connection )
.buildLobCreator( lobCreationContext );
assertTrue( lobCreator instanceof ContextualLobCreator );
testLobCreation( lobCreator ); testLobCreation( lobCreator );
connection.close(); connection.close();
} }
@Test @Test
public void testJdbc3LobCreator() throws SQLException { public void testJdbc3LobCreator() throws SQLException {
final Connection connection = createConnectionProxy( 3, new JdbcLobBuilderImpl( false) ); final Connection connection = createConnectionProxy( 3, new JdbcLobBuilderImpl() );
LobCreationContext lobCreationContext = new LobCreationContextImpl( connection ); final H2Dialect dialect = new H2Dialect();
LobCreator lobCreator = final EnumSet<LobTypes> supportedContextualLobTypes = LobCreationHelper.getSupportedContextualLobTypes(
new LobCreatorBuilder( new Properties(), connection ) dialect,
.buildLobCreator( lobCreationContext ); Collections.emptyMap(),
assertSame( NonContextualLobCreator.INSTANCE, lobCreator ); connection
);
final LobCreatorBuilderImpl creatorBuilder = new LobCreatorBuilderImpl( supportedContextualLobTypes );
final LobCreationContext lobCreationContext = new LobCreationContextImpl( connection );
final LobCreator lobCreator = creatorBuilder.buildLobCreator( lobCreationContext );
assertThat( lobCreator ).isSameAs( NonContextualLobCreator.INSTANCE );
testLobCreation( lobCreator ); testLobCreation( lobCreator );
connection.close(); connection.close();
} }
@Test @Test
public void testJdbc4UnsupportedLobCreator() throws SQLException { public void testJdbc4UnsupportedLobCreator() throws SQLException {
final Connection connection = createConnectionProxy( 4, new JdbcLobBuilderImpl( false ) ); final Connection connection = createConnectionProxy( 4, new JdbcLobBuilderImpl() );
LobCreationContext lobCreationContext = new LobCreationContextImpl( connection ); final H2Dialect dialect = new H2Dialect();
LobCreator lobCreator = final EnumSet<LobTypes> supportedContextualLobTypes = LobCreationHelper.getSupportedContextualLobTypes(
new LobCreatorBuilder( new Properties(), connection ) dialect,
.buildLobCreator( lobCreationContext ); Collections.emptyMap(),
assertSame( NonContextualLobCreator.INSTANCE, lobCreator ); connection
);
final LobCreatorBuilderImpl creatorBuilder = new LobCreatorBuilderImpl( supportedContextualLobTypes );
final LobCreationContext lobCreationContext = new LobCreationContextImpl( connection );
final LobCreator lobCreator = creatorBuilder.buildLobCreator( lobCreationContext );
assertThat( lobCreator ).isSameAs( NonContextualLobCreator.INSTANCE );
testLobCreation( lobCreator ); testLobCreation( lobCreator );
connection.close(); connection.close();
} }
@Test @Test
public void testConfiguredNonContextualLobCreator() throws SQLException { public void testConfiguredNonContextualLobCreator() throws SQLException {
final Connection connection = createConnectionProxy( 4, new JdbcLobBuilderImpl( true ) ); final Connection connection = createConnectionProxy( 4, new JdbcLobBuilderImpl( LobTypes.BLOB, LobTypes.CLOB, LobTypes.NCLOB ) );
LobCreationContext lobCreationContext = new LobCreationContextImpl( connection ); final H2Dialect dialect = new H2Dialect();
final Map<String,Object> props = new HashMap<>();
props.put( Environment.NON_CONTEXTUAL_LOB_CREATION, "true" );
Properties props = new Properties(); final EnumSet<LobTypes> supportedContextualLobTypes = LobCreationHelper.getSupportedContextualLobTypes(
props.setProperty( Environment.NON_CONTEXTUAL_LOB_CREATION, "true" ); dialect,
LobCreator lobCreator = props,
new LobCreatorBuilder( props, connection ) connection
.buildLobCreator( lobCreationContext ); );
assertSame( NonContextualLobCreator.INSTANCE, lobCreator ); final LobCreatorBuilderImpl creatorBuilder = new LobCreatorBuilderImpl( supportedContextualLobTypes );
final LobCreationContext lobCreationContext = new LobCreationContextImpl( connection );
final LobCreator lobCreator = creatorBuilder.buildLobCreator( lobCreationContext );
assertThat( lobCreator ).isSameAs( NonContextualLobCreator.INSTANCE );
testLobCreation( lobCreator );
connection.close();
}
@Test
public void testBlobAndClob() throws SQLException {
// no NCLOB
final Connection connection = createConnectionProxy( 4, new JdbcLobBuilderImpl( LobTypes.BLOB, LobTypes.CLOB ) );
final SybaseDialect dialect = new SybaseDialect();
final EnumSet<LobTypes> supportedContextualLobTypes = LobCreationHelper.getSupportedContextualLobTypes(
dialect,
Collections.emptyMap(),
connection
);
final LobCreatorBuilderImpl creatorBuilder = new LobCreatorBuilderImpl( supportedContextualLobTypes );
final LobCreationContext lobCreationContext = new LobCreationContextImpl( connection );
final LobCreator lobCreator = creatorBuilder.buildLobCreator( lobCreationContext );
assertThat( lobCreator ).isInstanceOf( BlobAndClobCreator.class );
testLobCreation( lobCreator ); testLobCreation( lobCreator );
connection.close(); connection.close();
@ -124,7 +186,7 @@ public class LobCreatorTest extends org.hibernate.testing.junit4.BaseUnitTestCas
assertTrue( nclob instanceof NClobImplementer ); assertTrue( nclob instanceof NClobImplementer );
} }
else { else {
assertTrue( nclob instanceof JdbcNClob ); assertTrue( nclob instanceof NClob );
} }
// assertTrue( nclob instanceof NClob ); // assertTrue( nclob instanceof NClob );
nclob = lobCreator.wrap( nclob ); nclob = lobCreator.wrap( nclob );
@ -159,27 +221,34 @@ public class LobCreatorTest extends org.hibernate.testing.junit4.BaseUnitTestCas
} }
private static class JdbcLobBuilderImpl implements JdbcLobBuilder { private static class JdbcLobBuilderImpl implements JdbcLobBuilder {
private final boolean isSupported; private final Set<LobTypes> supportedTypes;
private JdbcLobBuilderImpl(boolean isSupported) { private JdbcLobBuilderImpl(LobTypes... supportedTypes) {
this.isSupported = isSupported; this.supportedTypes = convert( supportedTypes );
} }
private static Set<LobTypes> convert(LobTypes... supportedTypes) {
final Set<LobTypes> result = new HashSet<>();
result.addAll( Arrays.asList( supportedTypes ) );
return result;
}
public Blob createBlob() throws SQLException { public Blob createBlob() throws SQLException {
if ( ! isSupported ) { if ( ! supportedTypes.contains( LobTypes.BLOB ) ) {
throw new SQLException( "not supported!" ); throw new SQLException( "not supported!" );
} }
return new JdbcBlob(); return new JdbcBlob();
} }
public Clob createClob() throws SQLException { public Clob createClob() throws SQLException {
if ( ! isSupported ) { if ( ! supportedTypes.contains( LobTypes.CLOB ) ) {
throw new SQLException( "not supported!" ); throw new SQLException( "not supported!" );
} }
return new JdbcClob(); return new JdbcClob();
} }
public NClob createNClob() throws SQLException { public NClob createNClob() throws SQLException {
if ( ! isSupported ) { if ( ! supportedTypes.contains( LobTypes.NCLOB ) ) {
throw new SQLException( "not supported!" ); throw new SQLException( "not supported!" );
} }
return new JdbcNClob(); return new JdbcNClob();

View File

@ -29,11 +29,16 @@ import jakarta.persistence.Lob;
import org.hibernate.Hibernate; import org.hibernate.Hibernate;
import org.hibernate.annotations.Cache; import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.dialect.SybaseDialect;
import org.hibernate.testing.DialectChecks; import org.hibernate.testing.DialectChecks;
import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.RequiresDialectFeature;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.hibernate.testing.orm.junit.Jira;
import org.hibernate.testing.orm.junit.RequiresDialect;
import org.hibernate.testing.orm.junit.SkipForDialect;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -102,6 +107,9 @@ public class LobUnfetchedPropertyTest extends BaseCoreFunctionalTestCase {
@Test @Test
@RequiresDialectFeature(DialectChecks.SupportsNClob.class) @RequiresDialectFeature(DialectChecks.SupportsNClob.class)
@SkipForDialect(
dialectClass = SybaseDialect.class, matchSubTypes = true,
reason = "jConnect does not support Connection#createNClob which is ultimately used by LobHelper#createNClob" )
public void testNClob() { public void testNClob() {
final int id = doInHibernate( this::sessionFactory, s -> { final int id = doInHibernate( this::sessionFactory, s -> {
FileNClob file = new FileNClob(); FileNClob file = new FileNClob();
@ -219,4 +227,34 @@ public class LobUnfetchedPropertyTest extends BaseCoreFunctionalTestCase {
this.clob = clob; this.clob = clob;
} }
} }
@Entity(name = "FileNClob2")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE, includeLazy = false)
public static class FileNClob2 {
private int id;
private String clob;
@Id
@GeneratedValue
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Column(name = "filedata", length = 1024 * 1024)
@Lob
@Basic(fetch = FetchType.LAZY)
public String getClob() {
return clob;
}
public void setClob(String clob) {
this.clob = clob;
}
}
} }