1 16 17 package org.springframework.jdbc.support.lob; 18 19 import java.io.InputStream ; 20 import java.io.OutputStream ; 21 import java.io.Reader ; 22 import java.io.Writer ; 23 import java.lang.reflect.InvocationTargetException ; 24 import java.lang.reflect.Method ; 25 import java.sql.Blob ; 26 import java.sql.Clob ; 27 import java.sql.Connection ; 28 import java.sql.PreparedStatement ; 29 import java.sql.ResultSet ; 30 import java.sql.SQLException ; 31 import java.util.HashMap ; 32 import java.util.Iterator ; 33 import java.util.LinkedList ; 34 import java.util.List ; 35 import java.util.Map ; 36 37 import org.apache.commons.logging.Log; 38 import org.apache.commons.logging.LogFactory; 39 40 import org.springframework.dao.DataAccessResourceFailureException; 41 import org.springframework.dao.InvalidDataAccessApiUsageException; 42 import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor; 43 import org.springframework.util.FileCopyUtils; 44 45 78 public class OracleLobHandler extends AbstractLobHandler { 79 80 private static final String BLOB_CLASS_NAME = "oracle.sql.BLOB"; 81 82 private static final String CLOB_CLASS_NAME = "oracle.sql.CLOB"; 83 84 private static final String DURATION_SESSION_FIELD_NAME = "DURATION_SESSION"; 85 86 private static final String MODE_READWRITE_FIELD_NAME = "MODE_READWRITE"; 87 88 89 protected final Log logger = LogFactory.getLog(getClass()); 90 91 private NativeJdbcExtractor nativeJdbcExtractor; 92 93 private Boolean cache = Boolean.TRUE; 94 95 private Class blobClass; 96 97 private Class clobClass; 98 99 private final Map durationSessionConstants = new HashMap (2); 100 101 private final Map modeReadWriteConstants = new HashMap (2); 102 103 104 120 public void setNativeJdbcExtractor(NativeJdbcExtractor nativeJdbcExtractor) { 121 this.nativeJdbcExtractor = nativeJdbcExtractor; 122 } 123 124 130 public void setCache(boolean cache) { 131 this.cache = new Boolean (cache); 132 } 133 134 135 146 protected synchronized void initOracleDriverClasses(Connection con) { 147 if (this.blobClass == null) { 148 try { 149 this.blobClass = con.getClass().getClassLoader().loadClass(BLOB_CLASS_NAME); 151 this.durationSessionConstants.put( 152 this.blobClass, new Integer (this.blobClass.getField(DURATION_SESSION_FIELD_NAME).getInt(null))); 153 this.modeReadWriteConstants.put( 154 this.blobClass, new Integer (this.blobClass.getField(MODE_READWRITE_FIELD_NAME).getInt(null))); 155 156 this.clobClass = con.getClass().getClassLoader().loadClass(CLOB_CLASS_NAME); 158 this.durationSessionConstants.put( 159 this.clobClass, new Integer (this.clobClass.getField(DURATION_SESSION_FIELD_NAME).getInt(null))); 160 this.modeReadWriteConstants.put( 161 this.clobClass, new Integer (this.clobClass.getField(MODE_READWRITE_FIELD_NAME).getInt(null))); 162 } 163 catch (Exception ex) { 164 throw new InvalidDataAccessApiUsageException( 165 "Couldn't initialize OracleLobHandler because Oracle driver classes are not available. " + 166 "Note that OracleLobHandler requires Oracle JDBC driver 9i or higher!", ex); 167 } 168 } 169 } 170 171 172 public byte[] getBlobAsBytes(ResultSet rs, int columnIndex) throws SQLException { 173 logger.debug("Returning Oracle BLOB as bytes"); 174 Blob blob = rs.getBlob(columnIndex); 175 return (blob != null ? blob.getBytes(1, (int) blob.length()) : null); 176 } 177 178 public InputStream getBlobAsBinaryStream(ResultSet rs, int columnIndex) throws SQLException { 179 logger.debug("Returning Oracle BLOB as binary stream"); 180 Blob blob = rs.getBlob(columnIndex); 181 return (blob != null ? blob.getBinaryStream() : null); 182 } 183 184 public String getClobAsString(ResultSet rs, int columnIndex) throws SQLException { 185 logger.debug("Returning Oracle CLOB as string"); 186 Clob clob = rs.getClob(columnIndex); 187 return (clob != null ? clob.getSubString(1, (int) clob.length()) : null); 188 } 189 190 public InputStream getClobAsAsciiStream(ResultSet rs, int columnIndex) throws SQLException { 191 logger.debug("Returning Oracle CLOB as ASCII stream"); 192 Clob clob = rs.getClob(columnIndex); 193 return (clob != null ? clob.getAsciiStream() : null); 194 } 195 196 public Reader getClobAsCharacterStream(ResultSet rs, int columnIndex) throws SQLException { 197 logger.debug("Returning Oracle CLOB as character stream"); 198 Clob clob = rs.getClob(columnIndex); 199 return (clob != null ? clob.getCharacterStream() : null); 200 } 201 202 public LobCreator getLobCreator() { 203 return new OracleLobCreator(); 204 } 205 206 207 212 protected class OracleLobCreator implements LobCreator { 213 214 private final List createdLobs = new LinkedList (); 215 216 public void setBlobAsBytes(PreparedStatement ps, int paramIndex, final byte[] content) 217 throws SQLException { 218 219 if (content != null) { 220 Blob blob = (Blob ) createLob(ps, false, new LobCallback() { 221 public void populateLob(Object lob) throws Exception { 222 Method methodToInvoke = lob.getClass().getMethod("getBinaryOutputStream", new Class [0]); 223 OutputStream out = (OutputStream ) methodToInvoke.invoke(lob, (Object []) null); 224 FileCopyUtils.copy(content, out); 225 } 226 }); 227 ps.setBlob(paramIndex, blob); 228 if (logger.isDebugEnabled()) { 229 logger.debug("Set bytes for Oracle BLOB with length " + blob.length()); 230 } 231 } 232 else { 233 ps.setBlob(paramIndex, null); 234 logger.debug("Set Oracle BLOB to null"); 235 } 236 } 237 238 public void setBlobAsBinaryStream( 239 PreparedStatement ps, int paramIndex, final InputStream binaryStream, int contentLength) 240 throws SQLException { 241 242 if (binaryStream != null) { 243 Blob blob = (Blob ) createLob(ps, false, new LobCallback() { 244 public void populateLob(Object lob) throws Exception { 245 Method methodToInvoke = lob.getClass().getMethod("getBinaryOutputStream", (Class []) null); 246 OutputStream out = (OutputStream ) methodToInvoke.invoke(lob, (Object []) null); 247 FileCopyUtils.copy(binaryStream, out); 248 } 249 }); 250 ps.setBlob(paramIndex, blob); 251 if (logger.isDebugEnabled()) { 252 logger.debug("Set binary stream for Oracle BLOB with length " + blob.length()); 253 } 254 } 255 else { 256 ps.setBlob(paramIndex, null); 257 logger.debug("Set Oracle BLOB to null"); 258 } 259 } 260 261 public void setClobAsString(PreparedStatement ps, int paramIndex, final String content) 262 throws SQLException { 263 264 if (content != null) { 265 Clob clob = (Clob ) createLob(ps, true, new LobCallback() { 266 public void populateLob(Object lob) throws Exception { 267 Method methodToInvoke = lob.getClass().getMethod("getCharacterOutputStream", (Class []) null); 268 Writer writer = (Writer ) methodToInvoke.invoke(lob, (Object []) null); 269 FileCopyUtils.copy(content, writer); 270 } 271 }); 272 ps.setClob(paramIndex, clob); 273 if (logger.isDebugEnabled()) { 274 logger.debug("Set string for Oracle CLOB with length " + clob.length()); 275 } 276 } 277 else { 278 ps.setClob(paramIndex, null); 279 logger.debug("Set Oracle CLOB to null"); 280 } 281 } 282 283 public void setClobAsAsciiStream( 284 PreparedStatement ps, int paramIndex, final InputStream asciiStream, int contentLength) 285 throws SQLException { 286 287 if (asciiStream != null) { 288 Clob clob = (Clob ) createLob(ps, true, new LobCallback() { 289 public void populateLob(Object lob) throws Exception { 290 Method methodToInvoke = lob.getClass().getMethod("getAsciiOutputStream", (Class []) null); 291 OutputStream out = (OutputStream ) methodToInvoke.invoke(lob, (Object []) null); 292 FileCopyUtils.copy(asciiStream, out); 293 } 294 }); 295 ps.setClob(paramIndex, clob); 296 if (logger.isDebugEnabled()) { 297 logger.debug("Set ASCII stream for Oracle CLOB with length " + clob.length()); 298 } 299 } 300 else { 301 ps.setClob(paramIndex, null); 302 logger.debug("Set Oracle CLOB to null"); 303 } 304 } 305 306 public void setClobAsCharacterStream( 307 PreparedStatement ps, int paramIndex, final Reader characterStream, int contentLength) 308 throws SQLException { 309 310 if (characterStream != null) { 311 Clob clob = (Clob ) createLob(ps, true, new LobCallback() { 312 public void populateLob(Object lob) throws Exception { 313 Method methodToInvoke = lob.getClass().getMethod("getCharacterOutputStream", (Class []) null); 314 Writer writer = (Writer ) methodToInvoke.invoke(lob, (Object []) null); 315 FileCopyUtils.copy(characterStream, writer); 316 } 317 }); 318 ps.setClob(paramIndex, clob); 319 if (logger.isDebugEnabled()) { 320 logger.debug("Set character stream for Oracle CLOB with length " + clob.length()); 321 } 322 } 323 else { 324 ps.setClob(paramIndex, null); 325 logger.debug("Set Oracle CLOB to null"); 326 } 327 } 328 329 333 protected Object createLob(PreparedStatement ps, boolean clob, LobCallback callback) 334 throws SQLException { 335 336 Connection con = null; 337 try { 338 con = getOracleConnection(ps); 339 initOracleDriverClasses(con); 340 Object lob = prepareLob(con, clob ? clobClass : blobClass); 341 callback.populateLob(lob); 342 lob.getClass().getMethod("close", (Class []) null).invoke(lob, (Object []) null); 343 this.createdLobs.add(lob); 344 if (logger.isDebugEnabled()) { 345 logger.debug("Created new Oracle " + (clob ? "CLOB" : "BLOB")); 346 } 347 return lob; 348 } 349 catch (SQLException ex) { 350 throw ex; 351 } 352 catch (InvocationTargetException ex) { 353 if (ex.getTargetException() instanceof SQLException ) { 354 throw (SQLException ) ex.getTargetException(); 355 } 356 else if (con != null && ex.getTargetException() instanceof ClassCastException ) { 357 throw new InvalidDataAccessApiUsageException( 358 "OracleLobCreator needs to work on [oracle.jdbc.OracleConnection], not on [" + 359 con.getClass().getName() + "]: specify a corresponding NativeJdbcExtractor", 360 ex.getTargetException()); 361 } 362 else { 363 throw new DataAccessResourceFailureException("Could not create Oracle LOB", 364 ex.getTargetException()); 365 } 366 } 367 catch (Exception ex) { 368 throw new DataAccessResourceFailureException("Could not create Oracle LOB", ex); 369 } 370 } 371 372 375 protected Connection getOracleConnection(PreparedStatement ps) 376 throws SQLException , ClassNotFoundException { 377 378 return (nativeJdbcExtractor != null) ? 379 nativeJdbcExtractor.getNativeConnectionFromStatement(ps) : ps.getConnection(); 380 } 381 382 385 protected Object prepareLob(Connection con, Class lobClass) throws Exception { 386 391 Method createTemporary = lobClass.getMethod( 392 "createTemporary", new Class [] {Connection .class, boolean.class, int.class}); 393 Object lob = createTemporary.invoke( 394 null, new Object [] {con, cache, durationSessionConstants.get(lobClass)}); 395 Method open = lobClass.getMethod("open", new Class [] {int.class}); 396 open.invoke(lob, new Object [] {modeReadWriteConstants.get(lobClass)}); 397 return lob; 398 } 399 400 403 public void close() { 404 try { 405 for (Iterator it = this.createdLobs.iterator(); it.hasNext();) { 406 410 Object lob = it.next(); 411 Method freeTemporary = lob.getClass().getMethod("freeTemporary", new Class [0]); 412 freeTemporary.invoke(lob, new Object [0]); 413 it.remove(); 414 } 415 } 416 catch (InvocationTargetException ex) { 417 logger.error("Could not free Oracle LOB", ex.getTargetException()); 418 } 419 catch (Exception ex) { 420 throw new DataAccessResourceFailureException("Could not free Oracle LOB", ex); 421 } 422 } 423 } 424 425 426 429 protected static interface LobCallback { 430 431 435 void populateLob(Object lob) throws Exception ; 436 } 437 438 } 439 | Popular Tags |