Author: arminw Date: Fri Dec 22 18:40:42 2006 New Revision: 489836 URL: http://svn.apache.org/viewvc?view=rev&rev=489836 Log: refactored version
Modified: db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/platforms/PlatformOracle9iImpl.java db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/platforms/PlatformOracleImpl.java Modified: db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/platforms/PlatformOracle9iImpl.java URL: http://svn.apache.org/viewvc/db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/platforms/PlatformOracle9iImpl.java?view=diff&rev=489836&r1=489835&r2=489836 ============================================================================== --- db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/platforms/PlatformOracle9iImpl.java (original) +++ db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/platforms/PlatformOracle9iImpl.java Fri Dec 22 18:40:42 2006 @@ -18,18 +18,20 @@ import java.io.ByteArrayInputStream; import java.lang.reflect.Method; import java.sql.Connection; -import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; import java.sql.Types; +import java.sql.PreparedStatement; import java.util.Collections; import java.util.Map; import java.util.WeakHashMap; +import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.ojb.broker.metadata.ConnectionPoolDescriptor; import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor; import org.apache.ojb.broker.util.ClassHelper; import org.apache.ojb.broker.util.UnwrapHelper; +import org.apache.ojb.broker.util.ConvertHelper; import org.apache.ojb.broker.util.logging.Logger; import org.apache.ojb.broker.util.logging.LoggerFactory; @@ -56,9 +58,6 @@ * TODO: Optimization: use ROWNUM to minimize the effects of not having server side cursors * see http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:127412348064 * - * @author <a href="mailto:[EMAIL PROTECTED]">Matthew Baird</a> - * @author <a href="mailto:[EMAIL PROTECTED]">Martin Kalén</a> - * @author Contributions from: Erik Forkalsrud, Danilo Tommasina, Thierry Hanot, Don Lyon * @version CVS $Id$ * @see Platform * @see PlatformDefaultImpl @@ -66,8 +65,54 @@ */ public class PlatformOracle9iImpl extends PlatformOracleImpl { - private Logger logger = LoggerFactory.getLogger(PlatformOracle9iImpl.class); + private static Logger log = LoggerFactory.getLogger(PlatformOracle9iImpl.class); + + /** + * Platform attribute of Oracle's native statement cache pool. + */ + protected static final String ATTRIBUTE_STATEMENT_CACHE_SIZE = "platform.oracle.statementCacheSize"; + /** + * Platform attribute of Oracle's native prefetch handling. + */ + protected static final String ATTRIBUTE_PREFETCH_SIZE = "platform.oracle.prefetchSize"; + /** + * Platform attribute to enable Oracle's native batch handling. + */ + protected static final String ATTRIBUTE_BATCH_SIZE = "platform.oracle.batchSize"; + + protected static final Class[] PARAM_TYPE_EMPTY = {}; + protected static final Class[] PARAM_TYPE_INTEGER = {Integer.TYPE}; + protected static final Class[] PARAM_TYPE_BOOLEAN = {Boolean.TYPE}; + protected static final Class[] PARAM_TYPE_STRING = {String.class}; + protected static final Object[] PARAM_EMPTY = new Object[]{}; + protected static final Object[] PARAM_BOOLEAN_TRUE = new Object[]{Boolean.TRUE}; + + protected static String JBOSS_CONN_NAME = "org.jboss.resource.adapter.jdbc.WrappedConnection"; + protected static String ORA_CONNECTION_CLASS_NAME = "oracle.jdbc.OracleConnection"; + protected static String ORA_STATEMENT_CLASS_NAME = "oracle.jdbc.OraclePreparedStatement"; + + protected Class JBOSS_CONN_CLASS; + protected Class ORA_CONN_CLASS; + protected Class ORA_PS_CLASS; + protected Class ORA_CLOB_CLASS; + protected Class ORA_BLOB_CLASS; + protected Class[] PARAM_TYPE_INT_ORACLOB; + protected Class[] PARAM_TYPE_INT_ORABLOB; + protected Method METHOD_SET_STATEMENT_CACHE_SIZE; + protected Method METHOD_SET_IMPLICIT_CACHING_ENABLED; + protected Method METHOD_SET_ROW_PREFETCH; + protected Method METHOD_SET_BLOB; + protected Method METHOD_SET_CLOB; + protected boolean oraStatementCachingAvailable; + protected boolean oraRowPrefetchAvailable; + protected boolean oraLobHandlingAvailable; + protected boolean oraBatchAvailable; + protected static Map m_batchStatementsInProgress = Collections.synchronizedMap(new WeakHashMap()); + /** + * Size of Oracle's native batch handling size. + */ + protected Object[] paramStatementBatchSize; /** * Number of cached statements per connection, * when using implicit caching with OracleConnections. @@ -75,7 +120,7 @@ * @see <a href="http://www.apache.org/~mkalen/ojb/broker-tests.html">Profiling page</a> * for a discussion re sizing */ - protected static final int STATEMENT_CACHE_SIZE = 10; + protected Object[] paramStatementCacheSize; /** * Number of rows pre-fetched by the JDBC-driver for each executed query, * when using Oracle row pre-fetching with OracleConnections. @@ -85,43 +130,7 @@ * connection-pool attribute with name="jdbc.defaultRowPrefetch". * Oracle JDBC-driver default value=10. */ - protected static final int ROW_PREFETCH_SIZE = 20; - - // From Oracle9i JDBC Developer's Guide and Reference: - // "Batch values between 5 and 30 tend to be the most effective." - protected static final int STATEMENTS_PER_BATCH = 20; - protected static Map m_batchStatementsInProgress = Collections.synchronizedMap(new WeakHashMap(STATEMENTS_PER_BATCH)); - - protected static final Class[] PARAM_TYPE_EMPTY = {}; - protected static final Class[] PARAM_TYPE_INTEGER = {Integer.TYPE}; - protected static final Class[] PARAM_TYPE_BOOLEAN = {Boolean.TYPE}; - protected static final Class[] PARAM_TYPE_STRING = {String.class}; - - protected static final Object[] PARAM_EMPTY = new Object[]{}; - protected static final Object[] PARAM_STATEMENT_CACHE_SIZE = new Object[]{new Integer(STATEMENT_CACHE_SIZE)}; - protected static final Object[] PARAM_ROW_PREFETCH_SIZE = new Object[]{new Integer(ROW_PREFETCH_SIZE)}; - protected static final Object[] PARAM_STATEMENT_BATCH_SIZE = new Object[]{new Integer(STATEMENTS_PER_BATCH)}; - protected static final Object[] PARAM_BOOLEAN_TRUE = new Object[]{Boolean.TRUE}; - - protected static final String JBOSS_CONN_NAME = - "org.jboss.resource.adapter.jdbc.WrappedConnection"; - protected static Class JBOSS_CONN_CLASS = null; - - protected static Class ORA_CONN_CLASS; - protected static Class ORA_PS_CLASS; - protected static Class ORA_CLOB_CLASS; - protected static Class ORA_BLOB_CLASS; - protected static Class[] PARAM_TYPE_INT_ORACLOB; - protected static Class[] PARAM_TYPE_INT_ORABLOB; - protected static Method METHOD_SET_STATEMENT_CACHE_SIZE; - protected static Method METHOD_SET_IMPLICIT_CACHING_ENABLED; - protected static Method METHOD_SET_ROW_PREFETCH; - protected static Method METHOD_SET_BLOB = null; - protected static Method METHOD_SET_CLOB = null; - protected static boolean ORA_STATEMENT_CACHING_AVAILABLE; - protected static boolean ORA_ROW_PREFETCH_AVAILABLE; - protected static boolean ORA_CLOB_HANDLING_AVAILABLE; - protected static boolean ORA_BLOB_HANDLING_AVAILABLE; + protected Object[] paramRowPrefetchSize; /** * Helper to unwrap connections and statements. @@ -129,97 +138,251 @@ protected UnwrapHelper unwrapHelper; - /** - * Default constructor. - */ - public PlatformOracle9iImpl() + public PlatformOracle9iImpl(JdbcConnectionDescriptor jcd) { - super(); + super(jcd); + initOracleReflectedVars(); } /** - * Enables Oracle statement caching and row prefetching if supported by the JDBC-driver. - * @param jcd the OJB <code>JdbcConnectionDescriptor</code> (metadata) for the connection to be initialized - * @param conn the <code>Connection</code>-object (physical) to be initialized - * @see PlatformDefaultImpl#initializeJdbcConnection - * @see <a href="http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/jdbc30/StmtCacheSample/Readme.html"> - * Oracle TechNet Statement Caching Sample</a> - * @see <a href="http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/advanced/RowPrefetchSample/Readme.html"> - * Oracle TechNet Row Pre-fetch Sample<a> + * Initializes static variables needed for Oracle-extensions and large BLOB/CLOB support, + * attributes set in [EMAIL PROTECTED] org.apache.ojb.broker.metadata.JdbcConnectionDescriptor}. */ - public void initializeJdbcConnection(final JdbcConnectionDescriptor jcd, - final Connection conn) - throws PlatformException + private void initOracleReflectedVars() { - // Do all the generic initialization in PlatformDefaultImpl first - super.initializeJdbcConnection(jcd, conn); + unwrapHelper = new UnwrapHelper(); + try + { + /* + Check for Oracle-specific classes, OracleConnection-specific + statement caching/row pre-fetch methods and Oracle BLOB/CLOB access methods. + We can do this in constructor in spite of possible mixing of instance being + able vs unable passed at runtime (since withouth these classes and methods + it's impossible to enable ORA-extensions at all even if instances are capable). + */ + ORA_CONN_CLASS = ClassHelper.getClass(ORA_CONNECTION_CLASS_NAME, false); + ORA_PS_CLASS = ClassHelper.getClass(ORA_STATEMENT_CLASS_NAME, false); + /* + The unwrap pattern used in [EMAIL PROTECTED] org.apache.ojb.broker.util.UnwrapHelper} + to unwrap connection instance to Oracle's specific connection implementation class. + */ + Object[] oracleUnwrapPattern = new Object[] {"oracle 10g", UnwrapHelper.TYPE_METHOD, + new Class[]{ORA_CONN_CLASS}, "unwrapCompletely", null, null, null}; + // add the oracle unwrap pattern + unwrapHelper.addUnwrapPattern(oracleUnwrapPattern); + } + catch(ClassNotFoundException e) + { + log.info("Can't access Oracle specific driver Connection/Statement classes", e); + } + - // Check for managed environments known to reject Oracle extension at this level - // (saves us from trying to unwrap just to catch exceptions next) - final Class connClass = conn.getClass(); - if (JBOSS_CONN_CLASS != null && JBOSS_CONN_CLASS.isAssignableFrom(connClass)) + try { - if (logger.isDebugEnabled()) - { - logger.debug("JBoss detected, Oracle Connection tuning left to J2EE container."); - } - return; + ORA_CLOB_CLASS = ClassHelper.getClass("oracle.sql.CLOB", false); + ORA_BLOB_CLASS = ClassHelper.getClass("oracle.sql.BLOB", false); + PARAM_TYPE_INT_ORACLOB = new Class[]{ Integer.TYPE, ORA_CLOB_CLASS }; + PARAM_TYPE_INT_ORABLOB = new Class[]{ Integer.TYPE, ORA_BLOB_CLASS }; + METHOD_SET_CLOB = ClassHelper.getMethod(ORA_PS_CLASS, "setCLOB", PARAM_TYPE_INT_ORACLOB); + METHOD_SET_BLOB = ClassHelper.getMethod(ORA_PS_CLASS, "setBLOB", PARAM_TYPE_INT_ORABLOB); + } + catch(ClassNotFoundException e) + { + log.info("Can't use Oracle specific BLOB/CLOB classes", e); } - // Check if this is a wrapped connection and if so unwrap it - final Connection oraConn = unwrapConnection(conn); - if (oraConn == null) + try { - return; + METHOD_SET_STATEMENT_CACHE_SIZE = + ClassHelper.getMethod(ORA_CONN_CLASS, "setStatementCacheSize", PARAM_TYPE_INTEGER); + METHOD_SET_IMPLICIT_CACHING_ENABLED = + ClassHelper.getMethod(ORA_CONN_CLASS, "setImplicitCachingEnabled", PARAM_TYPE_BOOLEAN); } + catch(Exception e) + { + log.info("Can't enable Oracle specific implicit statement caching", e); + } + - // At this point we know that we have an OracleConnection instance and can thus - // try to invoke methods via reflection (if available) - if (ORA_STATEMENT_CACHING_AVAILABLE) + try { - try + METHOD_SET_ROW_PREFETCH = ClassHelper.getMethod(ORA_CONN_CLASS, "setDefaultRowPrefetch", PARAM_TYPE_INTEGER); + } + catch(Exception e) + { + log.info("Can't use Oracle specific row prefetch settings", e); + } + + oraStatementCachingAvailable = METHOD_SET_STATEMENT_CACHE_SIZE != null && METHOD_SET_IMPLICIT_CACHING_ENABLED != null; + oraRowPrefetchAvailable = METHOD_SET_ROW_PREFETCH != null; + oraLobHandlingAvailable = METHOD_SET_CLOB != null && METHOD_SET_BLOB != null; + + if(oraStatementCachingAvailable) + { + String stmtCacheSize = jcd.getAttribute(ATTRIBUTE_STATEMENT_CACHE_SIZE); + boolean enabled = false; + if(stmtCacheSize != null && stmtCacheSize.length() > 0) { - // Set number of cached statements and enable implicit caching - METHOD_SET_STATEMENT_CACHE_SIZE.invoke(oraConn, PARAM_STATEMENT_CACHE_SIZE); - METHOD_SET_IMPLICIT_CACHING_ENABLED.invoke(oraConn, PARAM_BOOLEAN_TRUE); + try + { + Integer size = ConvertHelper.toInteger(stmtCacheSize); + if(size.intValue() > 0) + { + paramStatementCacheSize = new Object[]{size}; + enabled = true; + } + } + catch(NumberFormatException e) + { + log.error("Can't enable Oracle's statement caching, illegal value for attribute: " + + ATTRIBUTE_STATEMENT_CACHE_SIZE + "=" + stmtCacheSize); + } } - catch (Exception e) + if(!enabled) + { + oraStatementCachingAvailable = false; + log.info("Oracle's statement caching not used: "+ ATTRIBUTE_STATEMENT_CACHE_SIZE + "=" + stmtCacheSize); + } + else + { + log.info("Prepared to use Oracle's statement caching: "+ ATTRIBUTE_STATEMENT_CACHE_SIZE + "=" + stmtCacheSize); + } + } + + if(oraRowPrefetchAvailable) + { + String prefetchSize = jcd.getAttribute(ATTRIBUTE_PREFETCH_SIZE); + boolean enabled = false; + if(prefetchSize != null && prefetchSize.length() > 0) { - if (logger.isDebugEnabled()) + try + { + Integer size = ConvertHelper.toInteger(prefetchSize); + if(size.intValue() > 0) + { + paramRowPrefetchSize = new Object[]{size}; + enabled = true; + } + } + catch(NumberFormatException e) { - logger.debug("PlatformOracle9iImpl could not enable Oracle statement caching." - + " Original/unwrapped connection classes=" - + connClass.getName() + "/" + oraConn.getClass().getName()); + log.error("Can't enable Oracle's row prefetching, illegal value for attribute: " + + ATTRIBUTE_PREFETCH_SIZE + "=" + prefetchSize); } } + if(!enabled) + { + oraRowPrefetchAvailable = false; + log.info("Oracle's row prefetching not used: " + ATTRIBUTE_PREFETCH_SIZE + "=" + prefetchSize); + } + else + { + log.info("Prepared to use Oracle's row prefetching: " + ATTRIBUTE_PREFETCH_SIZE + "=" + prefetchSize); + } } - /* - mkalen: Note from the Oracle documentation: - Do not mix the JDBC 2.0 fetch size API and the Oracle row prefetching API - in your application. You can use one or the other, but not both. - */ - final ConnectionPoolDescriptor cpd = jcd.getConnectionPoolDescriptor(); - final int cpdFetchSizeHint = cpd.getFetchSize(); - if (cpdFetchSizeHint == 0 && ORA_ROW_PREFETCH_AVAILABLE) + String batchSize = jcd.getAttribute(ATTRIBUTE_BATCH_SIZE); + boolean enabled = false; + if(batchSize != null && batchSize.length() > 0) { try { - final String prefetchFromJcd; - prefetchFromJcd = cpd.getJdbcProperties().getProperty("defaultRowPrefetch"); - if (prefetchFromJcd == null) + Integer size = ConvertHelper.toInteger(batchSize); + if(size.intValue() > 0) { - METHOD_SET_ROW_PREFETCH.invoke(oraConn, PARAM_ROW_PREFETCH_SIZE); + paramStatementBatchSize = new Object[]{size}; + enabled = true; } - // Else, number of prefetched rows were set via Properties on Connection } - catch (Exception e) + catch(NumberFormatException e) { - if (logger.isDebugEnabled()) + log.error("Can't enable Oracle's native batching, illegal value for attribute: " + + ATTRIBUTE_BATCH_SIZE + "=" + batchSize); + } + } + if(!enabled) + { + oraBatchAvailable = false; + log.info("Oracle's native batching not used: " + ATTRIBUTE_BATCH_SIZE + "=" + batchSize); + } + else + { + log.info("Prepared to use Oracle's native batching: " + ATTRIBUTE_BATCH_SIZE + "=" + batchSize); + } + } + + /** + * Enables Oracle statement caching and row prefetching if supported by the JDBC-driver. + * @param conn the <code>Connection</code>-object (physical) to be initialized + * @see PlatformDefaultImpl#initializeJdbcConnection + * @see <a href="http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/jdbc30/StmtCacheSample/Readme.html"> + * Oracle TechNet Statement Caching Sample</a> + * @see <a href="http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/advanced/RowPrefetchSample/Readme.html"> + * Oracle TechNet Row Pre-fetch Sample<a> + */ + public void initializeJdbcConnection(final Connection conn) throws PlatformException + { + // Do all the generic initialization in PlatformDefaultImpl first + super.initializeJdbcConnection(conn); + + Connection oraConn = null; + if(oraStatementCachingAvailable || oraRowPrefetchAvailable) + { + oraConn = unwrapConnection(conn); + } + if(oraConn != null) + { + final Class connClass = conn.getClass(); + if(oraRowPrefetchAvailable) + { + /* + mkalen: Note from the Oracle documentation: + Do not mix the JDBC 2.0 fetch size API and the Oracle row prefetching API + in your application. You can use one or the other, but not both. + */ + final ConnectionPoolDescriptor cpd = jcd.getConnectionPoolDescriptor(); + if (cpd.getFetchSize() == 0) { - logger.debug("PlatformOracle9iImpl could not enable Oracle row pre-fetching." - + "Original/unwrapped connection classes=" - + connClass.getName() + "/" + oraConn.getClass().getName()); + try + { + final String prefetchFromJcd = cpd.getJdbcProperties().getProperty("defaultRowPrefetch"); + if (prefetchFromJcd == null) + { + METHOD_SET_ROW_PREFETCH.invoke(oraConn, paramRowPrefetchSize); + if(log.isDebugEnabled()) log.debug("Oracle's native row prefetching enabled for connection " + conn); + } + // Else, number of prefetched rows were set via Properties on Connection + } + catch (Exception e) + { + if (log.isDebugEnabled()) + { + log.debug("PlatformOracle9iImpl could not enable Oracle row pre-fetching." + + "Original/unwrapped connection classes=" + + connClass.getName() + "/" + oraConn.getClass().getName(), e); + } + } + } + } + if(oraStatementCachingAvailable) + { + // At this point we know that we have an OracleConnection instance and can thus + // try to invoke methods via reflection (if available) + try + { + // Set number of cached statements and enable implicit caching + METHOD_SET_STATEMENT_CACHE_SIZE.invoke(oraConn, paramStatementCacheSize); + METHOD_SET_IMPLICIT_CACHING_ENABLED.invoke(oraConn, PARAM_BOOLEAN_TRUE); + if(log.isDebugEnabled()) log.debug("Oracle's native implicit statement caching enabled for connection " + conn); + } + catch (Exception e) + { + if (log.isDebugEnabled()) + { + log.debug("PlatformOracle9iImpl could not enable Oracle statement caching." + + " Original/unwrapped connection classes=" + + connClass.getName() + "/" + oraConn.getClass().getName()); + } } } } @@ -246,24 +409,29 @@ */ public void beforeBatch(PreparedStatement stmt) throws PlatformException { - // Check for Oracle batching support - final Method methodSetExecuteBatch; - final Method methodSendBatch; - methodSetExecuteBatch = ClassHelper.getMethod(stmt, "setExecuteBatch", PARAM_TYPE_INTEGER); - methodSendBatch = ClassHelper.getMethod(stmt, "sendBatch", null); - - final boolean statementBatchingSupported = methodSetExecuteBatch != null && methodSendBatch != null; - if (statementBatchingSupported) + if (oraBatchAvailable) { - try + // Check for Oracle batching support + final Method methodSetExecuteBatch; + final Method methodSendBatch; + methodSetExecuteBatch = ClassHelper.getMethod(stmt, "setExecuteBatch", PARAM_TYPE_INTEGER); + methodSendBatch = ClassHelper.getMethod(stmt, "sendBatch", null); + if(methodSetExecuteBatch != null && methodSendBatch != null) { - // Set number of statements per batch - methodSetExecuteBatch.invoke(stmt, PARAM_STATEMENT_BATCH_SIZE); - m_batchStatementsInProgress.put(stmt, methodSendBatch); + try + { + // Set number of statements per batch + methodSetExecuteBatch.invoke(stmt, paramStatementBatchSize); + m_batchStatementsInProgress.put(stmt, methodSendBatch); + } + catch (Exception e) + { + throw new PlatformException(e.getLocalizedMessage(), e); + } } - catch (Exception e) + else { - throw new PlatformException(e.getLocalizedMessage(), e); + super.beforeBatch(stmt); } } else @@ -281,8 +449,7 @@ public void addBatch(PreparedStatement stmt) throws PlatformException { // Check for Oracle batching support - final boolean statementBatchingSupported = m_batchStatementsInProgress.containsKey(stmt); - if (statementBatchingSupported) + if (m_batchStatementsInProgress.containsKey(stmt)) { try { @@ -325,6 +492,8 @@ } catch (Exception e) { + log.error("Error when invoke batch statement", e); + e = (Exception) ExceptionUtils.getRootCause(e); throw new PlatformException(e.getLocalizedMessage(), e); } } @@ -346,9 +515,7 @@ { oraStmt = unwrapStatement(ps); oraConn = unwrapConnection(ps.getConnection()); - oraLargeLobSupportAvailable = - oraStmt != null && oraConn != null && - (sqlType == Types.CLOB ? ORA_CLOB_HANDLING_AVAILABLE : ORA_BLOB_HANDLING_AVAILABLE); + oraLargeLobSupportAvailable = oraStmt != null && oraConn != null && oraLobHandlingAvailable; } else { @@ -365,20 +532,6 @@ super.changePreparedStatementResultSetType(ps); ps.setBinaryStream(index, inputStream, buf.length); } - else if (value instanceof Double) - { - // workaround for the bug in Oracle thin driver - ps.setDouble(index, ((Double) value).doubleValue()); - } - else if (sqlType == Types.BIGINT && value instanceof Integer) - { - // workaround: Oracle thin driver problem when expecting long - ps.setLong(index, ((Integer) value).intValue()); - } - else if (sqlType == Types.INTEGER && value instanceof Long) - { - ps.setLong(index, ((Long) value).longValue()); - } else if (sqlType == Types.CLOB && oraLargeLobSupportAvailable && value instanceof String) { // TODO: If using Oracle update batching with the thin driver, throw exception on 4k limit @@ -427,20 +580,24 @@ * @param conn the connection to unwrap (if needed) * @return OracleConnection or null if not able to unwrap */ - protected Connection unwrapConnection(Connection conn) + protected Connection unwrapConnection(final Connection conn) { - final Connection unwrapped = unwrapHelper.unwrapConnection(ORA_CONN_CLASS, conn); - if (unwrapped == null) + Connection result = conn; + if(conn != null) { - // mkalen: only log this as debug since it will be logged for every connection - // (ie only useful during development). - if (logger.isDebugEnabled()) + result = unwrapHelper.unwrapConnection(ORA_CONN_CLASS, conn); + if (result == null) { - logger.debug("PlatformOracle9iImpl could not unwrap " + conn.getClass().getName() + - ", Oracle-extensions disabled."); + // mkalen: only log this as debug since it will be logged for every connection + // (ie only useful during development). + if (log.isDebugEnabled()) + { + log.debug("PlatformOracle9iImpl could not unwrap connection: " + conn.getClass() + + ", can't use Oracle's native Connection extensions."); + } } } - return unwrapped; + return result; } /** @@ -448,81 +605,23 @@ * @param ps the PreparedStatement to unwrap (if needed) * @return OraclePreparedStatement or null if not able to unwrap */ - protected Statement unwrapStatement(Statement ps) + protected Statement unwrapStatement(final Statement ps) { - final Statement unwrapped = unwrapHelper.unwrapStatement(ORA_PS_CLASS, ps); - if (unwrapped == null) + Statement result = ps; + if(ps != null) { - // mkalen: only log this as debug since it will be logged for every connection - // (ie only useful during development). - if (logger.isDebugEnabled()) + result = unwrapHelper.unwrapStatement(ORA_PS_CLASS, ps); + if (result == null) { - logger.debug("PlatformOracle9iImpl could not unwrap " + ps.getClass().getName() + - ", large CLOB/BLOB support disabled."); + // mkalen: only log this as debug since it will be logged for every connection + // (ie only useful during development). + if (log.isDebugEnabled()) + { + log.debug("PlatformOracle9iImpl could not unwrap statement: " + ps.getClass() + + ", can't use Oracle's native Statement extensions."); + } } } - return unwrapped; + return result; } - - /** - * Initializes static variables needed for Oracle-extensions and large BLOB/CLOB support. - */ - protected void initOracleReflectedVars() - { - super.initOracleReflectedVars(); - try - { - /* - Check for Oracle-specific classes, OracleConnection-specific - statement caching/row pre-fetch methods and Oracle BLOB/CLOB access methods. - We can do this in constructor in spite of possible mixing of instance being - able vs unable passed at runtime (since withouth these classes and methods - it's impossible to enable ORA-extensions at all even if instances are capable). - */ - ORA_CONN_CLASS = ClassHelper.getClass("oracle.jdbc.OracleConnection", false); - ORA_PS_CLASS = ClassHelper.getClass("oracle.jdbc.OraclePreparedStatement", false); - ORA_CLOB_CLASS = ClassHelper.getClass("oracle.sql.CLOB", false); - ORA_BLOB_CLASS = ClassHelper.getClass("oracle.sql.BLOB", false); - PARAM_TYPE_INT_ORACLOB = new Class[]{ Integer.TYPE, ORA_CLOB_CLASS }; - PARAM_TYPE_INT_ORABLOB = new Class[]{ Integer.TYPE, ORA_BLOB_CLASS }; - - /* - The unwrap pattern used in [EMAIL PROTECTED] org.apache.ojb.broker.util.UnwrapHelper} - to unwrap connection instance to Oracle's specific connection implementation class. - */ - Object[] oracleUnwrapPattern = new Object[] {"oracle 10g", UnwrapHelper.TYPE_METHOD, - new Class[]{ORA_CONN_CLASS}, "unwrapCompletely", null, null, null}; - unwrapHelper = new UnwrapHelper(); - // add the oracle unwrap pattern - unwrapHelper.addUnwrapPattern(oracleUnwrapPattern); - - METHOD_SET_STATEMENT_CACHE_SIZE = - ClassHelper.getMethod(ORA_CONN_CLASS, "setStatementCacheSize", PARAM_TYPE_INTEGER); - METHOD_SET_IMPLICIT_CACHING_ENABLED = - ClassHelper.getMethod(ORA_CONN_CLASS, "setImplicitCachingEnabled", PARAM_TYPE_BOOLEAN); - METHOD_SET_ROW_PREFETCH = ClassHelper.getMethod(ORA_CONN_CLASS, "setDefaultRowPrefetch", PARAM_TYPE_INTEGER); - METHOD_SET_CLOB = ClassHelper.getMethod(ORA_PS_CLASS, "setCLOB", PARAM_TYPE_INT_ORACLOB); - METHOD_SET_BLOB = ClassHelper.getMethod(ORA_PS_CLASS, "setBLOB", PARAM_TYPE_INT_ORABLOB); - - ORA_STATEMENT_CACHING_AVAILABLE = - METHOD_SET_STATEMENT_CACHE_SIZE != null && METHOD_SET_IMPLICIT_CACHING_ENABLED != null; - ORA_ROW_PREFETCH_AVAILABLE = METHOD_SET_ROW_PREFETCH != null; - ORA_CLOB_HANDLING_AVAILABLE = METHOD_SET_CLOB != null; - ORA_BLOB_HANDLING_AVAILABLE = METHOD_SET_BLOB != null; - } - catch (ClassNotFoundException e) - { - // ignore (we tried...) - } - // Isolated checks for other connection classes (OK when not found) - try - { - JBOSS_CONN_CLASS = ClassHelper.getClass(JBOSS_CONN_NAME, false); - } - catch (ClassNotFoundException e) - { - // ignore (no problem) - } - } - } Modified: db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/platforms/PlatformOracleImpl.java URL: http://svn.apache.org/viewvc/db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/platforms/PlatformOracleImpl.java?view=diff&rev=489836&r1=489835&r2=489836 ============================================================================== --- db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/platforms/PlatformOracleImpl.java (original) +++ db/ojb/branches/OJB_1_0_RELEASE/src/java/org/apache/ojb/broker/platforms/PlatformOracleImpl.java Fri Dec 22 18:40:42 2006 @@ -19,11 +19,16 @@ import org.apache.ojb.broker.util.logging.LoggerFactory; import org.apache.ojb.broker.util.ClassHelper; import org.apache.ojb.broker.util.sequence.SequenceManagerHelper; +import org.apache.ojb.broker.metadata.FieldDescriptor; +import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor; +import org.apache.ojb.broker.PersistenceBrokerSQLException; +import org.apache.commons.lang.SerializationUtils; import java.io.ByteArrayInputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; +import java.io.Serializable; import java.lang.reflect.Field; import java.security.AccessController; import java.security.PrivilegedAction; @@ -34,6 +39,7 @@ import java.sql.SQLException; import java.sql.Statement; import java.sql.Types; +import java.sql.Blob; import java.util.Properties; /** @@ -128,36 +134,55 @@ * </tr> * </table> * - * @author <a href="mailto:[EMAIL PROTECTED]">Thomas Mahler <a> * @version $Id$ */ public class PlatformOracleImpl extends PlatformDefaultImpl { protected static final String THIN_URL_PREFIX = "jdbc:oracle:thin"; - // Oracle:thin handles direct BLOB insert <= 4000 and update <= 2000 - protected static final int THIN_BLOB_MAX_SIZE = 2000; - // Oracle:thin handles direct CLOB insert and update <= 4000 - protected static final int THIN_CLOB_MAX_SIZE = 4000; /** * Field value of <code>oracle.jdbc.OracleTypes.CURSOR</code>. * @see #initOracleReflectedVars */ - protected static int ORACLE_JDBC_TYPE_CURSOR = -10; + protected int ORACLE_JDBC_TYPE_CURSOR = -10; private Logger logger = LoggerFactory.getLogger(PlatformOracleImpl.class); + public PlatformOracleImpl(JdbcConnectionDescriptor jcd) + { + super(jcd); + initOracleReflectedVars(); + } + /** - * Default constructor. + * Initializes static variables needed for getting Oracle-specific JDBC types. */ - public PlatformOracleImpl() + private void initOracleReflectedVars() { - initOracleReflectedVars(); + try + { + // Check for Oracle-specific Types class + final Class oracleTypes = ClassHelper.getClass("oracle.jdbc.OracleTypes", false); + final Field cursorField = oracleTypes.getField("CURSOR"); + ORACLE_JDBC_TYPE_CURSOR = cursorField.getInt(null); + } + catch (ClassNotFoundException e) + { + log.warn("PlatformOracleImpl could not find Oracle JDBC classes"); + } + catch (NoSuchFieldException e) + { + log.warn("PlatformOracleImpl could not find Oracle JDBC type fields"); + } + catch (IllegalAccessException e) + { + log.warn("PlatformOracleImpl could not get Oracle JDBC type values"); + } } /** - * Method prepareNextValProcedureStatement implementation + * Method prepareNextValProcedureStatement implementation * is simply copied over from PlatformMsSQLServerImpl class. * @see org.apache.ojb.broker.platforms.Platform#prepareNextValProcedureStatement(java.sql.Connection, java.lang.String, java.lang.String) */ @@ -198,25 +223,22 @@ * all cases it's better to use setBinaryStream. Oracle also requires a change in the resultset * type of the prepared statement. MBAIRD NOTE: BLOBS may not work with Oracle database/thin * driver versions prior to 8.1.6. - * + * * @see Platform#setObjectForStatement */ public void setObjectForStatement(PreparedStatement ps, int index, Object value, int sqlType) throws SQLException { - if (((sqlType == Types.VARBINARY) || (sqlType == Types.LONGVARBINARY) || (sqlType == Types.BLOB)) - && (value instanceof byte[])) + if ((sqlType == Types.VARCHAR || sqlType == Types.CHAR) && (value instanceof String || value instanceof Character)) { - byte buf[] = (byte[]) value; - int length = buf.length; - if (isUsingOracleThinDriver(ps.getConnection()) && length > THIN_BLOB_MAX_SIZE) - { - throw new SQLException( - "Oracle thin driver cannot update BLOB values with length>2000. (Consider using Oracle9i as OJB platform.)"); - } - ByteArrayInputStream inputStream = new ByteArrayInputStream(buf); - changePreparedStatementResultSetType(ps); - ps.setBinaryStream(index, inputStream, length); + if (value instanceof String) + { + ps.setString(index, (String) value); + } + else // assert: value instanceof Character + { + ps.setString(index, value.toString()); + } } else if (value instanceof Double) { @@ -254,29 +276,55 @@ reader = new InputStreamReader(inputStream); length = buf.length; } - if (isUsingOracleThinDriver(ps.getConnection()) && length > THIN_CLOB_MAX_SIZE) - { - throw new SQLException( - "Oracle thin driver cannot insert CLOB values with length>4000. (Consider using Oracle9i as OJB platform.)"); - } ps.setCharacterStream(index, reader, length); } - else if ((sqlType == Types.CHAR || sqlType == Types.VARCHAR) - && - (value instanceof String || value instanceof Character)) + else if ((value instanceof byte[]) && ((sqlType == Types.VARBINARY) || (sqlType == Types.LONGVARBINARY) || (sqlType == Types.BLOB))) { - if (value instanceof String) +// byte buf[] = (byte[]) value; +// int length = buf.length; +// ByteArrayInputStream inputStream = new ByteArrayInputStream(buf); +// // arminw: seems this no longer mandatory +// //changePreparedStatementResultSetType(ps); +// ps.setBinaryStream(index, inputStream, length); + ps.setBytes(index, (byte[]) value); + } + else if(sqlType == Types.JAVA_OBJECT) + { + // JAVA_OBJECT is not proper supported, workaround: use BLOB type + byte[] ser = SerializationUtils.serialize((Serializable) value); + ps.setBytes(index, ser); + } + else + { + super.setObjectForStatement(ps, index, value, sqlType); + } + } + + public Object postPrepareReadInValue(final FieldDescriptor fld, final Object value) + { + /* + workaround for JAVA_OBJECT type. Currently Oracle doesn't + proper support JAVA_OBJECT type. On insert we transform JAVA_OBJECT + to byte[] array, thus on read we have to deserialize. The mapping type + is BLOB. + */ + if(fld.getJdbcType().getType() == Types.JAVA_OBJECT && value instanceof Blob) + { + Blob ret = (Blob) value; + byte[] b; + try { - ps.setString(index, (String) value); + b = ret.getBytes(1, (int) ret.length()); } - else // assert: value instanceof Character + catch(SQLException e) { - ps.setString(index, value.toString()); + throw new PersistenceBrokerSQLException("Can't read Blob object of serialized JAVA_OBJECT type", e); } + return SerializationUtils.deserialize(b); } else { - super.setObjectForStatement(ps, index, value, sqlType); + return super.postPrepareReadInValue(fld, value); } } @@ -314,6 +362,11 @@ return ORACLE_JOIN_SYNTAX; } + public boolean supportsOrderByInSubSelect() + { + return false; + } + public String createSequenceQuery(String sequenceName) { return "CREATE SEQUENCE " + sequenceName; @@ -399,15 +452,14 @@ /** * @see org.apache.ojb.broker.platforms.Platform#registerOutResultSet(java.sql.CallableStatement, int) */ - public void registerOutResultSet(CallableStatement stmt, int position) - throws SQLException + public void registerOutResultSet(CallableStatement stmt, int position) throws SQLException { stmt.registerOutParameter(position, ORACLE_JDBC_TYPE_CURSOR); } /** * Checks if the supplied connection is using the Oracle thin driver. - * + * * @param conn database connection for which to check JDBC-driver * @return <code>true</code> if the connection is using Oracle thin driver, <code>false</code> * otherwise. @@ -435,31 +487,4 @@ } return false; } - - /** - * Initializes static variables needed for getting Oracle-specific JDBC types. - */ - protected void initOracleReflectedVars() - { - try - { - // Check for Oracle-specific Types class - final Class oracleTypes = ClassHelper.getClass("oracle.jdbc.OracleTypes", false); - final Field cursorField = oracleTypes.getField("CURSOR"); - ORACLE_JDBC_TYPE_CURSOR = cursorField.getInt(null); - } - catch (ClassNotFoundException e) - { - log.warn("PlatformOracleImpl could not find Oracle JDBC classes"); - } - catch (NoSuchFieldException e) - { - log.warn("PlatformOracleImpl could not find Oracle JDBC type fields"); - } - catch (IllegalAccessException e) - { - log.warn("PlatformOracleImpl could not get Oracle JDBC type values"); - } - } - } --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]