Skip to content

Commit 2d9f58a

Browse files
committed
HHH-18806 attempt to make handling of Clob/NClob more robust
Signed-off-by: Gavin King <[email protected]>
1 parent 1ef22c0 commit 2d9f58a

32 files changed

+399
-276
lines changed

hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3777,12 +3777,12 @@ public boolean useInputStreamToInsertBlob() {
37773777
}
37783778

37793779
/**
3780-
* Should BLOB, CLOB, and NCLOB be created solely using respectively
3781-
* {@link Connection#createBlob()}, {@link Connection#createClob()},
3782-
* and {@link Connection#createNClob()}.
3780+
* Should {@link Blob}, {@link Clob}, and {@link NClob} be created solely
3781+
* using {@link Connection#createBlob()}, {@link Connection#createClob()},
3782+
* and {@link Connection#createNClob()}, instead of allowing the use of
3783+
* our own implementations.
37833784
*
3784-
* @return True if BLOB, CLOB, and NCLOB should be created using JDBC
3785-
* {@link Connection}.
3785+
* @return True if these types should be instantiated using {@link Connection}.
37863786
*
37873787
* @since 6.6
37883788
*/

hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
5959
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
6060
import org.hibernate.type.descriptor.jdbc.JdbcType;
61+
import org.hibernate.type.descriptor.jdbc.NClobJdbcType;
6162
import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
6263
import org.hibernate.type.descriptor.jdbc.TinyIntAsSmallIntJdbcType;
6364
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
@@ -213,7 +214,7 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
213214

214215
// The jTDS driver doesn't support the JDBC4 signatures using 'long length' for stream bindings
215216
jdbcTypeRegistry.addDescriptor( Types.CLOB, ClobJdbcType.CLOB_BINDING );
216-
jdbcTypeRegistry.addDescriptor( Types.NCLOB, ClobJdbcType.CLOB_BINDING );
217+
jdbcTypeRegistry.addDescriptor( Types.NCLOB, NClobJdbcType.NCLOB_BINDING );
217218
}
218219
else {
219220
// jConnect driver only conditionally supports getClob/getNClob depending on a server setting. See

hibernate-core/src/main/java/org/hibernate/engine/jdbc/AbstractLobCreator.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
* Copyright Red Hat Inc. and Hibernate Authors
44
*/
55
package org.hibernate.engine.jdbc;
6+
7+
68
import java.sql.Blob;
79
import java.sql.Clob;
810
import java.sql.NClob;
911

1012
/**
11-
* Convenient base class for proxy-based LobCreator for handling wrapping.
13+
* Convenient base class for proxy-based {@link LobCreator} for handling wrapping.
1214
*
1315
* @author Steve Ebersole
1416
*/
@@ -20,16 +22,28 @@ public Blob wrap(Blob blob) {
2022

2123
@Override
2224
public Clob wrap(Clob clob) {
23-
if ( clob instanceof NClob ) {
24-
return wrap( (NClob) clob );
25-
}
26-
else {
27-
return SerializableClobProxy.generateProxy( clob );
28-
}
25+
return clob instanceof NClob nclob
26+
? wrap( nclob )
27+
: SerializableClobProxy.generateProxy( clob );
2928
}
3029

3130
@Override
3231
public NClob wrap(NClob nclob) {
3332
return SerializableNClobProxy.generateProxy( nclob );
3433
}
34+
35+
@Override
36+
public Blob createBlob(Blob blob) {
37+
return blob;
38+
}
39+
40+
@Override
41+
public Clob createClob(Clob clob) {
42+
return clob;
43+
}
44+
45+
@Override
46+
public NClob createNClob(NClob clob) {
47+
return clob;
48+
}
3549
}

hibernate-core/src/main/java/org/hibernate/engine/jdbc/BinaryStream.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package org.hibernate.engine.jdbc;
66

77
import java.io.InputStream;
8+
import java.sql.Blob;
89

910
/**
1011
* Wraps a binary stream to also provide the length which is needed when binding.
@@ -37,4 +38,12 @@ public interface BinaryStream {
3738
* Release any underlying resources.
3839
*/
3940
void release();
41+
42+
/**
43+
* Use the given {@link LobCreator} to create a {@link Blob}
44+
* with the same data as this binary stream.
45+
*
46+
* @since 7.0
47+
*/
48+
Blob asBlob(LobCreator lobCreator);
4049
}

hibernate-core/src/main/java/org/hibernate/engine/jdbc/BlobProxy.java

Lines changed: 3 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
import java.sql.SQLException;
1212

1313
import org.hibernate.Internal;
14-
import org.hibernate.engine.jdbc.internal.BinaryStreamImpl;
14+
import org.hibernate.engine.jdbc.internal.ArrayBackedBinaryStream;
15+
import org.hibernate.engine.jdbc.internal.StreamBackedBinaryStream;
1516
import org.hibernate.type.descriptor.java.DataHelper;
1617

1718
/**
@@ -45,7 +46,7 @@ public final class BlobProxy implements Blob, BlobImplementer {
4546
* @see #generateProxy(byte[])
4647
*/
4748
private BlobProxy(byte[] bytes) {
48-
binaryStream = new BinaryStreamImpl( bytes );
49+
binaryStream = new ArrayBackedBinaryStream( bytes );
4950
}
5051

5152
/**
@@ -179,45 +180,6 @@ public InputStream getBinaryStream(final long start, final long length) throws S
179180
return DataHelper.subStream( getStream(), start-1, intLength );
180181
}
181182

182-
private static class StreamBackedBinaryStream implements BinaryStream {
183-
184-
private final InputStream stream;
185-
private final long length;
186-
private byte[] bytes;
187-
188-
private StreamBackedBinaryStream(InputStream stream, long length) {
189-
this.stream = stream;
190-
this.length = length;
191-
}
192-
193-
@Override
194-
public InputStream getInputStream() {
195-
return stream;
196-
}
197-
198-
@Override
199-
public byte[] getBytes() {
200-
if ( bytes == null ) {
201-
bytes = DataHelper.extractBytes( stream );
202-
}
203-
return bytes;
204-
}
205-
206-
@Override
207-
public long getLength() {
208-
return length;
209-
}
210-
211-
@Override
212-
public void release() {
213-
try {
214-
stream.close();
215-
}
216-
catch (IOException ignore) {
217-
}
218-
}
219-
}
220-
221183
private static UnsupportedOperationException notSupported() {
222184
return new UnsupportedOperationException( "Blob may not be manipulated from creating session" );
223185
}

hibernate-core/src/main/java/org/hibernate/engine/jdbc/LobCreator.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,8 @@ public interface LobCreator {
100100
* environments, also castable to java.sql.NClob
101101
*/
102102
NClob createNClob(Reader reader, long length);
103+
104+
Blob createBlob(Blob clob);
105+
Clob createClob(Clob clob);
106+
NClob createNClob(NClob clob);
103107
}

hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/BlobAndClobCreator.java

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import org.hibernate.engine.jdbc.NonContextualLobCreator;
2121

2222
/**
23-
* LobCreator which can use {@link Connection#createBlob} and {@link Connection#createClob},
23+
* {@link LobCreator} which can use {@link Connection#createBlob} and {@link Connection#createClob},
2424
* but {@link java.sql.NClob} references are created locally.
2525
*
2626
* @see NClobProxy
@@ -40,9 +40,11 @@ public class BlobAndClobCreator extends AbstractLobCreator implements LobCreator
4040
public static final LobCreationContext.Callback<Clob> CREATE_CLOB_CALLBACK = Connection::createClob;
4141

4242
protected final LobCreationContext lobCreationContext;
43+
protected final boolean useConnectionToCreateLob;
4344

44-
public BlobAndClobCreator(LobCreationContext lobCreationContext) {
45+
public BlobAndClobCreator(LobCreationContext lobCreationContext, boolean useConnectionToCreateLob) {
4546
this.lobCreationContext = lobCreationContext;
47+
this.useConnectionToCreateLob = useConnectionToCreateLob;
4648
}
4749

4850
/**
@@ -68,8 +70,9 @@ public Blob createBlob(byte[] bytes) {
6870

6971
@Override
7072
public Blob createBlob(InputStream stream, long length) {
71-
// IMPL NOTE : it is inefficient to use JDBC LOB locator creation to create a LOB
72-
// backed by a given stream. So just wrap the stream (which is what the NonContextualLobCreator does).
73+
// IMPL NOTE: it's inefficient to use JDBC LOB locator creation to
74+
// create a LOB backed by a given stream. So just wrap the stream
75+
// (which is what the NonContextualLobCreator does).
7376
return NonContextualLobCreator.INSTANCE.createBlob( stream, length );
7477
}
7578

@@ -96,8 +99,9 @@ public Clob createClob(String string) {
9699

97100
@Override
98101
public Clob createClob(Reader reader, long length) {
99-
// IMPL NOTE : it is inefficient to use JDBC LOB locator creation to create a LOB
100-
// backed by a given stream. So just wrap the stream (which is what the NonContextualLobCreator does).
102+
// IMPL NOTE: it's inefficient to use JDBC LOB locator creation to
103+
// create a LOB backed by a given stream. So just wrap the stream
104+
// (which is what the NonContextualLobCreator does).
101105
return NonContextualLobCreator.INSTANCE.createClob( reader, length );
102106
}
103107

@@ -110,4 +114,40 @@ public NClob createNClob(String string) {
110114
public NClob createNClob(Reader reader, long length) {
111115
return NonContextualLobCreator.INSTANCE.createNClob( reader, length );
112116
}
117+
118+
@Override
119+
public Blob createBlob(Blob blob) {
120+
try {
121+
return useConnectionToCreateLob
122+
? createBlob( blob.getBytes( 1, (int) blob.length() ) )
123+
: blob;
124+
}
125+
catch (SQLException e) {
126+
throw new JDBCException( "Could not create JDBC Clob", e );
127+
}
128+
}
129+
130+
@Override
131+
public Clob createClob(Clob clob) {
132+
try {
133+
return useConnectionToCreateLob
134+
? createClob( clob.getSubString( 1, (int) clob.length() ) )
135+
: clob;
136+
}
137+
catch (SQLException e) {
138+
throw new JDBCException( "Could not create JDBC Clob", e );
139+
}
140+
}
141+
142+
@Override
143+
public NClob createNClob(NClob clob) {
144+
try {
145+
return useConnectionToCreateLob
146+
? createNClob( clob.getSubString( 1, (int) clob.length() ) )
147+
: clob;
148+
}
149+
catch (SQLException e) {
150+
throw new JDBCException( "Could not create JDBC Clob", e );
151+
}
152+
}
113153
}

hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/JdbcEnvironmentImpl.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535

3636
import org.jboss.logging.Logger;
3737

38+
import static org.hibernate.engine.jdbc.env.internal.LobCreatorBuilderImpl.makeLobCreatorBuilder;
39+
3840
/**
3941
* @author Steve Ebersole
4042
*/
@@ -100,7 +102,7 @@ public JdbcEnvironmentImpl(final ServiceRegistryImplementor serviceRegistry, fin
100102

101103
this.qualifiedObjectNameFormatter = new QualifiedObjectNameFormatterStandardImpl( nameQualifierSupport );
102104

103-
this.lobCreatorBuilder = LobCreatorBuilderImpl.makeLobCreatorBuilder();
105+
this.lobCreatorBuilder = makeLobCreatorBuilder( dialect );
104106
}
105107

106108
private IdentifierHelperBuilder identifierHelperBuilder(
@@ -201,7 +203,7 @@ public JdbcEnvironmentImpl(
201203
databaseMetaData
202204
);
203205

204-
this.lobCreatorBuilder = LobCreatorBuilderImpl.makeLobCreatorBuilder();
206+
this.lobCreatorBuilder = makeLobCreatorBuilder( dialect );
205207
}
206208

207209
private IdentifierHelper identifierHelper(DatabaseMetaData databaseMetaData, Dialect dialect) {

hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/LobCreatorBuilderImpl.java

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@
2525
* @author Steve Ebersole
2626
*/
2727
public class LobCreatorBuilderImpl implements LobCreatorBuilder {
28+
private final boolean useConnectionToCreateLob;
2829
private final EnumSet<LobTypes> supportedContextualLobTypes;
2930

30-
public LobCreatorBuilderImpl(EnumSet<LobTypes> supportedContextualLobTypes) {
31+
public LobCreatorBuilderImpl(boolean useConnectionToCreateLob, EnumSet<LobTypes> supportedContextualLobTypes) {
32+
this.useConnectionToCreateLob = useConnectionToCreateLob;
3133
this.supportedContextualLobTypes = supportedContextualLobTypes;
3234
}
3335

@@ -46,23 +48,18 @@ public static LobCreatorBuilderImpl makeLobCreatorBuilder(
4648
Dialect dialect,
4749
Map<String,Object> configValues,
4850
Connection jdbcConnection) {
49-
final EnumSet<LobTypes> supportedContextualLobTypes = getSupportedContextualLobTypes(
50-
dialect,
51-
configValues,
52-
jdbcConnection
53-
);
54-
55-
return new LobCreatorBuilderImpl( supportedContextualLobTypes );
51+
return new LobCreatorBuilderImpl( dialect.useConnectionToCreateLob(),
52+
getSupportedContextualLobTypes( dialect, configValues, jdbcConnection ) );
5653
}
5754

5855
/**
5956
* For used when JDBC Connection is not available.
6057
*
6158
* @return Appropriate LobCreatorBuilder
6259
*/
63-
public static LobCreatorBuilderImpl makeLobCreatorBuilder() {
60+
public static LobCreatorBuilderImpl makeLobCreatorBuilder(Dialect dialect) {
6461
LOB_MESSAGE_LOGGER.disablingContextualLOBCreationSinceConnectionNull();
65-
return new LobCreatorBuilderImpl( NONE );
62+
return new LobCreatorBuilderImpl( dialect.useConnectionToCreateLob(), NONE );
6663
}
6764

6865
/**
@@ -76,17 +73,18 @@ public LobCreator buildLobCreator(LobCreationContext lobCreationContext) {
7673
if ( supportedContextualLobTypes.isEmpty() ) {
7774
return NonContextualLobCreator.INSTANCE;
7875
}
79-
80-
if ( supportedContextualLobTypes.contains( LobTypes.BLOB )
76+
else if ( supportedContextualLobTypes.contains( LobTypes.BLOB )
8177
&& supportedContextualLobTypes.contains( LobTypes.CLOB ) ){
8278
if ( !supportedContextualLobTypes.contains( LobTypes.NCLOB ) ) {
83-
return new BlobAndClobCreator( lobCreationContext );
79+
return new BlobAndClobCreator( lobCreationContext, useConnectionToCreateLob );
80+
}
81+
else {
82+
return new StandardLobCreator( lobCreationContext, useConnectionToCreateLob );
8483
}
85-
86-
return new StandardLobCreator( lobCreationContext );
8784
}
88-
89-
LOB_LOGGER.debug( "Unexpected condition resolving type of LobCreator to use. Falling back to NonContextualLobCreator" );
90-
return NonContextualLobCreator.INSTANCE;
85+
else {
86+
LOB_LOGGER.debug( "Unexpected condition resolving type of LobCreator to use. Falling back to NonContextualLobCreator" );
87+
return NonContextualLobCreator.INSTANCE;
88+
}
9189
}
9290
}

hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/StandardLobCreator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ public class StandardLobCreator extends BlobAndClobCreator {
2828
*/
2929
public static final LobCreationContext.Callback<NClob> CREATE_NCLOB_CALLBACK = Connection::createNClob;
3030

31-
public StandardLobCreator(LobCreationContext lobCreationContext) {
32-
super( lobCreationContext );
31+
public StandardLobCreator(LobCreationContext lobCreationContext, boolean useConnectionToCreateLob) {
32+
super( lobCreationContext, useConnectionToCreateLob );
3333
}
3434

3535
/**

0 commit comments

Comments
 (0)