diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/lob/DefaultLobHandler.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/lob/DefaultLobHandler.java index 6964172e962..e570be24f03 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/lob/DefaultLobHandler.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/lob/DefaultLobHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,18 +32,20 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** - * Default implementation of the {@link LobHandler} interface. Invokes - * the direct accessor methods that {@code java.sql.ResultSet} + * Default implementation of the {@link LobHandler} interface. + * Invokes the direct accessor methods that {@code java.sql.ResultSet} * and {@code java.sql.PreparedStatement} offer. * *
This LobHandler should work for any JDBC driver that is JDBC compliant * in terms of the spec's suggestions regarding simple BLOB and CLOB handling. - * This does not apply to Oracle 9i, and only to a limited degree to Oracle 10g! - * As a consequence, use {@link OracleLobHandler} for accessing Oracle BLOBs/CLOBs. + * This does not apply to Oracle 9i's drivers at all; as of Oracle 10g, + * it does work but may still come with LOB size limitations. Consider using + * recent Oracle drivers even when working against an older database server. + * See the {@link LobHandler} javadoc for the full set of recommendations. * *
Some JDBC drivers require values with a BLOB/CLOB target column to be - * explicitly set through the JDBC {@code setBlob} / {@code setClob} - * API: for example, PostgreSQL's driver. Switch the {@link #setWrapAsLob "wrapAsLob"} + * explicitly set through the JDBC {@code setBlob} / {@code setClob} API: + * for example, PostgreSQL's driver. Switch the {@link #setWrapAsLob "wrapAsLob"} * property to "true" when operating against such a driver. * *
On JDBC 4.0, this LobHandler also supports streaming the BLOB/CLOB content @@ -51,11 +53,15 @@ import org.apache.commons.logging.LogFactory; * argument directly. Consider switching the {@link #setStreamAsLob "streamAsLob"} * property to "true" when operating against a fully compliant JDBC 4.0 driver. * - *
See the {@link LobHandler} javadoc for a summary of recommendations. + *
Finally, primarily as a direct equivalent to {@link OracleLobHandler}, + * this LobHandler also supports the creation of temporary BLOB/CLOB objects. + * Consider switching the {@link #setCreateTemporaryLob "createTemporaryLob"} + * property to "true" when "streamAsLob" happens to run into LOB size limitations. + * + *
See the {@link LobHandler} interface javadoc for a summary of recommendations. * * @author Juergen Hoeller * @since 04.12.2003 - * @see #setStreamAsLob * @see java.sql.ResultSet#getBytes * @see java.sql.ResultSet#getBinaryStream * @see java.sql.ResultSet#getString @@ -75,15 +81,18 @@ public class DefaultLobHandler extends AbstractLobHandler { private boolean streamAsLob = false; + private boolean createTemporaryLob = false; + /** * Specify whether to submit a byte array / String to the JDBC driver * wrapped in a JDBC Blob / Clob object, using the JDBC {@code setBlob} / * {@code setClob} method with a Blob / Clob argument. *
Default is "false", using the common JDBC 2.0 {@code setBinaryStream} - * / {@code setCharacterStream} method for setting the content. - * Switch this to "true" for explicit Blob / Clob wrapping against - * JDBC drivers that are known to require such wrapping (e.g. PostgreSQL's). + * / {@code setCharacterStream} method for setting the content. Switch this + * to "true" for explicit Blob / Clob wrapping against JDBC drivers that + * are known to require such wrapping (e.g. PostgreSQL's for access to OID + * columns, whereas BYTEA columns need to be accessed the standard way). *
This setting affects byte array / String arguments as well as stream * arguments, unless {@link #setStreamAsLob "streamAsLob"} overrides this * handling to use JDBC 4.0's new explicit streaming support (if available). @@ -100,7 +109,7 @@ public class DefaultLobHandler extends AbstractLobHandler { * {@code setClob} method with a stream argument. *
Default is "false", using the common JDBC 2.0 {@code setBinaryStream} * / {@code setCharacterStream} method for setting the content. - * Switch this to "true" for explicit JDBC 4.0 usage, provided that your + * Switch this to "true" for explicit JDBC 4.0 streaming, provided that your * JDBC driver actually supports those JDBC 4.0 operations (e.g. Derby's). *
This setting affects stream arguments as well as byte array / String * arguments, requiring JDBC 4.0 support. For supporting LOB content against @@ -112,6 +121,23 @@ public class DefaultLobHandler extends AbstractLobHandler { this.streamAsLob = streamAsLob; } + /** + * Specify whether to copy a byte array / String into a temporary JDBC + * Blob / Clob object created through the JDBC 4.0 {@code createBlob} / + * {@code createClob} methods. + *
Default is "false", using the common JDBC 2.0 {@code setBinaryStream} + * / {@code setCharacterStream} method for setting the content. Switch this + * to "true" for explicit Blob / Clob creation using JDBC 4.0. + *
This setting affects stream arguments as well as byte array / String + * arguments, requiring JDBC 4.0 support. For supporting LOB content against + * JDBC 3.0, check out the {@link #setWrapAsLob "wrapAsLob"} setting. + * @see java.sql.Connection#createBlob() + * @see java.sql.Connection#createClob() + */ + public void setCreateTemporaryLob(boolean createTemporaryLob) { + this.createTemporaryLob = createTemporaryLob; + } + public byte[] getBlobAsBytes(ResultSet rs, int columnIndex) throws SQLException { logger.debug("Returning BLOB as bytes"); @@ -169,12 +195,12 @@ public class DefaultLobHandler extends AbstractLobHandler { } public LobCreator getLobCreator() { - return new DefaultLobCreator(); + return (this.createTemporaryLob ? new TemporaryLobCreator() : new DefaultLobCreator()); } /** - * Default LobCreator implementation as inner class. + * Default LobCreator implementation as an inner class. * Can be subclassed in DefaultLobHandler extensions. */ protected class DefaultLobCreator implements LobCreator { @@ -268,15 +294,10 @@ public class DefaultLobHandler extends AbstractLobHandler { PreparedStatement ps, int paramIndex, InputStream asciiStream, int contentLength) throws SQLException { - if (streamAsLob || wrapAsLob) { + if (streamAsLob) { if (asciiStream != null) { try { - if (streamAsLob) { - ps.setClob(paramIndex, new InputStreamReader(asciiStream, "US-ASCII"), contentLength); - } - else { - ps.setClob(paramIndex, new PassThroughClob(asciiStream, contentLength)); - } + ps.setClob(paramIndex, new InputStreamReader(asciiStream, "US-ASCII"), contentLength); } catch (UnsupportedEncodingException ex) { throw new SQLException("US-ASCII encoding not supported: " + ex); @@ -286,6 +307,14 @@ public class DefaultLobHandler extends AbstractLobHandler { ps.setClob(paramIndex, (Clob) null); } } + else if (wrapAsLob) { + if (asciiStream != null) { + ps.setClob(paramIndex, new PassThroughClob(asciiStream, contentLength)); + } + else { + ps.setClob(paramIndex, (Clob) null); + } + } else { ps.setAsciiStream(paramIndex, asciiStream, contentLength); } @@ -325,7 +354,7 @@ public class DefaultLobHandler extends AbstractLobHandler { } public void close() { - // nothing to do here + // nothing to do when not creating temporary LOBs } } diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/support/lob/LobHandler.java b/spring-jdbc/src/main/java/org/springframework/jdbc/support/lob/LobHandler.java index 251a63a0e8d..07e1847e4f5 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/support/lob/LobHandler.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/support/lob/LobHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,7 +24,7 @@ import java.sql.SQLException; /** * Abstraction for handling large binary fields and large text fields in * specific databases, no matter if represented as simple types or Large OBjects. - * Its main purpose is to isolate Oracle's peculiar handling of LOBs in + * Its main purpose is to isolate Oracle 9i's peculiar handling of LOBs in * {@link OracleLobHandler}; most other databases should be able to work * with the provided {@link DefaultLobHandler}. * @@ -45,8 +45,10 @@ import java.sql.SQLException; * proprietary BLOB/CLOB API, and additionally doesn't accept large streams for * PreparedStatement's corresponding setter methods. Therefore, you need to use * {@link OracleLobHandler} there, which uses Oracle's BLOB/CLOB API for both types - * of access. The Oracle 10g JDBC driver should basically work with - * {@link DefaultLobHandler} as well, with some limitations in terms of LOB sizes. + * of access. The Oracle 10g+ JDBC driver will work with {@link DefaultLobHandler} + * as well, with some limitations in terms of LOB sizes depending on DBMS setup; + * as of Oracle 11g (or actually, using the 11g driver even against older databases), + * there should be no need to use {@link OracleLobHandler} at all anymore. * *
Of course, you need to declare different field types for each database. * In Oracle, any binary content needs to go into a BLOB, and all character content @@ -57,12 +59,20 @@ import java.sql.SQLException; * *
Summarizing the recommended options (for actual LOB fields): *
Used by DefaultLobHandler's {@link DefaultLobHandler#setCreateTemporaryLob} mode.
+ * Can also be used directly to reuse the tracking and freeing of temporary LOBs.
+ *
+ * @author Juergen Hoeller
+ * @since 3.2.2
+ * @see DefaultLobHandler#setCreateTemporaryLob
+ * @see java.sql.Connection#createBlob()
+ * @see java.sql.Connection#createClob()
+ */
+public class TemporaryLobCreator implements LobCreator {
+
+ protected static final Log logger = LogFactory.getLog(TemporaryLobCreator.class);
+
+ private final Set