From f9e544e394ffb3106cd408373d3852454dd60140 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 4 Nov 2024 12:57:03 +0100 Subject: [PATCH] improve javadoc experiment with transferTo() to copy Blob/Clob streams instead of getSubString() Signed-off-by: Gavin King --- .../org/hibernate/engine/jdbc/LobCreator.java | 6 + .../jdbc/env/internal/BlobAndClobCreator.java | 115 +++++++++++++++--- .../env/internal/LobCreatorBuilderImpl.java | 4 +- 3 files changed, 105 insertions(+), 20 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/LobCreator.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/LobCreator.java index 50cfca5c93..46048a49da 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/LobCreator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/LobCreator.java @@ -108,6 +108,8 @@ public interface LobCreator { * Return an instance which can actually be written to a JDBC * {@code PreparedStatement}. * + * @see java.sql.PreparedStatement#setBlob(int, Blob) + * * @apiNote This is needed for Oracle * * @see org.hibernate.dialect.Dialect#useConnectionToCreateLob @@ -120,6 +122,8 @@ public interface LobCreator { * Return an instance which can actually be written to a JDBC * {@code PreparedStatement}. * + * @see java.sql.PreparedStatement#setClob(int, Clob) + * * @apiNote This is needed for Oracle * * @see org.hibernate.dialect.Dialect#useConnectionToCreateLob @@ -132,6 +136,8 @@ public interface LobCreator { * Return an instance which can actually be written to a JDBC * {@code PreparedStatement}. * + * @see java.sql.PreparedStatement#setNClob(int, NClob) + * * @apiNote This is needed for Oracle * * @see org.hibernate.dialect.Dialect#useConnectionToCreateLob diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/BlobAndClobCreator.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/BlobAndClobCreator.java index f1e271709a..2d24ea1c24 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/BlobAndClobCreator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/BlobAndClobCreator.java @@ -10,6 +10,7 @@ import java.sql.Clob; import java.sql.Connection; import java.sql.NClob; +import java.sql.ResultSet; import java.sql.SQLException; import org.hibernate.JDBCException; @@ -37,6 +38,11 @@ public class BlobAndClobCreator extends AbstractLobCreator implements LobCreator */ public static final LobCreationContext.Callback CREATE_CLOB_CALLBACK = Connection::createClob; + /** + * Callback for performing contextual NCLOB creation + */ + public static final LobCreationContext.Callback CREATE_NCLOB_CALLBACK = Connection::createNClob; + protected final LobCreationContext lobCreationContext; protected final boolean useConnectionToCreateLob; @@ -54,6 +60,10 @@ Blob createBlob() { return lobCreationContext.fromContext( CREATE_BLOB_CALLBACK ); } + /** + * Create a {@link Blob} object after reading a {@code byte[]} + * array from a JDBC {@link ResultSet}. + */ @Override public Blob createBlob(byte[] bytes) { final Blob blob = createBlob(); @@ -66,11 +76,18 @@ public Blob createBlob(byte[] bytes) { } } + /** + * Create a {@link Blob} object after reading an {@link InputStream} + * from a JDBC {@link ResultSet}. + * + * @implNote + * It's very inefficient to use JDBC LOB locator creation to create + * a LOB with the contents of the given stream, since that requires + * reading the whole stream. So instead just wrap the given stream, + * just like what {@link NonContextualLobCreator} does. + */ @Override public Blob createBlob(InputStream stream, long length) { - // IMPL NOTE: it's 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.createBlob( stream, length ); } @@ -79,10 +96,23 @@ public Blob createBlob(InputStream stream, long length) { * * @return The created CLOB reference. */ - public Clob createClob() { + Clob createClob() { return lobCreationContext.fromContext( CREATE_CLOB_CALLBACK ); } + /** + * Create the basic contextual NCLOB reference. + * + * @return The created NCLOB reference. + */ + NClob createNClob() { + return lobCreationContext.fromContext( CREATE_NCLOB_CALLBACK ); + } + + /** + * Create a {@link Clob} object after reading a {@code String} + * from a JDBC {@link ResultSet}. + */ @Override public Clob createClob(String string) { try { @@ -95,11 +125,18 @@ public Clob createClob(String string) { } } + /** + * Create a {@link Clob} object after reading an {@link InputStream} + * from a JDBC {@link ResultSet}. + * + * @implNote + * It's very inefficient to use JDBC LOB locator creation to create + * a LOB with the contents of the given stream, since that requires + * reading the whole stream. So instead just wrap the given stream, + * just like what {@link NonContextualLobCreator} does. + */ @Override public Clob createClob(Reader reader, long length) { - // IMPL NOTE: it's 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.createClob( reader, length ); } @@ -113,39 +150,81 @@ public NClob createNClob(Reader reader, long length) { return NonContextualLobCreator.INSTANCE.createNClob( reader, length ); } + /** + * Obtain a {@link Blob} instance which can be written to a JDBC + * {@link java.sql.PreparedStatement} using + * {@link java.sql.PreparedStatement#setBlob(int, Blob)}. + */ @Override public Blob toJdbcBlob(Blob blob) { try { - return useConnectionToCreateLob - ? createBlob( blob.getBytes( 1, (int) blob.length() ) ) - : super.toJdbcBlob( blob ); + if ( useConnectionToCreateLob ) { +// final Blob jdbcBlob = createBlob(); +// blob.getBinaryStream().transferTo( jdbcBlob.setBinaryStream(1) ); +// return jdbcBlob; + return createBlob( blob.getBytes( 1, (int) blob.length() ) ); + } + else { + return super.toJdbcBlob( blob ); + } } catch (SQLException e) { - throw new JDBCException( "Could not create JDBC Clob", e ); + throw new JDBCException( "Could not create JDBC Blob", e ); } +// catch (IOException e) { +// throw new HibernateException( "Could not create JDBC Blob", e ); +// } } + /** + * Obtain a {@link Clob} instance which can be written to a JDBC + * {@link java.sql.PreparedStatement} using + * {@link java.sql.PreparedStatement#setClob(int, Clob)}. + */ @Override public Clob toJdbcClob(Clob clob) { try { - return useConnectionToCreateLob - ? createClob( clob.getSubString( 1, (int) clob.length() ) ) - : super.toJdbcClob( clob ); + if ( useConnectionToCreateLob ) { +// final Clob jdbcClob = createClob(); +// clob.getCharacterStream().transferTo( jdbcClob.setCharacterStream(1) ); +// return jdbcClob; + return createClob( clob.getSubString( 1, (int) clob.length() ) ); + } + else { + return super.toJdbcClob( clob ); + } } catch (SQLException e) { throw new JDBCException( "Could not create JDBC Clob", e ); } +// catch (IOException e) { +// throw new HibernateException( "Could not create JDBC Clob", e ); +// } } + /** + * Obtain an {@link NClob} instance which can be written to a JDBC + * {@link java.sql.PreparedStatement} using + * {@link java.sql.PreparedStatement#setNClob(int, NClob)}. + */ @Override public NClob toJdbcNClob(NClob clob) { try { - return useConnectionToCreateLob - ? createNClob( clob.getSubString( 1, (int) clob.length() ) ) - : super.toJdbcNClob( clob ); + if ( useConnectionToCreateLob ) { +// final NClob jdbcClob = createNClob(); +// clob.getCharacterStream().transferTo( jdbcClob.setCharacterStream(1) ); +// return jdbcClob; + return createNClob( clob.getSubString( 1, (int) clob.length() ) ); + } + else { + return super.toJdbcNClob( clob ); + } } catch (SQLException e) { - throw new JDBCException( "Could not create JDBC Clob", e ); + throw new JDBCException( "Could not create JDBC NClob", e ); } +// catch (IOException e) { +// throw new HibernateException( "Could not create JDBC NClob", e ); +// } } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/LobCreatorBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/LobCreatorBuilderImpl.java index 2c41041f55..73c3737e85 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/LobCreatorBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/env/internal/LobCreatorBuilderImpl.java @@ -52,7 +52,7 @@ public static LobCreatorBuilderImpl makeLobCreatorBuilder( } /** - * For used when JDBC Connection is not available. + * For use when JDBC {@link Connection} is not available. * * @return Appropriate LobCreatorBuilder */ @@ -62,7 +62,7 @@ public static LobCreatorBuilderImpl makeLobCreatorBuilder(Dialect dialect) { } /** - * Build a LobCreator using the given context + * Build a {@link LobCreator} using the given context * * @param lobCreationContext The LOB creation context *