001/* 002 * Copyright 2002-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.jdbc.support.lob; 018 019import java.io.InputStream; 020import java.io.OutputStream; 021import java.io.Reader; 022import java.io.Writer; 023import java.lang.reflect.InvocationTargetException; 024import java.lang.reflect.Method; 025import java.sql.Blob; 026import java.sql.Clob; 027import java.sql.Connection; 028import java.sql.PreparedStatement; 029import java.sql.ResultSet; 030import java.sql.SQLException; 031import java.util.HashMap; 032import java.util.Iterator; 033import java.util.LinkedList; 034import java.util.List; 035import java.util.Map; 036 037import org.apache.commons.logging.Log; 038import org.apache.commons.logging.LogFactory; 039 040import org.springframework.dao.DataAccessResourceFailureException; 041import org.springframework.dao.InvalidDataAccessApiUsageException; 042import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor; 043import org.springframework.util.FileCopyUtils; 044 045/** 046 * {@link LobHandler} implementation for Oracle databases. Uses proprietary API 047 * to create {@code oracle.sql.BLOB} and {@code oracle.sql.CLOB} 048 * instances, as necessary when working with Oracle's JDBC driver. 049 * Note that this LobHandler requires Oracle JDBC driver 9i or higher! 050 * 051 * <p>While most databases are able to work with {@link DefaultLobHandler}, 052 * Oracle 9i (or more specifically, the Oracle 9i JDBC driver) just accepts 053 * Blob/Clob instances created via its own proprietary BLOB/CLOB API, 054 * and additionally doesn't accept large streams for PreparedStatement's 055 * corresponding setter methods. Therefore, you need to use a strategy like 056 * this LobHandler implementation, or upgrade to the Oracle 10g/11g driver 057 * (which still supports access to Oracle 9i databases). 058 * 059 * <p><b>NOTE: As of Oracle 10.2, {@link DefaultLobHandler} should work equally 060 * well out of the box. On Oracle 11g, JDBC 4.0 based options such as 061 * {@link DefaultLobHandler#setStreamAsLob} and {@link DefaultLobHandler#setCreateTemporaryLob} 062 * are available as well, rendering this proprietary OracleLobHandler obsolete.</b> 063 * Also, consider upgrading to a new driver even when accessing an older database. 064 * See the {@link LobHandler} interface javadoc for a summary of recommendations. 065 * 066 * <p>Needs to work on a native JDBC Connection, to be able to cast it to 067 * {@code oracle.jdbc.OracleConnection}. If you pass in Connections from a 068 * connection pool (the usual case in a Java EE environment), you need to set an 069 * appropriate {@link org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor} 070 * to allow for automatic retrieval of the underlying native JDBC Connection. 071 * LobHandler and NativeJdbcExtractor are separate concerns, therefore they 072 * are represented by separate strategy interfaces. 073 * 074 * <p>Coded via reflection to avoid dependencies on Oracle classes. 075 * Even reads in Oracle constants via reflection because of different Oracle 076 * drivers (classes12, ojdbc14, ojdbc5, ojdbc6) having different constant values! 077 * As this LobHandler initializes Oracle classes on instantiation, do not define 078 * this as eager-initializing singleton if you do not want to depend on the Oracle 079 * JAR being in the class path: use "lazy-init=true" to avoid this issue. 080 * 081 * @author Juergen Hoeller 082 * @author Thomas Risberg 083 * @since 04.12.2003 084 * @see DefaultLobHandler 085 * @see #setNativeJdbcExtractor 086 * @deprecated in favor of {@link DefaultLobHandler} for the Oracle 10g driver and 087 * higher. Consider using the 10g/11g driver even against an Oracle 9i database! 088 * {@link DefaultLobHandler#setCreateTemporaryLob} is the direct equivalent of this 089 * OracleLobHandler's implementation strategy, just using standard JDBC 4.0 API. 090 * That said, in most cases, regular DefaultLobHandler setup will work fine as well. 091 */ 092@Deprecated 093public class OracleLobHandler extends AbstractLobHandler { 094 095 private static final String BLOB_CLASS_NAME = "oracle.sql.BLOB"; 096 097 private static final String CLOB_CLASS_NAME = "oracle.sql.CLOB"; 098 099 private static final String DURATION_SESSION_FIELD_NAME = "DURATION_SESSION"; 100 101 private static final String MODE_READWRITE_FIELD_NAME = "MODE_READWRITE"; 102 103 private static final String MODE_READONLY_FIELD_NAME = "MODE_READONLY"; 104 105 106 protected final Log logger = LogFactory.getLog(getClass()); 107 108 private NativeJdbcExtractor nativeJdbcExtractor; 109 110 private Boolean cache = Boolean.TRUE; 111 112 private Boolean releaseResourcesAfterRead = Boolean.FALSE; 113 114 private Class<?> blobClass; 115 116 private Class<?> clobClass; 117 118 private final Map<Class<?>, Integer> durationSessionConstants = new HashMap<Class<?>, Integer>(2); 119 120 private final Map<Class<?>, Integer> modeReadWriteConstants = new HashMap<Class<?>, Integer>(2); 121 122 private final Map<Class<?>, Integer> modeReadOnlyConstants = new HashMap<Class<?>, Integer>(2); 123 124 125 /** 126 * Set an appropriate NativeJdbcExtractor to be able to retrieve the underlying 127 * native {@code oracle.jdbc.OracleConnection}. This is necessary for 128 * DataSource-based connection pools, as those need to return wrapped JDBC 129 * Connection handles that cannot be cast to a native Connection implementation. 130 * <p>Effectively, this LobHandler just invokes a single NativeJdbcExtractor 131 * method, namely {@code getNativeConnectionFromStatement} with a 132 * PreparedStatement argument (falling back to a 133 * {@code PreparedStatement.getConnection()} call if no extractor is set). 134 * <p>A common choice is {@code SimpleNativeJdbcExtractor}, whose Connection unwrapping 135 * (which is what OracleLobHandler needs) will work with many connection pools. 136 * See {@code SimpleNativeJdbcExtractor} and 137 * <a href="https://download.oracle.com/otn_hosted_doc/jdeveloper/905/jdbc-javadoc/oracle/jdbc/OracleConnection.html"> 138 * oracle.jdbc.OracleConnection</a> javadoc for details. 139 * @see org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor#getNativeConnectionFromStatement 140 * @see org.springframework.jdbc.support.nativejdbc.SimpleNativeJdbcExtractor 141 * @see org.springframework.jdbc.support.nativejdbc.OracleJdbc4NativeJdbcExtractor 142 */ 143 public void setNativeJdbcExtractor(NativeJdbcExtractor nativeJdbcExtractor) { 144 this.nativeJdbcExtractor = nativeJdbcExtractor; 145 } 146 147 /** 148 * Set whether to cache the temporary LOB in the buffer cache. 149 * This value will be passed into BLOB/CLOB.createTemporary. 150 * <p>Default is {@code true}. 151 * <p><strong>See Also:</strong> 152 * <ul> 153 * <li><a href="https://download.oracle.com/otn_hosted_doc/jdeveloper/905/jdbc-javadoc/oracle/sql/BLOB.html#createTemporary()">oracle.sql.BLOB.createTemporary</a></li> 154 * <li><a href="https://download.oracle.com/otn_hosted_doc/jdeveloper/905/jdbc-javadoc/oracle/sql/CLOB.html#createTemporary()">oracle.sql.CLOB.createTemporary</a></li> 155 * </ul> 156 */ 157 public void setCache(boolean cache) { 158 this.cache = cache; 159 } 160 161 /** 162 * Set whether to aggressively release any resources used by the LOB. If set to {@code true} 163 * then you can only read the LOB values once. Any subsequent reads will fail since the resources 164 * have been closed. 165 * <p>Setting this property to {@code true} can be useful when your queries generates large 166 * temporary LOBs that occupy space in the TEMPORARY tablespace or when you want to free up any 167 * memory allocated by the driver for the LOB reading. 168 * <p>Default is {@code false}. 169 * <p><strong>See Also:</strong> 170 * <ul> 171 * <li><a href="https://download.oracle.com/otn_hosted_doc/jdeveloper/905/jdbc-javadoc/oracle/sql/BLOB.html#freeTemporary()">oracle.sql.BLOB.freeTemporary</a></li> 172 * <li><a href="https://download.oracle.com/otn_hosted_doc/jdeveloper/905/jdbc-javadoc/oracle/sql/CLOB.html#freeTemporary()">oracle.sql.CLOB.freeTemporary</a></li> 173 * <li><a href="https://download.oracle.com/otn_hosted_doc/jdeveloper/905/jdbc-javadoc/oracle/sql/BLOB.html#open()">oracle.sql.BLOB.open</a></li> 174 * <li><a href="https://download.oracle.com/otn_hosted_doc/jdeveloper/905/jdbc-javadoc/oracle/sql/CLOB.html#open()">oracle.sql.CLOB.open</a></li> 175 * <li><a href="https://download.oracle.com/otn_hosted_doc/jdeveloper/905/jdbc-javadoc/oracle/sql/BLOB.html#open()">oracle.sql.BLOB.close</a></li> 176 * <li><a href="https://download.oracle.com/otn_hosted_doc/jdeveloper/905/jdbc-javadoc/oracle/sql/CLOB.html#open()">oracle.sql.CLOB.close</a></li> 177 * </ul> 178 */ 179 public void setReleaseResourcesAfterRead(boolean releaseResources) { 180 this.releaseResourcesAfterRead = releaseResources; 181 } 182 183 184 /** 185 * Retrieve the {@code oracle.sql.BLOB} and {@code oracle.sql.CLOB} 186 * classes via reflection, and initialize the values for the 187 * DURATION_SESSION, MODE_READWRITE and MODE_READONLY constants defined there. 188 * <p><strong>See Also:</strong> 189 * <ul> 190 * <li><a href="https://download.oracle.com/otn_hosted_doc/jdeveloper/905/jdbc-javadoc/oracle/sql/BLOB.html#DURATION_SESSION">oracle.sql.BLOB.DURATION_SESSION</a></li> 191 * <li><a href="https://download.oracle.com/otn_hosted_doc/jdeveloper/905/jdbc-javadoc/oracle/sql/BLOB.html#MODE_READWRITE">oracle.sql.BLOB.MODE_READWRITE</a></li> 192 * <li><a href="https://download.oracle.com/otn_hosted_doc/jdeveloper/905/jdbc-javadoc/oracle/sql/BLOB.html#MODE_READONLY">oracle.sql.BLOB.MODE_READONLY</a></li> 193 * <li><a href="https://download.oracle.com/otn_hosted_doc/jdeveloper/905/jdbc-javadoc/oracle/sql/CLOB.html#DURATION_SESSION">oracle.sql.CLOB.DURATION_SESSION</a></li> 194 * <li><a href="https://download.oracle.com/otn_hosted_doc/jdeveloper/905/jdbc-javadoc/oracle/sql/CLOB.html#MODE_READWRITE">oracle.sql.CLOB.MODE_READWRITE</a></li> 195 * <li><a href="https://download.oracle.com/otn_hosted_doc/jdeveloper/905/jdbc-javadoc/oracle/sql/CLOB.html#MODE_READONLY">oracle.sql.CLOB.MODE_READONLY</a></li> 196 * </ul> 197 * @param con the Oracle Connection, for using the exact same class loader 198 * that the Oracle driver was loaded with 199 */ 200 protected synchronized void initOracleDriverClasses(Connection con) { 201 if (this.blobClass == null) { 202 try { 203 // Initialize oracle.sql.BLOB class 204 this.blobClass = con.getClass().getClassLoader().loadClass(BLOB_CLASS_NAME); 205 this.durationSessionConstants.put( 206 this.blobClass, this.blobClass.getField(DURATION_SESSION_FIELD_NAME).getInt(null)); 207 this.modeReadWriteConstants.put( 208 this.blobClass, this.blobClass.getField(MODE_READWRITE_FIELD_NAME).getInt(null)); 209 this.modeReadOnlyConstants.put( 210 this.blobClass, this.blobClass.getField(MODE_READONLY_FIELD_NAME).getInt(null)); 211 212 // Initialize oracle.sql.CLOB class 213 this.clobClass = con.getClass().getClassLoader().loadClass(CLOB_CLASS_NAME); 214 this.durationSessionConstants.put( 215 this.clobClass, this.clobClass.getField(DURATION_SESSION_FIELD_NAME).getInt(null)); 216 this.modeReadWriteConstants.put( 217 this.clobClass, this.clobClass.getField(MODE_READWRITE_FIELD_NAME).getInt(null)); 218 this.modeReadOnlyConstants.put( 219 this.clobClass, this.clobClass.getField(MODE_READONLY_FIELD_NAME).getInt(null)); 220 } 221 catch (Exception ex) { 222 throw new InvalidDataAccessApiUsageException( 223 "Couldn't initialize OracleLobHandler because Oracle driver classes are not available. " + 224 "Note that OracleLobHandler requires Oracle JDBC driver 9i or higher!", ex); 225 } 226 } 227 } 228 229 230 @Override 231 public byte[] getBlobAsBytes(ResultSet rs, int columnIndex) throws SQLException { 232 logger.debug("Returning Oracle BLOB as bytes"); 233 Blob blob = rs.getBlob(columnIndex); 234 initializeResourcesBeforeRead(rs.getStatement().getConnection(), blob); 235 byte[] retVal = (blob != null ? blob.getBytes(1, (int) blob.length()) : null); 236 releaseResourcesAfterRead(rs.getStatement().getConnection(), blob); 237 return retVal; 238 } 239 240 @Override 241 public InputStream getBlobAsBinaryStream(ResultSet rs, int columnIndex) throws SQLException { 242 logger.debug("Returning Oracle BLOB as binary stream"); 243 Blob blob = rs.getBlob(columnIndex); 244 initializeResourcesBeforeRead(rs.getStatement().getConnection(), blob); 245 InputStream retVal = (blob != null ? blob.getBinaryStream() : null); 246 releaseResourcesAfterRead(rs.getStatement().getConnection(), blob); 247 return retVal; 248 } 249 250 @Override 251 public String getClobAsString(ResultSet rs, int columnIndex) throws SQLException { 252 logger.debug("Returning Oracle CLOB as string"); 253 Clob clob = rs.getClob(columnIndex); 254 initializeResourcesBeforeRead(rs.getStatement().getConnection(), clob); 255 String retVal = (clob != null ? clob.getSubString(1, (int) clob.length()) : null); 256 releaseResourcesAfterRead(rs.getStatement().getConnection(), clob); 257 return retVal; 258 } 259 260 @Override 261 public InputStream getClobAsAsciiStream(ResultSet rs, int columnIndex) throws SQLException { 262 logger.debug("Returning Oracle CLOB as ASCII stream"); 263 Clob clob = rs.getClob(columnIndex); 264 initializeResourcesBeforeRead(rs.getStatement().getConnection(), clob); 265 InputStream retVal = (clob != null ? clob.getAsciiStream() : null); 266 releaseResourcesAfterRead(rs.getStatement().getConnection(), clob); 267 return retVal; 268 } 269 270 @Override 271 public Reader getClobAsCharacterStream(ResultSet rs, int columnIndex) throws SQLException { 272 logger.debug("Returning Oracle CLOB as character stream"); 273 Clob clob = rs.getClob(columnIndex); 274 initializeResourcesBeforeRead(rs.getStatement().getConnection(), clob); 275 Reader retVal = (clob != null ? clob.getCharacterStream() : null); 276 releaseResourcesAfterRead(rs.getStatement().getConnection(), clob); 277 return retVal; 278 } 279 280 @Override 281 public LobCreator getLobCreator() { 282 return new OracleLobCreator(); 283 } 284 285 /** 286 * Initialize any LOB resources before a read is done. 287 * <p>This implementation calls {@code BLOB.open(BLOB.MODE_READONLY)} or 288 * {@code CLOB.open(CLOB.MODE_READONLY)} on any non-temporary LOBs if 289 * {@code releaseResourcesAfterRead} property is set to {@code true}. 290 * <p>This method can be overridden by sublcasses if different behavior is desired. 291 * @param con the connection to be usde for initilization 292 * @param lob the LOB to initialize 293 */ 294 protected void initializeResourcesBeforeRead(Connection con, Object lob) { 295 if (this.releaseResourcesAfterRead) { 296 initOracleDriverClasses(con); 297 try { 298 /* 299 if (!((BLOB) lob.isTemporary() { 300 */ 301 Method isTemporary = lob.getClass().getMethod("isTemporary"); 302 Boolean temporary = (Boolean) isTemporary.invoke(lob); 303 if (!temporary) { 304 /* 305 ((BLOB) lob).open(BLOB.MODE_READONLY); 306 */ 307 Method open = lob.getClass().getMethod("open", int.class); 308 open.invoke(lob, this.modeReadOnlyConstants.get(lob.getClass())); 309 } 310 } 311 catch (InvocationTargetException ex) { 312 logger.error("Could not open Oracle LOB", ex.getTargetException()); 313 } 314 catch (Exception ex) { 315 throw new DataAccessResourceFailureException("Could not open Oracle LOB", ex); 316 } 317 } 318 } 319 320 /** 321 * Release any LOB resources after read is complete. 322 * <p>If {@code releaseResourcesAfterRead} property is set to {@code true} 323 * then this implementation calls 324 * {@code BLOB.close()} or {@code CLOB.close()} 325 * on any non-temporary LOBs that are open or 326 * {@code BLOB.freeTemporary()} or {@code CLOB.freeTemporary()} 327 * on any temporary LOBs. 328 * <p>This method can be overridden by sublcasses if different behavior is desired. 329 * @param con the connection to be usde for initilization 330 * @param lob the LOB to initialize 331 */ 332 protected void releaseResourcesAfterRead(Connection con, Object lob) { 333 if (this.releaseResourcesAfterRead) { 334 initOracleDriverClasses(con); 335 Boolean temporary = Boolean.FALSE; 336 try { 337 /* 338 if (((BLOB) lob.isTemporary() { 339 */ 340 Method isTemporary = lob.getClass().getMethod("isTemporary"); 341 temporary = (Boolean) isTemporary.invoke(lob); 342 if (temporary) { 343 /* 344 ((BLOB) lob).freeTemporary(); 345 */ 346 Method freeTemporary = lob.getClass().getMethod("freeTemporary"); 347 freeTemporary.invoke(lob); 348 } 349 else { 350 /* 351 if (((BLOB) lob.isOpen() { 352 */ 353 Method isOpen = lob.getClass().getMethod("isOpen"); 354 Boolean open = (Boolean) isOpen.invoke(lob); 355 if (open) { 356 /* 357 ((BLOB) lob).close(); 358 */ 359 Method close = lob.getClass().getMethod("close"); 360 close.invoke(lob); 361 } 362 } 363 } 364 catch (InvocationTargetException ex) { 365 if (temporary) { 366 logger.error("Could not free Oracle LOB", ex.getTargetException()); 367 } 368 else { 369 logger.error("Could not close Oracle LOB", ex.getTargetException()); 370 } 371 } 372 catch (Exception ex) { 373 if (temporary) { 374 throw new DataAccessResourceFailureException("Could not free Oracle LOB", ex); 375 } 376 else { 377 throw new DataAccessResourceFailureException("Could not close Oracle LOB", ex); 378 } 379 } 380 } 381 } 382 383 384 /** 385 * LobCreator implementation for Oracle databases. 386 * Creates Oracle-style temporary BLOBs and CLOBs that it frees on close. 387 * @see #close 388 */ 389 protected class OracleLobCreator implements LobCreator { 390 391 private final List<Object> temporaryLobs = new LinkedList<Object>(); 392 393 @Override 394 public void setBlobAsBytes(PreparedStatement ps, int paramIndex, final byte[] content) 395 throws SQLException { 396 397 if (content != null) { 398 Blob blob = (Blob) createLob(ps, false, new LobCallback() { 399 @Override 400 public void populateLob(Object lob) throws Exception { 401 Method methodToInvoke = lob.getClass().getMethod("getBinaryOutputStream"); 402 OutputStream out = (OutputStream) methodToInvoke.invoke(lob); 403 FileCopyUtils.copy(content, out); 404 } 405 }); 406 ps.setBlob(paramIndex, blob); 407 if (logger.isDebugEnabled()) { 408 logger.debug("Set bytes for Oracle BLOB with length " + blob.length()); 409 } 410 } 411 else { 412 ps.setBlob(paramIndex, (Blob) null); 413 logger.debug("Set Oracle BLOB to null"); 414 } 415 } 416 417 @Override 418 public void setBlobAsBinaryStream( 419 PreparedStatement ps, int paramIndex, final InputStream binaryStream, int contentLength) 420 throws SQLException { 421 422 if (binaryStream != null) { 423 Blob blob = (Blob) createLob(ps, false, new LobCallback() { 424 @Override 425 public void populateLob(Object lob) throws Exception { 426 Method methodToInvoke = lob.getClass().getMethod("getBinaryOutputStream", (Class[]) null); 427 OutputStream out = (OutputStream) methodToInvoke.invoke(lob, (Object[]) null); 428 FileCopyUtils.copy(binaryStream, out); 429 } 430 }); 431 ps.setBlob(paramIndex, blob); 432 if (logger.isDebugEnabled()) { 433 logger.debug("Set binary stream for Oracle BLOB with length " + blob.length()); 434 } 435 } 436 else { 437 ps.setBlob(paramIndex, (Blob) null); 438 logger.debug("Set Oracle BLOB to null"); 439 } 440 } 441 442 @Override 443 public void setClobAsString(PreparedStatement ps, int paramIndex, final String content) 444 throws SQLException { 445 446 if (content != null) { 447 Clob clob = (Clob) createLob(ps, true, new LobCallback() { 448 @Override 449 public void populateLob(Object lob) throws Exception { 450 Method methodToInvoke = lob.getClass().getMethod("getCharacterOutputStream", (Class[]) null); 451 Writer writer = (Writer) methodToInvoke.invoke(lob, (Object[]) null); 452 FileCopyUtils.copy(content, writer); 453 } 454 }); 455 ps.setClob(paramIndex, clob); 456 if (logger.isDebugEnabled()) { 457 logger.debug("Set string for Oracle CLOB with length " + clob.length()); 458 } 459 } 460 else { 461 ps.setClob(paramIndex, (Clob) null); 462 logger.debug("Set Oracle CLOB to null"); 463 } 464 } 465 466 @Override 467 public void setClobAsAsciiStream( 468 PreparedStatement ps, int paramIndex, final InputStream asciiStream, int contentLength) 469 throws SQLException { 470 471 if (asciiStream != null) { 472 Clob clob = (Clob) createLob(ps, true, new LobCallback() { 473 @Override 474 public void populateLob(Object lob) throws Exception { 475 Method methodToInvoke = lob.getClass().getMethod("getAsciiOutputStream", (Class[]) null); 476 OutputStream out = (OutputStream) methodToInvoke.invoke(lob, (Object[]) null); 477 FileCopyUtils.copy(asciiStream, out); 478 } 479 }); 480 ps.setClob(paramIndex, clob); 481 if (logger.isDebugEnabled()) { 482 logger.debug("Set ASCII stream for Oracle CLOB with length " + clob.length()); 483 } 484 } 485 else { 486 ps.setClob(paramIndex, (Clob) null); 487 logger.debug("Set Oracle CLOB to null"); 488 } 489 } 490 491 @Override 492 public void setClobAsCharacterStream( 493 PreparedStatement ps, int paramIndex, final Reader characterStream, int contentLength) 494 throws SQLException { 495 496 if (characterStream != null) { 497 Clob clob = (Clob) createLob(ps, true, new LobCallback() { 498 @Override 499 public void populateLob(Object lob) throws Exception { 500 Method methodToInvoke = lob.getClass().getMethod("getCharacterOutputStream", (Class[]) null); 501 Writer writer = (Writer) methodToInvoke.invoke(lob, (Object[]) null); 502 FileCopyUtils.copy(characterStream, writer); 503 } 504 }); 505 ps.setClob(paramIndex, clob); 506 if (logger.isDebugEnabled()) { 507 logger.debug("Set character stream for Oracle CLOB with length " + clob.length()); 508 } 509 } 510 else { 511 ps.setClob(paramIndex, (Clob) null); 512 logger.debug("Set Oracle CLOB to null"); 513 } 514 } 515 516 /** 517 * Create a LOB instance for the given PreparedStatement, 518 * populating it via the given callback. 519 */ 520 protected Object createLob(PreparedStatement ps, boolean clob, LobCallback callback) 521 throws SQLException { 522 523 Connection con = null; 524 try { 525 con = getOracleConnection(ps); 526 initOracleDriverClasses(con); 527 Object lob = prepareLob(con, clob ? clobClass : blobClass); 528 callback.populateLob(lob); 529 lob.getClass().getMethod("close", (Class[]) null).invoke(lob, (Object[]) null); 530 this.temporaryLobs.add(lob); 531 if (logger.isDebugEnabled()) { 532 logger.debug("Created new Oracle " + (clob ? "CLOB" : "BLOB")); 533 } 534 return lob; 535 } 536 catch (SQLException ex) { 537 throw ex; 538 } 539 catch (InvocationTargetException ex) { 540 if (ex.getTargetException() instanceof SQLException) { 541 throw (SQLException) ex.getTargetException(); 542 } 543 else if (con != null && ex.getTargetException() instanceof ClassCastException) { 544 throw new InvalidDataAccessApiUsageException( 545 "OracleLobCreator needs to work on [oracle.jdbc.OracleConnection], not on [" + 546 con.getClass().getName() + "]: specify a corresponding NativeJdbcExtractor", 547 ex.getTargetException()); 548 } 549 else { 550 throw new DataAccessResourceFailureException("Could not create Oracle LOB", 551 ex.getTargetException()); 552 } 553 } 554 catch (Exception ex) { 555 throw new DataAccessResourceFailureException("Could not create Oracle LOB", ex); 556 } 557 } 558 559 /** 560 * Retrieve the underlying OracleConnection, using a NativeJdbcExtractor if set. 561 */ 562 protected Connection getOracleConnection(PreparedStatement ps) 563 throws SQLException, ClassNotFoundException { 564 565 return (nativeJdbcExtractor != null ? 566 nativeJdbcExtractor.getNativeConnectionFromStatement(ps) : ps.getConnection()); 567 } 568 569 /** 570 * Create and open an oracle.sql.BLOB/CLOB instance via reflection. 571 */ 572 protected Object prepareLob(Connection con, Class<?> lobClass) throws Exception { 573 /* 574 BLOB blob = BLOB.createTemporary(con, false, BLOB.DURATION_SESSION); 575 blob.open(BLOB.MODE_READWRITE); 576 return blob; 577 */ 578 Method createTemporary = lobClass.getMethod( 579 "createTemporary", Connection.class, boolean.class, int.class); 580 Object lob = createTemporary.invoke(null, con, cache, durationSessionConstants.get(lobClass)); 581 Method open = lobClass.getMethod("open", int.class); 582 open.invoke(lob, modeReadWriteConstants.get(lobClass)); 583 return lob; 584 } 585 586 /** 587 * Free all temporary BLOBs and CLOBs created by this creator. 588 */ 589 @Override 590 public void close() { 591 try { 592 for (Iterator<?> it = this.temporaryLobs.iterator(); it.hasNext();) { 593 /* 594 BLOB blob = (BLOB) it.next(); 595 blob.freeTemporary(); 596 */ 597 Object lob = it.next(); 598 Method freeTemporary = lob.getClass().getMethod("freeTemporary"); 599 freeTemporary.invoke(lob); 600 it.remove(); 601 } 602 } 603 catch (InvocationTargetException ex) { 604 logger.error("Could not free Oracle LOB", ex.getTargetException()); 605 } 606 catch (Exception ex) { 607 throw new DataAccessResourceFailureException("Could not free Oracle LOB", ex); 608 } 609 } 610 } 611 612 613 /** 614 * Internal callback interface for use with createLob. 615 */ 616 protected interface LobCallback { 617 618 /** 619 * Populate the given BLOB or CLOB instance with content. 620 * @throws Exception any exception including InvocationTargetException 621 */ 622 void populateLob(Object lob) throws Exception; 623 } 624 625}