HHH-18206 Switch to JDBC LOB APIs for Oracle Dialect (#8486)

HHH-18206 Switch to JDBC LOB APIs for Oracle Dialect
This commit is contained in:
Loïc LEFEVRE 2024-06-02 18:29:52 +02:00 committed by GitHub
parent 18ec7f178e
commit ef1cbf589d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 165 additions and 6 deletions

View File

@ -1045,6 +1045,11 @@ public class CockroachLegacyDialect extends Dialect {
return false; return false;
} }
@Override
public boolean useConnectionToCreateLob() {
return false;
}
@Override @Override
public boolean supportsOffsetInSubquery() { public boolean supportsOffsetInSubquery() {
return true; return true;

View File

@ -335,6 +335,11 @@ public class MimerSQLDialect extends Dialect {
return false; return false;
} }
@Override
public boolean useConnectionToCreateLob() {
return false;
}
@Override @Override
public IdentityColumnSupport getIdentityColumnSupport() { public IdentityColumnSupport getIdentityColumnSupport() {
return MimerSQLIdentityColumnSupport.INSTANCE; return MimerSQLIdentityColumnSupport.INSTANCE;

View File

@ -1563,4 +1563,11 @@ public class OracleLegacyDialect extends Dialect {
public boolean supportsFromClauseInUpdate() { public boolean supportsFromClauseInUpdate() {
return true; return true;
} }
@Override
public boolean useInputStreamToInsertBlob() {
// see HHH-18206
return false;
}
} }

View File

@ -849,6 +849,11 @@ public class PostgreSQLLegacyDialect extends Dialect {
return false; return false;
} }
@Override
public boolean useConnectionToCreateLob() {
return false;
}
@Override @Override
public String getSelectClauseNullString(int sqlType, TypeConfiguration typeConfiguration) { public String getSelectClauseNullString(int sqlType, TypeConfiguration typeConfiguration) {
// Workaround for postgres bug #1453 // Workaround for postgres bug #1453

View File

@ -138,6 +138,11 @@ public class TeradataDialect extends Dialect {
return getVersion().isSameOrAfter( 14 ); return getVersion().isSameOrAfter( 14 );
} }
@Override
public boolean useConnectionToCreateLob() {
return false;
}
@Override @Override
public int getMaxVarcharLength() { public int getMaxVarcharLength() {
//for the unicode server character set //for the unicode server character set

View File

@ -1014,6 +1014,11 @@ public class CockroachDialect extends Dialect {
return false; return false;
} }
@Override
public boolean useConnectionToCreateLob() {
return false;
}
@Override @Override
public boolean supportsOffsetInSubquery() { public boolean supportsOffsetInSubquery() {
return true; return true;

View File

@ -3647,6 +3647,20 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
return true; return true;
} }
/**
* Should BLOB, CLOB, and NCLOB be created solely using respectively
* {@link Connection#createBlob()}, {@link Connection#createClob()},
* and {@link Connection#createNClob()}.
*
* @return True if BLOB, CLOB, and NCLOB should be created using JDBC
* {@link Connection}.
*
* @since 6.6
*/
public boolean useConnectionToCreateLob() {
return !useInputStreamToInsertBlob();
}
/** /**
* Does this dialect support parameters within the {@code SELECT} clause * Does this dialect support parameters within the {@code SELECT} clause
* of {@code INSERT ... SELECT ...} statements? * of {@code INSERT ... SELECT ...} statements?

View File

@ -1041,6 +1041,11 @@ public class DialectDelegateWrapper extends Dialect {
return wrapped.useInputStreamToInsertBlob(); return wrapped.useInputStreamToInsertBlob();
} }
@Override
public boolean useConnectionToCreateLob() {
return wrapped.useConnectionToCreateLob();
}
@Override @Override
@Deprecated(since = "6", forRemoval = true) @Deprecated(since = "6", forRemoval = true)
public boolean supportsParametersInInsertSelect() { public boolean supportsParametersInInsertSelect() {

View File

@ -1676,4 +1676,11 @@ public class OracleDialect extends Dialect {
public String[] getDropEnumTypeCommand(String name) { public String[] getDropEnumTypeCommand(String name) {
return new String[] { "drop domain if exists " + name + " force" }; return new String[] { "drop domain if exists " + name + " force" };
} }
@Override
public boolean useInputStreamToInsertBlob() {
// see HHH-18206
return false;
}
} }

View File

@ -903,6 +903,11 @@ public class PostgreSQLDialect extends Dialect {
return false; return false;
} }
@Override
public boolean useConnectionToCreateLob() {
return false;
}
@Override @Override
public String getSelectClauseNullString(int sqlType, TypeConfiguration typeConfiguration) { public String getSelectClauseNullString(int sqlType, TypeConfiguration typeConfiguration) {
// TODO: adapt this to handle named enum types! // TODO: adapt this to handle named enum types!

View File

@ -41,7 +41,7 @@ public class NClobProxy extends ClobProxy {
* @return The generated proxy. * @return The generated proxy.
*/ */
public static NClob generateProxy(String string) { public static NClob generateProxy(String string) {
return (NClob) Proxy.newProxyInstance( getProxyClassLoader(), PROXY_INTERFACES, new ClobProxy( string ) ); return (NClob) Proxy.newProxyInstance( getProxyClassLoader(), PROXY_INTERFACES, new NClobProxy( string ) );
} }
/** /**
@ -53,7 +53,7 @@ public class NClobProxy extends ClobProxy {
* @return The generated proxy. * @return The generated proxy.
*/ */
public static NClob generateProxy(Reader reader, long length) { public static NClob generateProxy(Reader reader, long length) {
return (NClob) Proxy.newProxyInstance( getProxyClassLoader(), PROXY_INTERFACES, new ClobProxy( reader, length ) ); return (NClob) Proxy.newProxyInstance( getProxyClassLoader(), PROXY_INTERFACES, new NClobProxy( reader, length ) );
} }
/** /**

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.type.descriptor; package org.hibernate.type.descriptor;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.LobCreator; import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
@ -34,6 +35,13 @@ public interface WrapperOptions {
*/ */
SessionFactoryImplementor getSessionFactory(); SessionFactoryImplementor getSessionFactory();
/**
* Access to the current dialect.
*/
default Dialect getDialect() {
return getSessionFactory().getJdbcServices().getDialect();
}
/** /**
* Determines whether streams should be used for binding LOB values. * Determines whether streams should be used for binding LOB values.
* *

View File

@ -32,6 +32,7 @@ import org.hibernate.type.descriptor.jdbc.JdbcType;
* *
* @author Steve Ebersole * @author Steve Ebersole
* @author Brett Meyer * @author Brett Meyer
* @author Loïc Lefèvre
*/ */
public class BlobJavaType extends AbstractClassJavaType<Blob> { public class BlobJavaType extends AbstractClassJavaType<Blob> {
public static final BlobJavaType INSTANCE = new BlobJavaType(); public static final BlobJavaType INSTANCE = new BlobJavaType();
@ -133,7 +134,7 @@ public class BlobJavaType extends AbstractClassJavaType<Blob> {
else if (Blob.class.isAssignableFrom( type )) { else if (Blob.class.isAssignableFrom( type )) {
final Blob blob = value instanceof WrappedBlob final Blob blob = value instanceof WrappedBlob
? ( (WrappedBlob) value ).getWrappedBlob() ? ( (WrappedBlob) value ).getWrappedBlob()
: value; : getOrCreateBlob(value, options);
return (X) blob; return (X) blob;
} }
} }
@ -144,6 +145,21 @@ public class BlobJavaType extends AbstractClassJavaType<Blob> {
throw unknownUnwrap( type ); throw unknownUnwrap( type );
} }
private Blob getOrCreateBlob(Blob value, WrapperOptions options) throws SQLException {
if(options.getDialect().useConnectionToCreateLob()) {
if(value.length() == 0) {
// empty Blob
return options.getLobCreator().createBlob(new byte[0]);
}
else {
return options.getLobCreator().createBlob(value.getBytes(1, (int) value.length()));
}
}
else {
return value;
}
}
@Override @Override
public <X> Blob wrap(X value, WrapperOptions options) { public <X> Blob wrap(X value, WrapperOptions options) {
if ( value == null ) { if ( value == null ) {

View File

@ -9,6 +9,7 @@ package org.hibernate.type.descriptor.java;
import java.io.Reader; import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import java.sql.Clob; import java.sql.Clob;
import java.sql.NClob;
import java.util.Arrays; import java.util.Arrays;
import org.hibernate.engine.jdbc.CharacterStream; import org.hibernate.engine.jdbc.CharacterStream;
@ -80,6 +81,9 @@ public class CharacterArrayJavaType extends AbstractClassJavaType<Character[]> {
if ( String.class.isAssignableFrom( type ) ) { if ( String.class.isAssignableFrom( type ) ) {
return (X) new String( unwrapChars( value ) ); return (X) new String( unwrapChars( value ) );
} }
if ( NClob.class.isAssignableFrom( type ) ) {
return (X) options.getLobCreator().createNClob( new String( unwrapChars( value ) ) );
}
if ( Clob.class.isAssignableFrom( type ) ) { if ( Clob.class.isAssignableFrom( type ) ) {
return (X) options.getLobCreator().createClob( new String( unwrapChars( value ) ) ); return (X) options.getLobCreator().createClob( new String( unwrapChars( value ) ) );
} }

View File

@ -24,7 +24,6 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
/** /**
* Descriptor for {@link Clob} handling. * Descriptor for {@link Clob} handling.
@ -33,6 +32,7 @@ import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
* But we treat them as immutable because we simply have no way to dirty check nor deep copy them. * But we treat them as immutable because we simply have no way to dirty check nor deep copy them.
* *
* @author Steve Ebersole * @author Steve Ebersole
* @author Loïc Lefèvre
*/ */
public class ClobJavaType extends AbstractClassJavaType<Clob> { public class ClobJavaType extends AbstractClassJavaType<Clob> {
public static final ClobJavaType INSTANCE = new ClobJavaType(); public static final ClobJavaType INSTANCE = new ClobJavaType();
@ -107,7 +107,7 @@ public class ClobJavaType extends AbstractClassJavaType<Clob> {
else if (Clob.class.isAssignableFrom( type )) { else if (Clob.class.isAssignableFrom( type )) {
final Clob clob = value instanceof WrappedClob final Clob clob = value instanceof WrappedClob
? ( (WrappedClob) value ).getWrappedClob() ? ( (WrappedClob) value ).getWrappedClob()
: value; : getOrCreateClob(value, options);
return (X) clob; return (X) clob;
} }
else if ( String.class.isAssignableFrom( type ) ) { else if ( String.class.isAssignableFrom( type ) ) {
@ -128,6 +128,21 @@ public class ClobJavaType extends AbstractClassJavaType<Clob> {
throw unknownUnwrap( type ); throw unknownUnwrap( type );
} }
private Clob getOrCreateClob(Clob value, WrapperOptions options) throws SQLException {
if(options.getDialect().useConnectionToCreateLob()) {
if(value.length() == 0) {
// empty Clob
return options.getLobCreator().createClob("");
}
else {
return options.getLobCreator().createClob(value.getSubString(1, (int) value.length()));
}
}
else {
return value;
}
}
public <X> Clob wrap(X value, WrapperOptions options) { public <X> Clob wrap(X value, WrapperOptions options) {
if ( value == null ) { if ( value == null ) {
return null; return null;

View File

@ -28,6 +28,7 @@ import org.hibernate.type.descriptor.WrapperOptions;
* treat them as immutable because we cannot properly check them for changes nor deep copy them. * treat them as immutable because we cannot properly check them for changes nor deep copy them.
* *
* @author Steve Ebersole * @author Steve Ebersole
* @author Loïc Lefèvre
*/ */
public class NClobJavaType extends AbstractClassJavaType<NClob> { public class NClobJavaType extends AbstractClassJavaType<NClob> {
public static final NClobJavaType INSTANCE = new NClobJavaType(); public static final NClobJavaType INSTANCE = new NClobJavaType();
@ -105,7 +106,7 @@ public class NClobJavaType extends AbstractClassJavaType<NClob> {
else if (NClob.class.isAssignableFrom( type )) { else if (NClob.class.isAssignableFrom( type )) {
final NClob nclob = value instanceof WrappedNClob final NClob nclob = value instanceof WrappedNClob
? ( (WrappedNClob) value ).getWrappedNClob() ? ( (WrappedNClob) value ).getWrappedNClob()
: value; : getOrCreateNClob(value, options);
return (X) nclob; return (X) nclob;
} }
} }
@ -116,6 +117,22 @@ public class NClobJavaType extends AbstractClassJavaType<NClob> {
throw unknownUnwrap( type ); throw unknownUnwrap( type );
} }
private NClob getOrCreateNClob(NClob value, WrapperOptions options) throws SQLException {
if(options.getDialect().useConnectionToCreateLob()) {
if(value.length() == 0) {
// empty NClob
return options.getLobCreator().createNClob("");
}
else {
return options.getLobCreator().createNClob(value.getSubString(1, (int) value.length()));
}
}
else {
return value;
}
}
public <X> NClob wrap(X value, WrapperOptions options) { public <X> NClob wrap(X value, WrapperOptions options) {
if ( value == null ) { if ( value == null ) {
return null; return null;

View File

@ -9,6 +9,7 @@ package org.hibernate.type.descriptor.java;
import java.io.Reader; import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import java.sql.Clob; import java.sql.Clob;
import java.sql.NClob;
import java.util.Arrays; import java.util.Arrays;
import org.hibernate.engine.jdbc.CharacterStream; import org.hibernate.engine.jdbc.CharacterStream;
@ -62,6 +63,9 @@ public class PrimitiveCharacterArrayJavaType extends AbstractClassJavaType<char[
if ( String.class.isAssignableFrom( type ) ) { if ( String.class.isAssignableFrom( type ) ) {
return (X) new String( value ); return (X) new String( value );
} }
if ( NClob.class.isAssignableFrom( type ) ) {
return (X) options.getLobCreator().createNClob( new String( value ) );
}
if ( Clob.class.isAssignableFrom( type ) ) { if ( Clob.class.isAssignableFrom( type ) ) {
return (X) options.getLobCreator().createClob( new String( value ) ); return (X) options.getLobCreator().createClob( new String( value ) );
} }

View File

@ -9,6 +9,7 @@ package org.hibernate.type.descriptor.java;
import java.io.Reader; import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import java.sql.Clob; import java.sql.Clob;
import java.sql.NClob;
import java.sql.Types; import java.sql.Types;
import org.hibernate.engine.jdbc.CharacterStream; import org.hibernate.engine.jdbc.CharacterStream;
@ -76,6 +77,9 @@ public class StringJavaType extends AbstractClassJavaType<String> {
if ( DataHelper.isNClob( type ) ) { if ( DataHelper.isNClob( type ) ) {
return (X) options.getLobCreator().createNClob( value ); return (X) options.getLobCreator().createNClob( value );
} }
if ( NClob.class.isAssignableFrom( type ) ) {
return (X) options.getLobCreator().createNClob( value );
}
if ( Clob.class.isAssignableFrom( type ) ) { if ( Clob.class.isAssignableFrom( type ) ) {
return (X) options.getLobCreator().createClob( value ); return (X) options.getLobCreator().createClob( value );
} }

View File

@ -10,6 +10,8 @@ import java.sql.Blob;
import java.sql.Clob; import java.sql.Clob;
import java.util.TimeZone; import java.util.TimeZone;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.H2Dialect;
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.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -78,6 +80,18 @@ public abstract class AbstractDescriptorTest<T> extends BaseUnitTestCase {
public TimeZone getJdbcTimeZone() { public TimeZone getJdbcTimeZone() {
return null; return null;
} }
private final Dialect dialect = new H2Dialect() {
@Override
public boolean useConnectionToCreateLob() {
return false;
}
};
@Override
public Dialect getDialect() {
return dialect;
}
}; };
public AbstractDescriptorTest(JavaType<T> typeDescriptor) { public AbstractDescriptorTest(JavaType<T> typeDescriptor) {

View File

@ -12,6 +12,8 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.TimeZone; import java.util.TimeZone;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.H2Dialect;
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.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -75,6 +77,18 @@ public class StringValueMappingTest {
public TimeZone getJdbcTimeZone() { public TimeZone getJdbcTimeZone() {
return null; return null;
} }
private final Dialect dialect = new H2Dialect() {
@Override
public boolean useConnectionToCreateLob() {
return false;
}
};
@Override
public Dialect getDialect() {
return dialect;
}
}; };
public static final int COLUMN_POSITION = 0; public static final int COLUMN_POSITION = 0;