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;
}
@Override
public boolean useConnectionToCreateLob() {
return false;
}
@Override
public boolean supportsOffsetInSubquery() {
return true;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3647,6 +3647,20 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
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
* of {@code INSERT ... SELECT ...} statements?

View File

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

View File

@ -1676,4 +1676,11 @@ public class OracleDialect extends Dialect {
public String[] getDropEnumTypeCommand(String name) {
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;
}
@Override
public boolean useConnectionToCreateLob() {
return false;
}
@Override
public String getSelectClauseNullString(int sqlType, TypeConfiguration typeConfiguration) {
// TODO: adapt this to handle named enum types!

View File

@ -41,7 +41,7 @@ public class NClobProxy extends ClobProxy {
* @return The generated proxy.
*/
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.
*/
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;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
@ -34,6 +35,13 @@ public interface WrapperOptions {
*/
SessionFactoryImplementor getSessionFactory();
/**
* Access to the current dialect.
*/
default Dialect getDialect() {
return getSessionFactory().getJdbcServices().getDialect();
}
/**
* 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 Brett Meyer
* @author Loïc Lefèvre
*/
public class BlobJavaType extends AbstractClassJavaType<Blob> {
public static final BlobJavaType INSTANCE = new BlobJavaType();
@ -133,7 +134,7 @@ public class BlobJavaType extends AbstractClassJavaType<Blob> {
else if (Blob.class.isAssignableFrom( type )) {
final Blob blob = value instanceof WrappedBlob
? ( (WrappedBlob) value ).getWrappedBlob()
: value;
: getOrCreateBlob(value, options);
return (X) blob;
}
}
@ -144,6 +145,21 @@ public class BlobJavaType extends AbstractClassJavaType<Blob> {
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
public <X> Blob wrap(X value, WrapperOptions options) {
if ( value == null ) {

View File

@ -9,6 +9,7 @@ package org.hibernate.type.descriptor.java;
import java.io.Reader;
import java.io.StringReader;
import java.sql.Clob;
import java.sql.NClob;
import java.util.Arrays;
import org.hibernate.engine.jdbc.CharacterStream;
@ -80,6 +81,9 @@ public class CharacterArrayJavaType extends AbstractClassJavaType<Character[]> {
if ( String.class.isAssignableFrom( type ) ) {
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 ) ) {
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.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
/**
* 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.
*
* @author Steve Ebersole
* @author Loïc Lefèvre
*/
public class ClobJavaType extends AbstractClassJavaType<Clob> {
public static final ClobJavaType INSTANCE = new ClobJavaType();
@ -107,7 +107,7 @@ public class ClobJavaType extends AbstractClassJavaType<Clob> {
else if (Clob.class.isAssignableFrom( type )) {
final Clob clob = value instanceof WrappedClob
? ( (WrappedClob) value ).getWrappedClob()
: value;
: getOrCreateClob(value, options);
return (X) clob;
}
else if ( String.class.isAssignableFrom( type ) ) {
@ -128,6 +128,21 @@ public class ClobJavaType extends AbstractClassJavaType<Clob> {
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) {
if ( value == 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.
*
* @author Steve Ebersole
* @author Loïc Lefèvre
*/
public class NClobJavaType extends AbstractClassJavaType<NClob> {
public static final NClobJavaType INSTANCE = new NClobJavaType();
@ -105,7 +106,7 @@ public class NClobJavaType extends AbstractClassJavaType<NClob> {
else if (NClob.class.isAssignableFrom( type )) {
final NClob nclob = value instanceof WrappedNClob
? ( (WrappedNClob) value ).getWrappedNClob()
: value;
: getOrCreateNClob(value, options);
return (X) nclob;
}
}
@ -116,6 +117,22 @@ public class NClobJavaType extends AbstractClassJavaType<NClob> {
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) {
if ( value == null ) {
return null;

View File

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

View File

@ -10,6 +10,8 @@ import java.sql.Blob;
import java.sql.Clob;
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.NonContextualLobCreator;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -78,6 +80,18 @@ public abstract class AbstractDescriptorTest<T> extends BaseUnitTestCase {
public TimeZone getJdbcTimeZone() {
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) {

View File

@ -12,6 +12,8 @@ import java.sql.ResultSet;
import java.sql.SQLException;
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.NonContextualLobCreator;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -75,6 +77,18 @@ public class StringValueMappingTest {
public TimeZone getJdbcTimeZone() {
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;