From ccded10d861bf947b0eb674101d168c8925d89a3 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 9 Oct 2010 23:04:50 +0000 Subject: [PATCH] added configurable Connection/Statement/ResultSet target types to Jdbc4NativeJdbcExtractor (SPR-7613); added OracleJdbc4NativeJdbcExtractor with pre-configured Oracle JDBC API types --- .../jdbc/support/lob/OracleLobHandler.java | 38 +++++----- .../nativejdbc/Jdbc4NativeJdbcExtractor.java | 71 ++++++++++++++++--- .../OracleJdbc4NativeJdbcExtractor.java | 60 ++++++++++++++++ org.springframework.jdbc/template.mf | 1 + 4 files changed, 139 insertions(+), 31 deletions(-) create mode 100644 org.springframework.jdbc/src/main/java/org/springframework/jdbc/support/nativejdbc/OracleJdbc4NativeJdbcExtractor.java diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/support/lob/OracleLobHandler.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/support/lob/OracleLobHandler.java index dd5af5cfc0..56b34a75db 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/support/lob/OracleLobHandler.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/support/lob/OracleLobHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. @@ -122,6 +122,7 @@ public class OracleLobHandler extends AbstractLobHandler { * See SimpleNativeJdbcExtractor's javadoc for details. * @see org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor#getNativeConnectionFromStatement * @see org.springframework.jdbc.support.nativejdbc.SimpleNativeJdbcExtractor + * @see org.springframework.jdbc.support.nativejdbc.OracleJdbc4NativeJdbcExtractor * @see oracle.jdbc.OracleConnection */ public void setNativeJdbcExtractor(NativeJdbcExtractor nativeJdbcExtractor) { @@ -144,20 +145,16 @@ public class OracleLobHandler extends AbstractLobHandler { * Set whether to agressively release any resources used by the LOB. If set to true * then you can only read the LOB values once. Any subsequent reads will fail since the resources * have been closed. - * *

Setting this property to true can be useful when your queries generates large * temporary LOBs that occupy space in the TEMPORARY tablespace or when you want to free up any * memory allocated by the driver for the LOB reading. - * *

Default is false. - * * @see oracle.sql.BLOB#freeTemporary * @see oracle.sql.CLOB#freeTemporary * @see oracle.sql.BLOB#open * @see oracle.sql.CLOB#open * @see oracle.sql.BLOB#close * @see oracle.sql.CLOB#close - * @since 3.0 */ public void setReleaseResourcesAfterRead(boolean releaseResources) { this.releaseResourcesAfterRead = releaseResources; @@ -258,27 +255,25 @@ public class OracleLobHandler extends AbstractLobHandler { /** * Initialize any LOB resources before a read is done. - * - *

This implementation calls - * BLOB.open(BLOB.MODE_READONLY) or CLOB.open(CLOB.MODE_READONLY) - * on any non-temporary LOBs - * if releaseResourcesAfterRead property is set to true. + *

This implementation calls BLOB.open(BLOB.MODE_READONLY) or + * CLOB.open(CLOB.MODE_READONLY) on any non-temporary LOBs if + * releaseResourcesAfterRead property is set to true. *

This method can be overridden by sublcasses if different behavior is desired. * @param con the connection to be usde for initilization * @param lob the LOB to initialize */ protected void initializeResourcesBeforeRead(Connection con, Object lob) { - if (releaseResourcesAfterRead) { + if (this.releaseResourcesAfterRead) { initOracleDriverClasses(con); try { /* - if (!((BLOB)lob.isTemporary() { + if (!((BLOB) lob.isTemporary() { */ Method isTemporary = lob.getClass().getMethod("isTemporary"); Boolean temporary = (Boolean) isTemporary.invoke(lob); if (!temporary) { /* - ((BLOB)lob).open(BLOB.MODE_READONLY); + ((BLOB) lob).open(BLOB.MODE_READONLY); */ Method open = lob.getClass().getMethod("open", int.class); open.invoke(lob, modeReadOnlyConstants.get(lob.getClass())); @@ -295,44 +290,42 @@ public class OracleLobHandler extends AbstractLobHandler { /** * Release any LOB resources after read is complete. - * *

If releaseResourcesAfterRead property is set to true * then this implementation calls * BLOB.close() or CLOB.close() * on any non-temporary LOBs that are open or * BLOB.freeTemporary() or CLOB.freeTemporary() * on any temporary LOBs. - * *

This method can be overridden by sublcasses if different behavior is desired. * @param con the connection to be usde for initilization * @param lob the LOB to initialize */ protected void releaseResourcesAfterRead(Connection con, Object lob) { - if (releaseResourcesAfterRead) { + if (this.releaseResourcesAfterRead) { initOracleDriverClasses(con); Boolean temporary = Boolean.FALSE; try { /* - if (((BLOB)lob.isTemporary() { + if (((BLOB) lob.isTemporary() { */ Method isTemporary = lob.getClass().getMethod("isTemporary"); temporary = (Boolean) isTemporary.invoke(lob); if (temporary) { /* - ((BLOB)lob).freeTemporary(); + ((BLOB) lob).freeTemporary(); */ Method freeTemporary = lob.getClass().getMethod("freeTemporary"); freeTemporary.invoke(lob); } else { /* - if (((BLOB)lob.isOpen() { + if (((BLOB) lob.isOpen() { */ Method isOpen = lob.getClass().getMethod("isOpen"); Boolean open = (Boolean) isOpen.invoke(lob); if (open) { /* - ((BLOB)lob).close(); + ((BLOB) lob).close(); */ Method close = lob.getClass().getMethod("close"); close.invoke(lob); @@ -358,6 +351,7 @@ public class OracleLobHandler extends AbstractLobHandler { } } + /** * LobCreator implementation for Oracle databases. * Creates Oracle-style temporary BLOBs and CLOBs that it frees on close. @@ -529,8 +523,8 @@ public class OracleLobHandler extends AbstractLobHandler { protected Connection getOracleConnection(PreparedStatement ps) throws SQLException, ClassNotFoundException { - return (nativeJdbcExtractor != null) ? - nativeJdbcExtractor.getNativeConnectionFromStatement(ps) : ps.getConnection(); + return (nativeJdbcExtractor != null ? + nativeJdbcExtractor.getNativeConnectionFromStatement(ps) : ps.getConnection()); } /** diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/support/nativejdbc/Jdbc4NativeJdbcExtractor.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/support/nativejdbc/Jdbc4NativeJdbcExtractor.java index 827e482a80..cc22956202 100644 --- a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/support/nativejdbc/Jdbc4NativeJdbcExtractor.java +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/support/nativejdbc/Jdbc4NativeJdbcExtractor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2010 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. @@ -26,10 +26,16 @@ import java.sql.Statement; /** * {@link NativeJdbcExtractor} implementation that delegates to JDBC 4.0's * unwrap method, as defined by {@link java.sql.Wrapper}. + * You will typically need to specify a vendor {@link #setConnectionType Connection type} + * / {@link #setStatementType Statement type} / {@link #setResultSetType ResultSet type} + * to extract, since JDBC 4.0 only actually unwraps to a given target type. * - *

Note: Only use this when actually running against a JDBC 4.0 driver, - * with a connection pool that supports the JDBC 4.0 API (i.e. at least accepts - * JDBC 4.0 API calls and passes them through to the underlying driver)! + *

Note: Only use this when actually running against a JDBC 4.0 driver, with a + * connection pool that supports the JDBC 4.0 API (i.e. at least accepts JDBC 4.0 + * API calls and passes them through to the underlying driver)! Other than that, + * there is no need for connection pool specific setup. As of JDBC 4.0, + * NativeJdbcExtractors will typically be implemented for specific drivers + * instead of for specific pools (e.g. {@link OracleJdbc4NativeJdbcExtractor}). * * @author Juergen Hoeller * @since 2.5 @@ -40,29 +46,76 @@ import java.sql.Statement; */ public class Jdbc4NativeJdbcExtractor extends NativeJdbcExtractorAdapter { + private Class connectionType = Connection.class; + + private Class statementType = Statement.class; + + private Class preparedStatementType = PreparedStatement.class; + + private Class callableStatementType = CallableStatement.class; + + private Class resultSetType = ResultSet.class; + + + /** + * Set the vendor's Connection type, e.g. oracle.jdbc.OracleConnection. + */ + public void setConnectionType(Class connectionType) { + this.connectionType = connectionType; + } + + /** + * Set the vendor's Statement type, e.g. oracle.jdbc.OracleStatement. + */ + public void setStatementType(Class statementType) { + this.statementType = statementType; + } + + /** + * Set the vendor's PreparedStatement type, e.g. oracle.jdbc.OraclePreparedStatement. + */ + public void setPreparedStatementType(Class preparedStatementType) { + this.preparedStatementType = preparedStatementType; + } + + /** + * Set the vendor's CallableStatement type, e.g. oracle.jdbc.OracleCallableStatement. + */ + public void setCallableStatementType(Class callableStatementType) { + this.callableStatementType = callableStatementType; + } + + /** + * Set the vendor's ResultSet type, e.g. oracle.jdbc.OracleResultSet. + */ + public void setResultSetType(Class resultSetType) { + this.resultSetType = resultSetType; + } + + @Override protected Connection doGetNativeConnection(Connection con) throws SQLException { - return (Connection) con.unwrap(Connection.class); + return con.unwrap(this.connectionType); } @Override public Statement getNativeStatement(Statement stmt) throws SQLException { - return (Statement) stmt.unwrap(Statement.class); + return stmt.unwrap(this.statementType); } @Override public PreparedStatement getNativePreparedStatement(PreparedStatement ps) throws SQLException { - return (PreparedStatement) ps.unwrap(PreparedStatement.class); + return ps.unwrap(this.preparedStatementType); } @Override public CallableStatement getNativeCallableStatement(CallableStatement cs) throws SQLException { - return (CallableStatement) cs.unwrap(CallableStatement.class); + return cs.unwrap(this.callableStatementType); } @Override public ResultSet getNativeResultSet(ResultSet rs) throws SQLException { - return (ResultSet) rs.unwrap(ResultSet.class); + return rs.unwrap(this.resultSetType); } } diff --git a/org.springframework.jdbc/src/main/java/org/springframework/jdbc/support/nativejdbc/OracleJdbc4NativeJdbcExtractor.java b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/support/nativejdbc/OracleJdbc4NativeJdbcExtractor.java new file mode 100644 index 0000000000..123e73c8dd --- /dev/null +++ b/org.springframework.jdbc/src/main/java/org/springframework/jdbc/support/nativejdbc/OracleJdbc4NativeJdbcExtractor.java @@ -0,0 +1,60 @@ +/* + * Copyright 2002-2010 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.jdbc.support.nativejdbc; + +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Statement; + +/** + * A {@link Jdbc4NativeJdbcExtractor} which comes pre-configured for Oracle's JDBC driver, + * specifying the following vendor-specific API types for unwrapping: + *

+ * + *

Note: This will work with any JDBC 4.0 compliant connection pool, without a need for + * connection pool specific setup. In other words, as of JDBC 4.0, NativeJdbcExtractors + * will typically be implemented for specific drivers instead of for specific pools. + * + * @author Juergen Hoeller + * @since 3.0.5 + */ +public class OracleJdbc4NativeJdbcExtractor extends Jdbc4NativeJdbcExtractor { + + @SuppressWarnings("unchecked") + public OracleJdbc4NativeJdbcExtractor() { + try { + setConnectionType((Class) getClass().getClassLoader().loadClass("oracle.jdbc.OracleConnection")); + setStatementType((Class) getClass().getClassLoader().loadClass("oracle.jdbc.OracleStatement")); + setPreparedStatementType((Class) getClass().getClassLoader().loadClass("oracle.jdbc.OraclePreparedStatement")); + setCallableStatementType((Class) getClass().getClassLoader().loadClass("oracle.jdbc.OracleCallableStatement")); + setResultSetType((Class) getClass().getClassLoader().loadClass("oracle.jdbc.OracleResultSet")); + } + catch (Exception ex) { + throw new IllegalStateException( + "Could not initialize OracleJdbc4NativeJdbcExtractor because Oracle API classes are not available: " + ex); + } + } + +} diff --git a/org.springframework.jdbc/template.mf b/org.springframework.jdbc/template.mf index 076b538e85..4357e0cd6d 100644 --- a/org.springframework.jdbc/template.mf +++ b/org.springframework.jdbc/template.mf @@ -5,6 +5,7 @@ Bundle-ManifestVersion: 2 Import-Package: com.ibm.websphere.rsadapter;version="0";resolution:=optional, com.ibm.ws.rsadapter.jdbc;version="0";resolution:=optional, + oracle.jdbc;version="0";resolution:=optional, oracle.sql;version="0";resolution:=optional, org.h2;version="[1.8.0, 2.0.0)";resolution:=optional, org.hsqldb;version="[1.0.0, 2.0.0)";resolution:=optional,