You are viewing a plain text version of this content. The canonical link for it is here.
Posted to ojb-dev@db.apache.org by ma...@apache.org on 2004/01/24 01:08:58 UTC
cvs commit: db-ojb/src/java/org/apache/ojb/broker/platforms PlatformOracleImpl.java PlatformDefaultImpl.java Oracle9iLobHandler.java BlobWrapper.java PlatformOracle9iImpl.java Platform.java ClobWrapper.java
mattbaird 2004/01/23 16:08:58
Modified: src/java/org/apache/ojb/broker/util JdbcTypesHelper.java
ClassHelper.java
src/java/org/apache/ojb/broker/platforms
PlatformOracleImpl.java PlatformDefaultImpl.java
Oracle9iLobHandler.java BlobWrapper.java
PlatformOracle9iImpl.java Platform.java
ClobWrapper.java
Log:
Oracle BLOB/CLOB lovin'
thin driver in 9i works with > 4k blobs and clobs. thanks to all who helped.
Revision Changes Path
1.5 +32 -4 db-ojb/src/java/org/apache/ojb/broker/util/JdbcTypesHelper.java
Index: JdbcTypesHelper.java
===================================================================
RCS file: /home/cvs//db-ojb/src/java/org/apache/ojb/broker/util/JdbcTypesHelper.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- JdbcTypesHelper.java 19 Dec 2003 16:23:52 -0000 1.4
+++ JdbcTypesHelper.java 24 Jan 2004 00:08:57 -0000 1.5
@@ -997,6 +997,34 @@
public static final class T_Clob extends BaseType
{
+ protected static final int BUFSZ = 32768;
+ /**
+ * Convert CLOB to String. Safe for very large objects.
+ * @param aClob clob with character data
+ * @return a string containing the clob data
+ * @throws SQLException if conversion fails or the clob cannot be read
+ */
+ protected static String safeClobToString(Clob aClob) throws SQLException {
+ long length = aClob.length();
+ if (length == 0) {
+ return new String();
+ }
+ StringBuffer sb = new StringBuffer();
+ char[] buf = new char[BUFSZ];
+ java.io.Reader stream = aClob.getCharacterStream();
+ try {
+ int numRead;
+ while ((numRead = stream.read(buf)) != -1)
+ {
+ sb.append(buf, 0, numRead);
+ }
+ stream.close();
+ } catch (java.io.IOException e) {
+ throw new SQLException(e.getLocalizedMessage());
+ }
+ return sb.toString();
+ }
+
public Object sequenceKeyConversion(Long identifier) throws SequenceManagerException
{
throw new SequenceManagerException("Not supported sequence key type 'CLOB'");
@@ -1011,19 +1039,19 @@
Object readValueFromStatement(CallableStatement stmt, int columnIndex) throws SQLException
{
Clob aClob = stmt.getClob(columnIndex);
- return (stmt.wasNull() ? null : aClob.getSubString(1L, (int) aClob.length()));
+ return (stmt.wasNull() ? null : safeClobToString(aClob));
}
Object readValueFromResultSet(ResultSet rs, String columnName) throws SQLException
{
Clob aClob = rs.getClob(columnName);
- return (rs.wasNull() ? null : aClob.getSubString(1L, (int) aClob.length()));
+ return (rs.wasNull() ? null : safeClobToString(aClob));
}
Object readValueFromResultSet(ResultSet rs, int columnIndex) throws SQLException
{
Clob aClob = rs.getClob(columnIndex);
- return (rs.wasNull() ? null : aClob.getSubString(1L, (int) aClob.length()));
+ return (rs.wasNull() ? null : safeClobToString(aClob));
}
public int getType()
1.4 +68 -1 db-ojb/src/java/org/apache/ojb/broker/util/ClassHelper.java
Index: ClassHelper.java
===================================================================
RCS file: /home/cvs//db-ojb/src/java/org/apache/ojb/broker/util/ClassHelper.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- ClassHelper.java 31 Aug 2003 00:46:30 -0000 1.3
+++ ClassHelper.java 24 Jan 2004 00:08:58 -0000 1.4
@@ -1,6 +1,8 @@
package org.apache.ojb.broker.util;
import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Field;
/**
*
@@ -38,6 +40,40 @@
return target.getConstructor(types).newInstance(args);
}
+ /**
+ * Returns a method via reflection look-up of the specific signature.
+ * @param clazz method's java class
+ * @param methodName method name
+ * @param params method signature
+ * @return method invokable via <code>java.lang.reflect.Method#invoke</code>,
+ * or <code>null</code> if no matching method can be found
+ */
+ public static Method getMethod(Class clazz, String methodName, Class[] params) {
+ Method method;
+ try {
+ method = clazz.getMethod(methodName, params);
+ } catch (Exception ignore) {
+ method = null;
+ }
+ return method;
+ }
+
+ /**
+ * Returns a field via reflection look-up.
+ * @param clazz fields's java class
+ * @param fieldName field name
+ * @return field retrievable via <code>java.lang.reflect.Field#getXXX</code>,
+ * or <code>null</code> if no matching field can be found
+ */
+ public static Field getField(Class clazz, String fieldName) {
+ Field field;
+ try {
+ field = clazz.getField(fieldName);
+ } catch (Exception ignore) {
+ field = null;
+ }
+ return field;
+ }
// *******************************************************************
@@ -86,4 +122,35 @@
{
return newInstance(className, new Class[]{type}, new Object[]{arg});
}
+
+ /**
+ * Returns a method via reflection look-up of the specific signature.
+ * @param object runtime object instance
+ * @param methodName method name
+ * @param params method signature
+ * @return method invokable via <code>java.lang.reflect.Method#invoke</code>,
+ * or <code>null</code> if no matching method can be found
+ */
+ public static Method getMethod(Object object, String methodName, Class[] params) {
+ return getMethod(object.getClass(), methodName, params);
+ }
+
+ /**
+ * Returns a method via reflection look-up of the specific signature.
+ * @param className class name
+ * @param methodName method name
+ * @param params method signature
+ * @return method invokable via <code>java.lang.reflect.Method#invoke</code>,
+ * or <code>null</code> if no matching method can be found
+ */
+ public static Method getMethod(String className, String methodName, Class[] params) {
+ Method method = null;
+ try {
+ Class clazz = getClass(className, false);
+ method = getMethod(clazz, methodName, params);
+ } catch (Exception ignore) {
+ }
+ return method;
+ }
+
}
1.16 +54 -7 db-ojb/src/java/org/apache/ojb/broker/platforms/PlatformOracleImpl.java
Index: PlatformOracleImpl.java
===================================================================
RCS file: /home/cvs//db-ojb/src/java/org/apache/ojb/broker/platforms/PlatformOracleImpl.java,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -r1.15 -r1.16
--- PlatformOracleImpl.java 29 Jul 2003 16:54:12 -0000 1.15
+++ PlatformOracleImpl.java 24 Jan 2004 00:08:58 -0000 1.16
@@ -64,6 +64,8 @@
import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedAction;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
@@ -80,6 +82,11 @@
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;
private Logger logger = LoggerFactory.getLogger(PlatformOracleImpl.class);
/**
@@ -114,9 +121,13 @@
(sqlType == Types.BLOB)) && (value instanceof byte[]))
{
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, buf.length);
+ ps.setBinaryStream(index, inputStream, length);
}
else if (value instanceof Double)
{
@@ -132,20 +143,26 @@
{
ps.setLong(index, ((Long) value).longValue());
}
- else if (sqlType == Types.CLOB)
+ else if (sqlType == Types.CLOB &&
+ (value instanceof String || value instanceof byte[]))
{
Reader reader = null;
int length = 0;
if (value instanceof String)
{
- reader = new StringReader((String) value);
- length = (((String) value)).length();
+ String stringValue = (String) value;
+ length = stringValue.length();
+ reader = new StringReader(stringValue);
}
- else if (value instanceof byte[])
+ else
{
byte buf[] = (byte[]) value;
ByteArrayInputStream inputStream = new ByteArrayInputStream(buf);
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);
}
@@ -178,7 +195,7 @@
}
catch (Exception e)
{
- this.logger.info("Not using classes12.zip.");
+ logger.info("Not using classes12.zip.");
}
}
@@ -204,4 +221,34 @@
{
return "drop sequence " + sequenceName;
}
+
+ /**
+ * 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.
+ */
+ protected static boolean isUsingOracleThinDriver(Connection conn)
+ {
+ if (conn == null)
+ {
+ return false;
+ }
+ final DatabaseMetaData dbMetaData;
+ final String dbUrl;
+ try
+ {
+ dbMetaData = conn.getMetaData();
+ dbUrl = dbMetaData.getURL();
+ if (dbUrl != null && dbUrl.startsWith(THIN_URL_PREFIX))
+ {
+ return true;
+ }
+ }
+ catch (Exception e)
+ {
+ }
+ return false;
+ }
+
}
1.22 +1 -7 db-ojb/src/java/org/apache/ojb/broker/platforms/PlatformDefaultImpl.java
Index: PlatformDefaultImpl.java
===================================================================
RCS file: /home/cvs//db-ojb/src/java/org/apache/ojb/broker/platforms/PlatformDefaultImpl.java,v
retrieving revision 1.21
retrieving revision 1.22
diff -u -r1.21 -r1.22
--- PlatformDefaultImpl.java 16 Nov 2003 18:21:48 -0000 1.21
+++ PlatformDefaultImpl.java 24 Jan 2004 00:08:58 -0000 1.22
@@ -333,12 +333,6 @@
throw new UnsupportedOperationException("This feature is not supported by this implementation");
}
- public Object getClob(ResultSet rs, int jdbcType, String columnId) throws SQLException
- {
- java.sql.Clob aClob = rs.getClob(columnId);
- return (rs.wasNull() ? null : aClob.getSubString(1L, (int) aClob.length()));
- }
-
/* (non-Javadoc)
* @see org.apache.ojb.broker.platforms.Platform#addPagingSql(java.lang.StringBuffer)
*/
1.2 +298 -178 db-ojb/src/java/org/apache/ojb/broker/platforms/Oracle9iLobHandler.java
Index: Oracle9iLobHandler.java
===================================================================
RCS file: /home/cvs//db-ojb/src/java/org/apache/ojb/broker/platforms/Oracle9iLobHandler.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- Oracle9iLobHandler.java 29 Jul 2003 16:54:12 -0000 1.1
+++ Oracle9iLobHandler.java 24 Jan 2004 00:08:58 -0000 1.2
@@ -54,75 +54,82 @@
* <http://www.apache.org/>.
*/
+import org.apache.ojb.broker.util.logging.Logger;
+import org.apache.ojb.broker.util.logging.LoggerFactory;
+
import java.io.*;
import java.sql.Connection;
+import java.lang.reflect.InvocationTargetException;
/**
- * handles the Oracle LOB problems for 9i
+ * Handles the Oracle LOB problems for 9i.
*
* @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird<a>
+ * @author <a href="mailto:erik@cj.com">Erik Forkalsrud</a>
+ * @author <a href="mailto:martin.kalen@curalia.se">Martin Kalén</a>
+ * @version CVS $Id$
*/
-
public class Oracle9iLobHandler
{
- Connection m_connection = null;
- // Temporary LOBs
- BlobWrapper m_tempBlob = null;
+
+ protected static Logger logger = LoggerFactory.getLogger(Oracle9iLobHandler.class);
private static ClobWrapper createTempCLOB(Connection connection, ClobWrapper clob)
{
- ClobWrapper tempClob = null;
- if (clob != null)
- {
- try
- {
- // Create a temporary CLOB with duration session
- tempClob = ClobWrapper.createTemporary(connection, true, ClobWrapper.DURATION_SESSION);
-
- // Open the CLOB in readonly mode
- clob.open(ClobWrapper.MODE_READONLY);
-
- // Open the temporary CLOB in readwrite mode to enable writing
- tempClob.open(ClobWrapper.MODE_READWRITE);
+ if (clob == null) {
+ return null;
+ }
- // No of bytes read each trip to database
- int bytesread = 0;
-
- // Get the input stream for reading from the CLOB
- Reader clobReader = clob.getCharacterStream();
-
- // Get the output stream for writing into the CLOB
- Writer tempClobWriter = tempClob.getCharacterOutputStream();
-
- // Create a buffer to read data
- // getBufferSize() returns the optimal buffer size
- char[] charbuffer = new char[clob.getBufferSize()];
-
- // Read from the CLOB and write into the temporary CLOB
- while ((bytesread = clobReader.read(charbuffer)) != -1)
- tempClobWriter.write(charbuffer, 0, bytesread);
-
- // Flush and close the streams
- tempClobWriter.flush();
- tempClobWriter.close();
- clobReader.close();
-
- // Close the CLOBs
- clob.close();
- tempClob.close();
- }
- catch (Exception ex)
- {
- // Since an error has been caught, free the temporary LOBs
- freeTempLOB(tempClob, null);
- }
- }
+ ClobWrapper tempClob = null;
+ try
+ {
+ // Create a temporary CLOB with duration session
+ tempClob = ClobWrapper.createTemporary(connection, true, ClobWrapper.getDurationSessionValue());
+
+ // Open the CLOB in readonly mode
+ clob.open(ClobWrapper.getModeReadOnlyValue());
+
+ // Open the temporary CLOB in readwrite mode to enable writing
+ tempClob.open(ClobWrapper.getModeReadWriteValue());
+
+ // No of bytes read each trip to database
+ int bytesread;
+
+ // Get the input stream for reading from the CLOB
+ Reader clobReader = clob.getCharacterStream();
+
+ // Get the output stream for writing into the CLOB
+ Writer tempClobWriter = tempClob.getCharacterOutputStream();
+
+ // Create a buffer to read data
+ // getBufferSize() returns the optimal buffer size
+ char[] charbuffer = new char[clob.getBufferSize()];
+
+ // Read from the CLOB and write into the temporary CLOB
+ while ((bytesread = clobReader.read(charbuffer)) != -1)
+ {
+ tempClobWriter.write(charbuffer, 0, bytesread);
+ }
+
+ // Flush and close the streams
+ tempClobWriter.flush();
+ tempClobWriter.close();
+ clobReader.close();
+
+ // Close the CLOBs
+ clob.close();
+ tempClob.close();
+ }
+ catch (Exception e)
+ {
+ logger.error("Error during temporary CLOB write", e);
+ freeTempLOB(tempClob, null);
+ }
return tempClob;
}
public static String convertCLOBtoString(Connection connection, Object nativeclob)
{
- String retval = null;
ClobWrapper temp = new ClobWrapper();
temp.setClob(nativeclob);
/**
@@ -130,139 +137,252 @@
*/
ClobWrapper clob = createTempCLOB(connection, temp);
- if (clob != null)
- {
- // Buffer to hold the CLOB data
- StringBuffer clobdata = new StringBuffer();
- // No of bytes read each trip to database
- int bytesread = 0;
- try
- {
- // Open the CLOB in readonly mode
- clob.open(ClobWrapper.MODE_READONLY);
- // Open the stream to read data
- Reader clobReader = clob.getCharacterStream();
- // Buffer size is fixed using the getBufferSize() method which returns
- // the optimal buffer size to read data from the LOB
- char[] charbuffer = new char[clob.getBufferSize()];
- // Keep reading from the CLOB and append it to the stringbuffer till
- // there is no more to read
- while ((bytesread = clobReader.read(charbuffer)) != -1)
- {
- clobdata.append(charbuffer, 0, bytesread);
- }
- // Close the input stream
- clobReader.close();
- // Close the CLOB
- clob.close();
- retval = clobdata.toString();
- clobdata = null;
- }
- catch (Exception ex)
- {
- // report
- }
- }
+ if (clob == null) {
+ return null;
+ }
+
+ String retval = null;
+ // Buffer to hold the CLOB data
+ StringBuffer clobdata = new StringBuffer();
+ // No of bytes read each trip to database
+ int bytesread = 0;
+ try
+ {
+ // Open the CLOB in readonly mode
+ clob.open(ClobWrapper.getModeReadOnlyValue());
+ // Open the stream to read data
+ Reader clobReader = clob.getCharacterStream();
+ // Buffer size is fixed using the getBufferSize() method which returns
+ // the optimal buffer size to read data from the LOB
+ char[] charbuffer = new char[clob.getBufferSize()];
+ // Keep reading from the CLOB and append it to the stringbuffer till
+ // there is no more to read
+ while ((bytesread = clobReader.read(charbuffer)) != -1)
+ {
+ clobdata.append(charbuffer, 0, bytesread);
+ }
+ // Close the input stream
+ clobReader.close();
+ // Close the CLOB
+ clob.close();
+ retval = clobdata.toString();
+ clobdata = null;
+ }
+ catch (Exception e)
+ {
+ logger.error("Error during CLOB read", e);
+ freeTempLOB(clob, null);
+ }
return retval;
}
public static Object createCLOBFromString(Connection conn, String clobData)
{
- ClobWrapper clob = new ClobWrapper();
- if (clobData != null)
- {
- try
- {
- clob = ClobWrapper.createTemporary(conn, true, ClobWrapper.DURATION_SESSION);
-
- // Open the temporary CLOB in readwrite mode to enable writing
- clob.open(ClobWrapper.MODE_READWRITE);
-
- // Clear the previous contents of the CLOB
- clob.trim(0);
-
- // Get the output stream to write
- Writer tempClobWriter = clob.getCharacterOutputStream();
-
- // Write the data into the temporary CLOB
- tempClobWriter.write(clobData);
-
- // Flush and close the stream
- tempClobWriter.flush();
- tempClobWriter.close();
-
- // Close the temporary CLOB
- clob.close();
- }
- catch (Exception ex)
- {
- // Since an error has been caught, free the temporary LOBs
- freeTempLOB(clob, null);
- }
- }
- return clob.getClob();
+ if (clobData == null) {
+ return null;
+ }
+ ClobWrapper clob = null;
+ try
+ {
+ clob = ClobWrapper.createTemporary(conn, true, ClobWrapper.getDurationSessionValue());
+ if (clob != null) {
+ // Open the temporary CLOB in readwrite mode to enable writing
+ clob.open(ClobWrapper.getModeReadWriteValue());
+
+ // Clear the previous contents of the CLOB
+ clob.trim(0);
+
+ // Get the output stream to write
+ Writer tempClobWriter = clob.getCharacterOutputStream();
+
+ if (tempClobWriter != null) {
+ // Write the data into the temporary CLOB
+ tempClobWriter.write(clobData);
+
+ // Flush and close the stream
+ tempClobWriter.flush();
+ tempClobWriter.close();
+ }
+
+ // Close the temporary CLOB
+ clob.close();
+ }
+ }
+ catch (InvocationTargetException ite) {
+ Throwable t = ite.getTargetException();
+ freeTempLOB(clob, null);
+ if (t instanceof java.lang.UnsatisfiedLinkError) {
+ logger.error("Oracle JDBC-driver version does not match installed OCI-driver");
+ } else {
+ logger.error("Error during temporary CLOB write", t);
+ }
+ }
+ catch (Exception e)
+ {
+ logger.error("Error during temporary CLOB write", e);
+ freeTempLOB(clob, null);
+ }
+ return clob == null ? null : clob.getClob();
}
- /**
- * Updates the content of the temporary BLOB with the new data in the file.
- **/
- void createBLOBFromFile(File file)
- {
- if (file != null)
- {
- try
- {
- // If temporary BLOB has not yet been created, create new
- if (m_tempBlob == null)
- {
- BlobWrapper temp = new BlobWrapper();
- m_tempBlob = temp.createTemporary(m_connection, true,
- BlobWrapper.DURATION_SESSION);
- }
-
- // Open the temporary BLOB in readwrite mode to enable writing
- m_tempBlob.open(BlobWrapper.MODE_READWRITE);
-
- // Clear the contents of the temporary BLOB
- m_tempBlob.trim(0);
-
- // Get the input stream to read from the file
- FileInputStream fileIStream = new FileInputStream(file);
-
- // Get the output stream to write into the temporary BLOB
- OutputStream tempBlobOStream = m_tempBlob.getBinaryOutputStream();
-
- // Get the optimal buffer size to read bytes
- byte[] buffer = new byte[m_tempBlob.getBufferSize()];
-
- // No of bytes read in each trip to database
- int bytesread = 0;
-
- // Read from the file and write to the temporary BLOB
- while ((bytesread = fileIStream.read(buffer)) != -1)
- tempBlobOStream.write(buffer, 0, bytesread);
-
- // Flush and close the streams
- tempBlobOStream.flush();
- fileIStream.close();
- tempBlobOStream.close();
-
- // Close the temporary BLOB
- m_tempBlob.close();
- }
- catch (Exception ex)
- {
- // Since an error has been caught, free the temporary LOBs
- freeTempLOB(null, m_tempBlob);
- }
- }
- }
+ private static BlobWrapper createTempBLOB(Connection connection, BlobWrapper blob)
+ {
+ if (blob == null) {
+ return null;
+ }
+
+ BlobWrapper tempBlob = null;
+ try
+ {
+ // Create a temporary BLOB with duration session
+ tempBlob = BlobWrapper.createTemporary(connection, true, BlobWrapper.getDurationSessionValue());
+
+ // Open the CLOB in readonly mode
+ blob.open(BlobWrapper.getModeReadOnlyValue());
+
+ // Open the temporary CLOB in readwrite mode to enable writing
+ tempBlob.open(BlobWrapper.getModeReadWriteValue());
+
+ // No of bytes read each trip to database
+ int bytesread;
+
+ // Get the input stream for reading from the BLOB
+ InputStream blobInputStream = blob.getBinaryStream();
+
+ // Get the output stream for writing into the BLOB
+ OutputStream tempBlobOutputStream = tempBlob.getBinaryOutputStream();
+
+ // Create a buffer to read data
+ // getBufferSize() returns the optimal buffer size
+ byte[] bytebuffer = new byte[blob.getBufferSize()];
+
+ // Read from the BLOB and write into the temporary BLOB
+ while ((bytesread = blobInputStream.read(bytebuffer)) != -1)
+ {
+ tempBlobOutputStream.write(bytebuffer, 0, bytesread);
+ }
+
+ // Flush and close the streams
+ tempBlobOutputStream.flush();
+ tempBlobOutputStream.close();
+ blobInputStream.close();
+
+ // Close the BLOBs
+ blob.close();
+ tempBlob.close();
+ }
+ catch (Exception e)
+ {
+ logger.error("Error during temporary BLOB write", e);
+ freeTempLOB(null, tempBlob);
+ }
+ return tempBlob;
+ }
+
+ public static byte[] convertBLOBtoByteArray(Connection connection, Object nativeblob)
+ {
+ BlobWrapper temp = new BlobWrapper();
+ temp.setBlob(nativeblob);
+ /**
+ * first, convert the blob to another blob. Thanks Oracle, you rule.
+ */
+ BlobWrapper blob = createTempBLOB(connection, temp);
+ if (blob == null) {
+ return null;
+ }
+
+ byte[] retval = null;
+ // Buffer to hold the BLOB data
+ ByteArrayOutputStream blobdata = new ByteArrayOutputStream();
+ // No of bytes read each trip to database
+ int bytesread = 0;
+ try
+ {
+ // Open the BLOB in readonly mode
+ blob.open(BlobWrapper.getModeReadOnlyValue());
+ // Open the stream to read data
+ InputStream blobInputStream = blob.getBinaryStream();
+ // Buffer size is fixed using the getBufferSize() method which returns
+ // the optimal buffer size to read data from the LOB
+ byte[] bytebuffer = new byte[blob.getBufferSize()];
+ // Keep reading from the BLOB and append it to the bytebuffer till
+ // there is no more to read
+ while ((bytesread = blobInputStream.read(bytebuffer)) != -1)
+ {
+ blobdata.write(bytebuffer, 0, bytesread);
+ }
+ // Close the input and output streams stream
+ blobInputStream.close();
+ blobdata.flush();
+ blobdata.close();
+
+ // Close the BLOB
+ blob.close();
+ retval = blobdata.toByteArray();
+ blobdata = null;
+ }
+ catch (Exception e)
+ {
+ logger.error("Error during BLOB read", e);
+ freeTempLOB(null, blob);
+ }
+ return retval;
+ }
+
+ public static Object createBLOBFromByteArray(Connection conn, byte[] blobData)
+ {
+ if (blobData == null) {
+ return null;
+ }
+
+ BlobWrapper blob = null;
+ try
+ {
+ blob = BlobWrapper.createTemporary(conn, true, BlobWrapper.getDurationSessionValue());
+
+ // Open the temporary BLOB in readwrite mode to enable writing
+ blob.open(BlobWrapper.getModeReadWriteValue());
+
+ // Clear the previous contents of the BLOB
+ blob.trim(0);
+
+ // Get the output stream to write
+ OutputStream tempBlobOutputStream = blob.getBinaryOutputStream();
+
+ // Write the data into the temporary BLOB
+ tempBlobOutputStream.write(blobData);
+
+ // Flush and close the stream
+ tempBlobOutputStream.flush();
+ tempBlobOutputStream.close();
+
+ // Close the temporary BLOB
+ blob.close();
+ }
+ catch (InvocationTargetException ite) {
+ Throwable t = ite.getTargetException();
+ freeTempLOB(null, blob);
+ if (t instanceof java.lang.UnsatisfiedLinkError) {
+ logger.error("Oracle JDBC-driver version does not match installed OCI-driver");
+ } else {
+ logger.error("Error during temporary BLOB write", t);
+ }
+ }
+ catch (Exception e)
+ {
+ logger.error("Error during temporary BLOB write", e);
+ freeTempLOB(null, blob);
+ }
+ return blob == null ? null : blob.getBlob();
+ }
/**
- * Frees the temporary LOBs when an exception is raised in the application
- * or when the LOBs are no longer needed. If the LOBs are not freed , the
- * space used by these LOBs are not reclaimed.
- **/
+ * Frees the temporary LOBs when an exception is raised in the application
+ * or when the LOBs are no longer needed. If the LOBs are not freed, the
+ * space used by these LOBs are not reclaimed.
+ * @param clob CLOB-wrapper to free or null
+ * @param blob BLOB-wrapper to free or null
+ */
private static void freeTempLOB(ClobWrapper clob, BlobWrapper blob)
{
try
@@ -277,7 +397,6 @@
// Free the memory used by this CLOB
clob.freeTemporary();
- clob = null;
}
if (blob != null)
@@ -290,11 +409,12 @@
// Free the memory used by this BLOB
blob.freeTemporary();
- blob = null;
}
}
- catch (Exception ex)
+ catch (Exception e)
{
+ logger.error("Error during temporary LOB release", e);
}
}
+
}
1.2 +237 -60 db-ojb/src/java/org/apache/ojb/broker/platforms/BlobWrapper.java
Index: BlobWrapper.java
===================================================================
RCS file: /home/cvs//db-ojb/src/java/org/apache/ojb/broker/platforms/BlobWrapper.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- BlobWrapper.java 29 Jul 2003 16:54:12 -0000 1.1
+++ BlobWrapper.java 24 Jan 2004 00:08:58 -0000 1.2
@@ -54,71 +54,248 @@
* <http://www.apache.org/>.
*/
-import java.sql.Connection;
-import java.sql.SQLException;
+import org.apache.commons.lang.BooleanUtils;
+import org.apache.ojb.broker.util.ClassHelper;
+
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.reflect.Method;
+import java.lang.reflect.Field;
+import java.sql.Connection;
+import java.sql.SQLException;
/**
- * Created by IntelliJ IDEA.
- * User: matthew.baird
- * Date: Jun 26, 2003
- * Time: 3:58:47 PM
- * To change this template use Options | File Templates.
+ * Wraps the Oracle BLOB type and makes it accessible via reflection
+ * without having to import the Oracle Classes.
+ * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird</a>
+ * @author <a href="mailto:erik@cj.com">Erik Forkalsrud</a>
+ * @author <a href="martin.kalen@curalia.se">Martin Kalén</a>
+ * @version CVS $Id$
*/
public class BlobWrapper
{
- public static final int MAX_CHUNK_SIZE = 32768;
- public static final int DURATION_SESSION = 10;
- public static final int DURATION_CALL = 12;
- static final int OLD_WRONG_DURATION_SESSION = 1;
- static final int OLD_WRONG_DURATION_CALL = 2;
- public static final int MODE_READONLY = 0;
- public static final int MODE_READWRITE = 1;
-
- private Object m_blob;
-
- public BlobWrapper createTemporary(Connection conn, boolean b, int i)
- {
- return new BlobWrapper();
- }
-
- public void open(int i) throws SQLException
- {
-
- }
-
- public int getBufferSize() throws SQLException
- {
- return 1;
- }
-
- public void close() throws SQLException
- {
- }
-
- public void trim(long l) throws SQLException
- {
-
- }
-
- public void freeTemporary() throws SQLException
- {
-
- }
-
- public InputStream getBinaryStream() throws SQLException
- {
- return null;
- }
-
- public OutputStream getBinaryOutputStream() throws SQLException
- {
- return null;
- }
-
- public boolean isOpen() throws SQLException
- {
- return true;
- }
+ protected Object m_blob;
+
+ // Fields - values must be looked up via reflection not be compile-time Oracle-version dependent
+ protected static Field durationSession;
+ protected static Field durationCall;
+ protected static Field modeReadOnly;
+ protected static Field modeReadWrite;
+
+ // Methods
+ protected static Method createTemporary;
+ protected static Method freeTemporary;
+ protected static Method open;
+ protected static Method isOpen;
+ protected static Method getBinaryStream;
+ protected static Method getBinaryOutputStream;
+ protected static Method getBufferSize;
+ protected static Method close;
+ protected static Method trim;
+
+ /**
+ * Initialize all methods and fields via reflection.
+ */
+ static
+ {
+ try
+ {
+ Class blobClass = ClassHelper.getClass("oracle.sql.BLOB", false);
+ createTemporary = blobClass.getMethod("createTemporary", new Class[]{Connection.class, Boolean.TYPE, Integer.TYPE});
+ freeTemporary = blobClass.getMethod("freeTemporary", null);
+ open = blobClass.getMethod("open", new Class[]{Integer.TYPE});
+ isOpen = blobClass.getMethod("isOpen", null);
+ getBinaryStream = blobClass.getMethod("getBinaryStream", null);
+ getBinaryOutputStream = blobClass.getMethod("getBinaryOutputStream", null);
+ getBufferSize = blobClass.getMethod("getBufferSize", null);
+ close = blobClass.getMethod("close", null);
+ trim = blobClass.getMethod("trim", new Class[]{Long.TYPE});
+
+ durationSession = ClassHelper.getField(blobClass, "DURATION_SESSION");
+ durationCall = ClassHelper.getField(blobClass, "DURATION_CALL");
+ modeReadOnly = ClassHelper.getField(blobClass, "MODE_READONLY");
+ modeReadWrite = ClassHelper.getField(blobClass, "MODE_READWRITE");
+ }
+ catch (Exception ingore)
+ {
+ }
+ }
+
+ public Object getBlob()
+ {
+ return m_blob;
+ }
+
+ public void setBlob(Object blob)
+ {
+ m_blob = blob;
+ }
+
+ protected static int staticIntFieldValue(Field field) {
+ int value = 0;
+ try {
+ value = field.getInt(null);
+ } catch (Exception ignore) {
+ value = -1;
+ }
+ return value;
+ }
+
+ public static int getDurationSessionValue() {
+ return staticIntFieldValue(durationSession);
+ }
+
+ public static int getDurationCallValue() {
+ return staticIntFieldValue(durationCall);
+ }
+
+ public static int getModeReadOnlyValue() {
+ return staticIntFieldValue(modeReadOnly);
+ }
+
+ public static int getModeReadWriteValue() {
+ return staticIntFieldValue(modeReadWrite);
+ }
+
+ public static BlobWrapper createTemporary(Connection conn, boolean b, int i) throws Exception
+ {
+ BlobWrapper retval = new BlobWrapper();
+ // Passing null to invoke static method
+ retval.setBlob(createTemporary.invoke(null, new Object[]{conn, BooleanUtils.toBooleanObject(b), new Integer(i)}));
+ return retval;
+ }
+
+ public void open(int i) throws SQLException
+ {
+ if (m_blob == null) {
+ return;
+ }
+ try
+ {
+ open.invoke(m_blob, new Object[]{new Integer(i)});
+ }
+ catch (Throwable e)
+ {
+ throw new SQLException(e.getMessage());
+ }
+ }
+
+ public boolean isOpen() throws SQLException
+ {
+ if (m_blob == null) {
+ return false;
+ }
+ boolean open = false;
+ try
+ {
+ Boolean retval = (Boolean) isOpen.invoke(m_blob, null);
+ if (retval != null) {
+ open = retval.booleanValue();
+ }
+ }
+ catch (Throwable e)
+ {
+ throw new SQLException(e.getMessage());
+ }
+ return open;
+ }
+
+ public InputStream getBinaryStream() throws SQLException
+ {
+ if (m_blob == null) {
+ return null;
+ }
+ InputStream retval = null;
+ try
+ {
+ retval = (InputStream) getBinaryStream.invoke(m_blob, null);
+ }
+ catch (Throwable e)
+ {
+ throw new SQLException(e.getMessage());
+ }
+ return retval;
+ }
+
+ public OutputStream getBinaryOutputStream() throws SQLException
+ {
+ if (m_blob == null) {
+ return null;
+ }
+ OutputStream retval = null;
+ try
+ {
+ retval = (OutputStream) getBinaryOutputStream.invoke(m_blob, null);
+ }
+ catch (Throwable e)
+ {
+ throw new SQLException(e.getMessage());
+ }
+ return retval;
+ }
+
+ public int getBufferSize() throws SQLException
+ {
+ if (m_blob == null) {
+ return 0;
+ }
+ Integer retval = null;
+ try
+ {
+ retval = (Integer) getBufferSize.invoke(m_blob, null);
+ }
+ catch (Throwable e)
+ {
+ throw new SQLException(e.getMessage());
+ }
+ return retval.intValue();
+ }
+
+ public void close() throws SQLException
+ {
+ if (m_blob == null) {
+ return;
+ }
+ try
+ {
+ close.invoke(m_blob, null);
+ }
+ catch (Throwable e)
+ {
+ throw new SQLException(e.getMessage());
+ }
+
+ }
+
+ public void trim(long l) throws SQLException
+ {
+ if (m_blob == null) {
+ return;
+ }
+ try
+ {
+ trim.invoke(m_blob, new Object[]{new Long(l)});
+ }
+ catch (Throwable e)
+ {
+ throw new SQLException(e.getMessage());
+ }
+
+ }
+
+ public void freeTemporary() throws SQLException
+ {
+ if (m_blob == null) {
+ return;
+ }
+ try
+ {
+ freeTemporary.invoke(m_blob, null);
+ }
+ catch (Throwable e)
+ {
+ throw new SQLException(e.getMessage());
+ }
+ }
+
}
1.9 +283 -399 db-ojb/src/java/org/apache/ojb/broker/platforms/PlatformOracle9iImpl.java
Index: PlatformOracle9iImpl.java
===================================================================
RCS file: /home/cvs//db-ojb/src/java/org/apache/ojb/broker/platforms/PlatformOracle9iImpl.java,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- PlatformOracle9iImpl.java 8 Dec 2003 10:18:16 -0000 1.8
+++ PlatformOracle9iImpl.java 24 Jan 2004 00:08:58 -0000 1.9
@@ -54,22 +54,25 @@
* <http://www.apache.org/>.
*/
+import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
+import org.apache.ojb.broker.metadata.JdbcType;
+import org.apache.ojb.broker.util.ClassHelper;
+import org.apache.ojb.broker.util.JdbcTypesHelper;
+
import java.io.ByteArrayInputStream;
-import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
-import java.sql.ResultSet;
import java.sql.SQLException;
-import java.sql.Statement;
import java.sql.Types;
-
-import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
/**
- * This class is a concrete implementation of <code>Platform</code>. Provides
- * an implementation that works around some issues with Oracle in general and
- * Oracle 9i's Thin driver in particular.
+ * This class is a concrete implementation of <code>Platform</code>. Provides
+ * an implementation that works around some issues with Oracle in general and
+ * Oracle 9i's Thin driver in particular.
*
* Optimization: Oracle Batching (not standard JDBC batching)
* see http://technet.oracle.com/products/oracle9i/daily/jun07.html
@@ -81,400 +84,281 @@
* see http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/jdbc30/StmtCacheSample/Readme.html
*
* 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:mattbaird@yahoo.com">Matthew Baird<a>
+ * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird</a>
+ * @author <a href="mailto:erik@cj.com">Erik Forkalsrud</a>
+ * @author <a href="mailto:martin.kalen@curalia.se">Martin Kalén</a>
+ * @version CVS $Id$
+ * @see Platform
+ * @see PlatformDefaultImpl
+ * @see PlatformOracleImpl
*/
-
public class PlatformOracle9iImpl extends PlatformOracleImpl
{
- private static final Object[] BATCH_SIZE = {new Integer(10)};
- private static final Class[] PARAM_TYPE_INTEGER = {Integer.TYPE};
- private static final Class[] PARAM_TYPE_BOOLEAN = {Boolean.TYPE};
- private static boolean STATEMENT_CACHING_SUPPORTED = true;
- private static boolean SET_EXECUTE_BATCH_METHOD_EXISTS = true;
- private static boolean SEND_BATCH_METHOD_EXISTS = true;
- private static Method SET_EXECUTE_BATCH = null;
- private static Method SEND_BATCH = null;
- private static Method SET_STATEMENT_CACHING_ENABLE = null;
- private static Method SET_IMPLICIT_CACHING_ENABLED = null;
- private static Method SET_CLOB = null;
- private static Method SET_BLOB = null;
- private static boolean SET_CLOB_AND_LOB_SUPPORTED = false;
- private static Method GET_CLOB = null;
- private static boolean GET_CLOB_AND_LOB_SUPPORTED = false;
-
- private static boolean ROW_PREFETCH_SUPPORTED = true;
- private static Method SET_ROW_PREFETCH = null;
- private static final int STATEMENT_CACHE_SIZE = 100;
- private static final int ROW_PREFETCH_SIZE = 100;
-
- // Session time zone has to be set while accessing Timestamp with
- // Time Zone and Timestamp with Local TimeZone datatypes.
- // OracleConnection has the APIs to set the session time zone.
- // Sets the session time zone to the time zone specified in the java
- // virtual machine.
- //((OracleConnection) m_connection).setSessionTimeZone(TimeZone.getDefault().getID());
-
-
- /**
- * enable oracle statement caching
- *
- * see http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/jdbc30/StmtCacheSample/Readme.html
- *
- * @param jcd
- * @param conn
- * @throws PlatformException
- */
- public void initializeJdbcConnection(JdbcConnectionDescriptor jcd, Connection conn) throws PlatformException
- {
- /**
- * do all the generic initialization first
- */
- super.initializeJdbcConnection(jcd, conn);
-
- if (SET_STATEMENT_CACHING_ENABLE == null && SET_IMPLICIT_CACHING_ENABLED == null && STATEMENT_CACHING_SUPPORTED)
- {
- try
- {
- SET_STATEMENT_CACHING_ENABLE = conn.getClass().getMethod("setStatementCacheSize", PARAM_TYPE_INTEGER);
- SET_IMPLICIT_CACHING_ENABLED = conn.getClass().getMethod("setImplicitCachingEnabled", PARAM_TYPE_BOOLEAN);
- }
- catch (NoSuchMethodException e)
- {
- STATEMENT_CACHING_SUPPORTED = false;
- }
- catch (SecurityException e)
- {
- STATEMENT_CACHING_SUPPORTED = false;
- throw new PlatformException(e.getMessage(), e);
- }
- }
- if (STATEMENT_CACHING_SUPPORTED)
- {
- try
- {
- SET_STATEMENT_CACHING_ENABLE.invoke(conn, new Object[]{new Integer(STATEMENT_CACHE_SIZE)}); // use a 100 item cache
- SET_IMPLICIT_CACHING_ENABLED.invoke(conn, new Object[]{Boolean.TRUE}); // use implicit caching
- }
- catch (IllegalAccessException e)
- {
- STATEMENT_CACHING_SUPPORTED = false;
- throw new PlatformException(e.getMessage(), e);
- }
- catch (IllegalArgumentException e)
- {
- STATEMENT_CACHING_SUPPORTED = false;
- throw new PlatformException(e.getMessage(), e);
- }
- catch (InvocationTargetException e)
- {
- STATEMENT_CACHING_SUPPORTED = false;
- throw new PlatformException(e.getMessage(), e);
- }
- }
- }
-
- /**
- *
- * doesn't seem to improve performance.
- *
- * support for oracle prefetching
- *
- * see http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/advanced/RowPrefetchSample/Readme.html
- *
- * @param stmt
- * @throws PlatformException
- */
- public void XXXXafterStatementCreate(Statement stmt) throws PlatformException
- {
- super.afterStatementCreate(stmt);
- if (ROW_PREFETCH_SUPPORTED && SET_ROW_PREFETCH == null)
- {
- try
- {
- SET_ROW_PREFETCH = stmt.getClass().getMethod("setRowPrefetch", PARAM_TYPE_INTEGER);
- }
- catch (NoSuchMethodException e)
- {
- ROW_PREFETCH_SUPPORTED = false;
- }
- catch (SecurityException e)
- {
- ROW_PREFETCH_SUPPORTED = false;
- throw new PlatformException(e.getMessage(), e);
- }
- }
- if (ROW_PREFETCH_SUPPORTED)
- {
- try
- {
- SET_ROW_PREFETCH.invoke(stmt, new Object[]{new Integer(ROW_PREFETCH_SIZE)}); // use a 100 item cache
- }
- catch (IllegalAccessException e)
- {
- STATEMENT_CACHING_SUPPORTED = false;
- throw new PlatformException(e.getMessage(), e);
- }
- catch (IllegalArgumentException e)
- {
- STATEMENT_CACHING_SUPPORTED = false;
- throw new PlatformException(e.getMessage(), e);
- }
- catch (InvocationTargetException e)
- {
- STATEMENT_CACHING_SUPPORTED = false;
- throw new PlatformException(e.getMessage(), e);
- }
- }
- }
-
- /**
- * don't bind to the Oracle drivers and thus have to cast, rather do the
- * reflection to find out if we can use the setExecuteBatch method with this
- * particular driver, and degrade to default JDBC batching if necessary
- * @param stmt
- * @throws PlatformException
- */
- public void beforeBatch(PreparedStatement stmt) throws PlatformException
- {
- // ((OraclePreparedStatement)ps).setExecuteBatch (3);
- if (SET_EXECUTE_BATCH_METHOD_EXISTS && SEND_BATCH_METHOD_EXISTS)
- {
- try
- {
- if (SET_EXECUTE_BATCH == null)
- {
- SET_EXECUTE_BATCH = stmt.getClass().getMethod("setExecuteBatch", PARAM_TYPE_INTEGER);
- }
- SET_EXECUTE_BATCH.invoke(stmt, BATCH_SIZE);
- }
- catch (IllegalAccessException e)
- {
- SET_EXECUTE_BATCH_METHOD_EXISTS = false;
- throw new PlatformException(e.getMessage(), e);
- }
- catch (IllegalArgumentException e)
- {
- SET_EXECUTE_BATCH_METHOD_EXISTS = false;
- throw new PlatformException(e.getMessage(), e);
- }
- catch (InvocationTargetException e)
- {
- SET_EXECUTE_BATCH_METHOD_EXISTS = false;
- throw new PlatformException(e.getMessage(), e);
- }
- catch (NoSuchMethodException e)
- {
- SET_EXECUTE_BATCH_METHOD_EXISTS = false;
- }
- }
- if (!SET_EXECUTE_BATCH_METHOD_EXISTS)
- {
- super.beforeBatch(stmt);
- }
- }
-
- /**
- * in oracle batching we call executeUpdate instead of addBatch
- * @param stmt
- * @throws PlatformException
- */
- public void addBatch(PreparedStatement stmt) throws PlatformException
- {
- // ps.executeUpdate();
- if (SET_EXECUTE_BATCH_METHOD_EXISTS && SEND_BATCH_METHOD_EXISTS)
- {
- try
- {
- stmt.executeUpdate();
- }
- catch (SQLException e)
- {
- throw new PlatformException(e.getMessage(), e);
- }
- }
- else
- {
- super.addBatch(stmt);
- }
- }
-
- /**
- * equivelent of calling ((OraclePreparedStatement)ps).sendBatch() but we do it reflectively so
- * we don't have to bind to the Oracle classes
- * JDBC sends the queued request
- * @param stmt the statement you want to execute sendBatch on.
- * @return an int array of the success statuses.
- * @throws PlatformException
- */
- public int[] executeBatch(PreparedStatement stmt) throws PlatformException
- {
- int[] retval = null;
- if (SEND_BATCH_METHOD_EXISTS)
- {
- try
- {
- if (SEND_BATCH == null)
- {
- SEND_BATCH = stmt.getClass().getMethod("sendBatch", null);
- }
- int numberOfItemsSubmitted = ((Integer) SEND_BATCH.invoke(stmt, null)).intValue();
- retval = new int[numberOfItemsSubmitted];
- for (int i = 0; i < numberOfItemsSubmitted; i++)
- {
- retval[i] = 1;
- }
- }
- catch (IllegalAccessException e)
- {
- SEND_BATCH_METHOD_EXISTS = false;
- throw new PlatformException(e.getMessage(), e);
- }
- catch (IllegalArgumentException e)
- {
- SEND_BATCH_METHOD_EXISTS = false;
- throw new PlatformException(e.getMessage(), e);
- }
- catch (InvocationTargetException e)
- {
- SEND_BATCH_METHOD_EXISTS = false;
- throw new PlatformException(e.getMessage(), e);
- }
- catch (NoSuchMethodException e)
- {
- SEND_BATCH_METHOD_EXISTS = false;
- }
- }
- if (!SEND_BATCH_METHOD_EXISTS)
- {
- retval = super.executeBatch(stmt);
- }
- return retval;
- }
-
- public void setObjectForStatement(PreparedStatement ps, int index, Object value, int sqlType) throws SQLException
- {
- if (SET_CLOB == null && SET_CLOB_AND_LOB_SUPPORTED)
- {
- try
- {
- SET_CLOB = ps.getClass().getMethod("setCLOB", new Class[]{Integer.TYPE, Class.forName("oracle.sql.CLOB")});
- SET_BLOB = ps.getClass().getMethod("setBLOB", new Class[]{Integer.TYPE, Class.forName("oracle.sql.BLOB")});
- SET_CLOB_AND_LOB_SUPPORTED = true;
- }
- catch (Throwable t)
- {
- SET_CLOB_AND_LOB_SUPPORTED = false;
- }
- }
- if (((sqlType == Types.VARBINARY) || (sqlType == Types.LONGVARBINARY)) && (value instanceof byte[]))
- {
- byte buf[] = (byte[]) value;
- ByteArrayInputStream inputStream = new ByteArrayInputStream(buf);
- 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 && value instanceof String)
- {
- if (SET_CLOB_AND_LOB_SUPPORTED)
- {
- try
- {
- SET_CLOB.invoke(ps, new Object[]{new Integer(index), Oracle9iLobHandler.createCLOBFromString(ps.getConnection(), (String) value)});
- }
- catch (IllegalAccessException e)
- {
- throw new SQLException(e.getMessage());
- }
- catch (IllegalArgumentException e)
- {
- throw new SQLException(e.getMessage());
- }
- catch (InvocationTargetException e)
- {
- throw new SQLException(e.getMessage());
- }
- }
- }
- else if (sqlType == Types.BLOB)
- {
- if (SET_CLOB_AND_LOB_SUPPORTED)
- {
- try
- {
- SET_BLOB.invoke(ps, new Object[]{new Integer(index)});
- }
- catch (IllegalAccessException e)
- {
- throw new SQLException(e.getMessage());
- }
- catch (IllegalArgumentException e)
- {
- throw new SQLException(e.getMessage());
- }
- catch (InvocationTargetException e)
- {
- throw new SQLException(e.getMessage());
- }
- }
- }
- else
- {
- super.setObjectForStatement(ps, index, value, sqlType);
- }
- }
-
- public Object getClob(ResultSet rs, int jdbcType, String columnId) throws SQLException
- {
- String retval = "";
- if (GET_CLOB == null && GET_CLOB_AND_LOB_SUPPORTED)
- {
- try
- {
- GET_CLOB = rs.getClass().getMethod("getCLOB", new Class[]{String.class});
- GET_CLOB_AND_LOB_SUPPORTED = true;
- }
- catch (Throwable t)
- {
- GET_CLOB_AND_LOB_SUPPORTED = false;
- }
- }
- if (GET_CLOB_AND_LOB_SUPPORTED)
- {
- try
- {
- Object obj = GET_CLOB.invoke(rs, new Object[]{columnId});
- retval = Oracle9iLobHandler.convertCLOBtoString(rs.getStatement().getConnection(), obj);
- }
- catch (IllegalAccessException e)
- {
- throw new SQLException(e.getMessage());
- }
- catch (IllegalArgumentException e)
- {
- throw new SQLException(e.getMessage());
- }
- catch (InvocationTargetException e)
- {
- throw new SQLException(e.getMessage());
- }
- }
- return retval;
- }
+ protected static final int STATEMENT_CACHE_SIZE = 100;
+ protected static final int ROW_PREFETCH_SIZE = 100;
+
+ // 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_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_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 JdbcType BASE_CLOB = JdbcTypesHelper.getJdbcTypeByName("clob");
+ protected static final JdbcType BASE_BLOB = JdbcTypesHelper.getJdbcTypeByName("blob");
+
+ /**
+ * Enables Oracle statement caching if supported by the JDBC-driver.
+ * See
+ * {@link "http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/jdbc30/StmtCacheSample/Readme.html"}
+ * @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
+ */
+ public void initializeJdbcConnection(JdbcConnectionDescriptor jcd, Connection conn) throws PlatformException
+ {
+ // Do all the generic initialization in PlatformDefaultImpl first
+ super.initializeJdbcConnection(jcd, conn);
+
+ // Check for OracleConnection-specific statement caching methods
+ final Method methodSetStatementCacheSize;
+ final Method methodSetImplicitCachingEnabled;
+ methodSetStatementCacheSize = ClassHelper.getMethod(conn, "setStatementCacheSize", PARAM_TYPE_INTEGER);
+ methodSetImplicitCachingEnabled = ClassHelper.getMethod(conn, "setImplicitCachingEnabled", PARAM_TYPE_BOOLEAN);
+
+ final boolean statementCachingSupported = methodSetStatementCacheSize != null && methodSetImplicitCachingEnabled != null;
+ if (statementCachingSupported)
+ {
+ try
+ {
+ // Set number of cached statements and enable implicit caching
+ methodSetStatementCacheSize.invoke(conn, PARAM_STATEMENT_CACHE_SIZE);
+ methodSetImplicitCachingEnabled.invoke(conn, PARAM_BOOLEAN_TRUE);
+ }
+ catch (Exception e)
+ {
+ throw new PlatformException(e.getLocalizedMessage(), e);
+ }
+ }
+ }
+
+ /**
+ * Enables Oracle row prefetching if supported.
+ * See http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/advanced/RowPrefetchSample/Readme.html.
+ * This is RDBMS server-to-client prefetching and thus one layer below
+ * the OJB-internal prefetching-to-cache introduced in version 1.0rc5.
+ * @param stmt the statement just created
+ * @throws PlatformException upon JDBC failure
+ */
+ public void afterStatementCreate(java.sql.Statement stmt) throws PlatformException
+ {
+ super.afterStatementCreate(stmt);
+
+ // Check for OracleStatement-specific row prefetching support
+ final Method methodSetRowPrefetch;
+ methodSetRowPrefetch = ClassHelper.getMethod(stmt, "setRowPrefetch", PARAM_TYPE_INTEGER);
+
+ final boolean rowPrefetchingSupported = methodSetRowPrefetch != null;
+ if (rowPrefetchingSupported)
+ {
+ try
+ {
+ // Set number of prefetched rows
+ methodSetRowPrefetch.invoke(stmt, PARAM_ROW_PREFETCH_SIZE);
+ }
+ catch (Exception e)
+ {
+ throw new PlatformException(e.getLocalizedMessage(), e);
+ }
+ }
+ }
+
+ /**
+ * Try Oracle update batching and call setExecuteBatch or revert to
+ * JDBC update batching. See 12-2 Update Batching in the Oracle9i
+ * JDBC Developer's Guide and Reference.
+ * @param stmt the prepared statement to be used for batching
+ * @throws PlatformException upon JDBC failure
+ */
+ 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)
+ {
+ try
+ {
+ // Set number of statements per batch
+ methodSetExecuteBatch.invoke(stmt, PARAM_STATEMENT_BATCH_SIZE);
+ m_batchStatementsInProgress.put(stmt, methodSendBatch);
+ }
+ catch (Exception e)
+ {
+ throw new PlatformException(e.getLocalizedMessage(), e);
+ }
+ }
+ else
+ {
+ super.beforeBatch(stmt);
+ }
+ }
+
+ /**
+ * Try Oracle update batching and call executeUpdate or revert to
+ * JDBC update batching.
+ * @param stmt the statement beeing added to the batch
+ * @throws PlatformException upon JDBC failure
+ */
+ public void addBatch(PreparedStatement stmt) throws PlatformException
+ {
+ // Check for Oracle batching support
+ final boolean statementBatchingSupported = m_batchStatementsInProgress.containsKey(stmt);
+ if (statementBatchingSupported)
+ {
+ try
+ {
+ stmt.executeUpdate();
+ }
+ catch (SQLException e)
+ {
+ throw new PlatformException(e.getLocalizedMessage(), e);
+ }
+ }
+ else
+ {
+ super.addBatch(stmt);
+ }
+ }
+
+ /**
+ * Try Oracle update batching and call sendBatch or revert to
+ * JDBC update batching.
+ * @param stmt the batched prepared statement about to be executed
+ * @return always <code>null</code> if Oracle update batching is used,
+ * since it is impossible to dissolve total row count into distinct
+ * statement counts. If JDBC update batching is used, an int array is
+ * returned containing number of updated rows for each batched statement.
+ * @throws PlatformException upon JDBC failure
+ */
+ public int[] executeBatch(PreparedStatement stmt) throws PlatformException
+ {
+ // Check for Oracle batching support
+ final Method methodSendBatch = (Method) m_batchStatementsInProgress.remove(stmt);
+ final boolean statementBatchingSupported = methodSendBatch != null;
+
+ int[] retval = null;
+ if (statementBatchingSupported)
+ {
+ try
+ {
+ // sendBatch() returns total row count as an Integer
+ methodSendBatch.invoke(stmt, null);
+ }
+ catch (Exception e)
+ {
+ throw new PlatformException(e.getLocalizedMessage(), e);
+ }
+ }
+ else
+ {
+ retval = super.executeBatch(stmt);
+ }
+ return retval;
+ }
+
+ /** @see Platform#setObjectForStatement */
+ public void setObjectForStatement(PreparedStatement ps, int index, Object value, int sqlType) throws SQLException
+ {
+ boolean blobHandlingSupported = false;
+ boolean clobHandlingSupported = false;
+ Method methodSetBlob = null;
+ Method methodSetClob = null;
+
+ // Check for Oracle JDBC-driver LOB-support
+ if (sqlType == Types.CLOB) {
+ try {
+ Class clobClass = ClassHelper.getClass("oracle.sql.CLOB", false);
+ methodSetClob = ClassHelper.getMethod(ps, "setCLOB", new Class[]{Integer.TYPE, clobClass});
+ clobHandlingSupported = methodSetClob != null;
+ } catch (Exception ignore) {
+ }
+ }
+ else if (sqlType == Types.BLOB) {
+ try {
+ Class blobClass = ClassHelper.getClass("oracle.sql.BLOB", false);
+ methodSetBlob = ClassHelper.getMethod(ps, "setBLOB", new Class[]{Integer.TYPE, blobClass});
+ blobHandlingSupported = methodSetBlob != null;
+ } catch (Exception ignore) {
+ }
+ }
+
+ // Type-specific Oracle conversions
+ if (((sqlType == Types.VARBINARY) || (sqlType == Types.LONGVARBINARY)) && (value instanceof byte[]))
+ {
+ byte buf[] = (byte[]) value;
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(buf);
+ 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 && clobHandlingSupported && value instanceof String)
+ {
+ // TODO: If using Oracle update batching with the thin driver, throw exception on 4k limit
+ try
+ {
+ Object clob = Oracle9iLobHandler.createCLOBFromString(ps.getConnection(), (String) value);
+ methodSetClob.invoke(ps, new Object[]{new Integer(index), clob});
+ }
+ catch (Exception e)
+ {
+ throw new SQLException(e.getLocalizedMessage());
+ }
+ }
+ else if (sqlType == Types.BLOB && blobHandlingSupported && value instanceof byte[])
+ {
+ // TODO: If using Oracle update batching with the thin driver, throw exception on 2k limit
+ try
+ {
+ Object blob = Oracle9iLobHandler.createBLOBFromByteArray(ps.getConnection(), (byte[]) value);
+ methodSetBlob.invoke(ps, new Object[]{new Integer(index), blob});
+ }
+ catch (Exception e)
+ {
+ throw new SQLException(e.getLocalizedMessage());
+ }
+ }
+ else
+ {
+ // Fall-through to superclass
+ super.setObjectForStatement(ps, index, value, sqlType);
+ }
+ }
+
}
1.20 +1 -7 db-ojb/src/java/org/apache/ojb/broker/platforms/Platform.java
Index: Platform.java
===================================================================
RCS file: /home/cvs//db-ojb/src/java/org/apache/ojb/broker/platforms/Platform.java,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -r1.19 -r1.20
--- Platform.java 23 Nov 2003 10:29:23 -0000 1.19
+++ Platform.java 24 Jan 2004 00:08:58 -0000 1.20
@@ -214,11 +214,6 @@
*/
public String getLastInsertIdentityQuery(String tableName);
- /**
- * Oracle has funky clobs.
- */
- public Object getClob(ResultSet rs, int jdbcType, String columnId) throws SQLException;
-
/**
* Answer true if LIMIT or equivalent is supported
* <b> SQL-Paging is not yet supported </b>
@@ -229,7 +224,6 @@
/**
* Add the LIMIT or equivalent to the SQL
* <b> SQL-Paging is not yet supported </b>
- * @return
*/
public void addPagingSql(StringBuffer anSqlString);
1.5 +96 -66 db-ojb/src/java/org/apache/ojb/broker/platforms/ClobWrapper.java
Index: ClobWrapper.java
===================================================================
RCS file: /home/cvs//db-ojb/src/java/org/apache/ojb/broker/platforms/ClobWrapper.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- ClobWrapper.java 8 Dec 2003 10:18:16 -0000 1.4
+++ ClobWrapper.java 24 Jan 2004 00:08:58 -0000 1.5
@@ -54,55 +54,54 @@
* <http://www.apache.org/>.
*/
+import org.apache.commons.lang.BooleanUtils;
+import org.apache.ojb.broker.util.ClassHelper;
+
import java.io.Reader;
import java.io.Writer;
-import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.SQLException;
-import org.apache.commons.lang.BooleanUtils;
-import org.apache.ojb.broker.util.logging.LoggerFactory;
-
/**
- * Wraps the Oracle CLOB type and makes it accessible via reflection without having to import the Oracle Classes.
- * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird<a>
+ * Wraps the Oracle CLOB type and makes it accessible via reflection
+ * without having to import the Oracle Classes.
+ * @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird</a>
+ * @author <a href="martin.kalen@curalia.se">Martin Kalén</a>
+ * @version CVS $Id$
*/
-
public class ClobWrapper
{
- public static final int MAX_CHUNK_SIZE = 32768;
- public static final int DURATION_SESSION = 10;
- public static final int DURATION_CALL = 12;
- static final int OLD_WRONG_DURATION_SESSION = 1;
- static final int OLD_WRONG_DURATION_CALL = 2;
- public static final int MODE_READONLY = 0;
- public static final int MODE_READWRITE = 1;
- private Object m_clob = null;
-
- // methods
- private static Method createTemporary = null;
- private static Method open = null;
- private static Method isOpen = null;
- private static Method getCharacterStream = null;
- private static Method getCharacterOutputStream = null;
- private static Method getBufferSize = null;
- private static Method close = null;
- private static Method trim = null;
- private static Method freeTemporary = null;
+ protected Object m_clob;
+
+ // Fields - values must be looked up via reflection not be compile-time Oracle-version dependent
+ protected static Field durationSession;
+ protected static Field durationCall;
+ protected static Field modeReadOnly;
+ protected static Field modeReadWrite;
+
+ // Methods
+ protected static Method createTemporary;
+ protected static Method freeTemporary;
+ protected static Method open;
+ protected static Method isOpen;
+ protected static Method getCharacterStream;
+ protected static Method getCharacterOutputStream;
+ protected static Method getBufferSize;
+ protected static Method close;
+ protected static Method trim;
/**
- * initialize all the methods
+ * Initialize all methods and fields via reflection.
*/
static
{
try
{
- /**
- * try to find it
- */
- Class clobClass = Class.forName("oracle.sql.CLOB");
- createTemporary = clobClass.getDeclaredMethod("createTemporary", new Class[]{Connection.class, Boolean.TYPE, Integer.TYPE});
+ Class clobClass = ClassHelper.getClass("oracle.sql.CLOB", false);
+ createTemporary = clobClass.getMethod("createTemporary", new Class[]{Connection.class, Boolean.TYPE, Integer.TYPE});
+ freeTemporary = clobClass.getMethod("freeTemporary", null);
open = clobClass.getMethod("open", new Class[]{Integer.TYPE});
isOpen = clobClass.getMethod("isOpen", null);
getCharacterStream = clobClass.getMethod("getCharacterStream", null);
@@ -110,19 +109,14 @@
getBufferSize = clobClass.getMethod("getBufferSize", null);
close = clobClass.getMethod("close", null);
trim = clobClass.getMethod("trim", new Class[]{Long.TYPE});
- freeTemporary = clobClass.getMethod("freeTemporary", null);
- }
- catch (NoSuchMethodException e)
- {
- System.out.println(e.getMessage());
- }
- catch (SecurityException e)
- {
- System.out.println(e.getMessage());
+
+ durationSession = ClassHelper.getField(clobClass, "DURATION_SESSION");
+ durationCall = ClassHelper.getField(clobClass, "DURATION_CALL");
+ modeReadOnly = ClassHelper.getField(clobClass, "MODE_READONLY");
+ modeReadWrite = ClassHelper.getField(clobClass, "MODE_READWRITE");
}
- catch (ClassNotFoundException e)
+ catch (Exception ingore)
{
- System.out.println(e.getMessage());
}
}
@@ -136,33 +130,45 @@
m_clob = clob;
}
- public static ClobWrapper createTemporary(Connection conn, boolean b, int i)
+ protected static int staticIntFieldValue(Field field) {
+ int value = 0;
+ try {
+ value = field.getInt(null);
+ } catch (Exception ignore) {
+ value = -1;
+ }
+ return value;
+ }
+
+ public static int getDurationSessionValue() {
+ return staticIntFieldValue(durationSession);
+ }
+
+ public static int getDurationCallValue() {
+ return staticIntFieldValue(durationCall);
+ }
+
+ public static int getModeReadOnlyValue() {
+ return staticIntFieldValue(modeReadOnly);
+ }
+
+ public static int getModeReadWriteValue() {
+ return staticIntFieldValue(modeReadWrite);
+ }
+
+ public static ClobWrapper createTemporary(Connection conn, boolean b, int i) throws Exception
{
ClobWrapper retval = new ClobWrapper();
- try
- {
- /**
- * passing null to the invoke means this is a static method.
- */
- retval.m_clob = createTemporary.invoke(null, new Object[]{conn, BooleanUtils.toBooleanObject(b), new Integer(i)});
- }
- catch (IllegalAccessException e)
- {
- LoggerFactory.getDefaultLogger().error(e);
- }
- catch (IllegalArgumentException e)
- {
- LoggerFactory.getDefaultLogger().error(e);
- }
- catch (InvocationTargetException e)
- {
- LoggerFactory.getDefaultLogger().error(e);
- }
+ // Passing null to invoke static method
+ retval.setClob(createTemporary.invoke(null, new Object[]{conn, BooleanUtils.toBooleanObject(b), new Integer(i)}));
return retval;
}
public void open(int i) throws SQLException
{
+ if (m_clob == null) {
+ return;
+ }
try
{
open.invoke(m_clob, new Object[]{new Integer(i)});
@@ -175,20 +181,29 @@
public boolean isOpen() throws SQLException
{
- Boolean retval = null;
+ if (m_clob == null) {
+ return false;
+ }
+ boolean open = false;
try
{
- retval = (Boolean) isOpen.invoke(m_clob, null);
+ Boolean retval = (Boolean) isOpen.invoke(m_clob, null);
+ if (retval != null) {
+ open = retval.booleanValue();
+ }
}
catch (Throwable e)
{
throw new SQLException(e.getMessage());
}
- return retval.booleanValue();
+ return open;
}
public Reader getCharacterStream() throws SQLException
{
+ if (m_clob == null) {
+ return null;
+ }
Reader retval = null;
try
{
@@ -203,6 +218,9 @@
public Writer getCharacterOutputStream() throws SQLException
{
+ if (m_clob == null) {
+ return null;
+ }
Writer retval = null;
try
{
@@ -217,6 +235,9 @@
public int getBufferSize() throws SQLException
{
+ if (m_clob == null) {
+ return 0;
+ }
Integer retval = null;
try
{
@@ -231,6 +252,9 @@
public void close() throws SQLException
{
+ if (m_clob == null) {
+ return;
+ }
try
{
close.invoke(m_clob, null);
@@ -244,6 +268,9 @@
public void trim(long l) throws SQLException
{
+ if (m_clob == null) {
+ return;
+ }
try
{
trim.invoke(m_clob, new Object[]{new Long(l)});
@@ -257,6 +284,9 @@
public void freeTemporary() throws SQLException
{
+ if (m_clob == null) {
+ return;
+ }
try
{
freeTemporary.invoke(m_clob, null);
---------------------------------------------------------------------
To unsubscribe, e-mail: ojb-dev-unsubscribe@db.apache.org
For additional commands, e-mail: ojb-dev-help@db.apache.org