You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by ht...@apache.org on 2011/07/09 02:27:18 UTC
svn commit: r1144556 - in /openjpa/branches/2.0.x: ./
openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/
openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/
openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/
openjpa-project/src/doc/ma...
Author: hthomann
Date: Sat Jul 9 00:27:17 2011
New Revision: 1144556
URL: http://svn.apache.org/viewvc?rev=1144556&view=rev
Log:
OPENJPA-1691: Oracle XMLType column failed to insert/update when xml contains more than 4000 characters - applied Jeremy's 2.0.x patch and in addition I added documentation.
Modified:
openjpa/branches/2.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
openjpa/branches/2.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java
openjpa/branches/2.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/RowImpl.java
openjpa/branches/2.0.x/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties
openjpa/branches/2.0.x/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/LoggingConnectionDecorator.java
openjpa/branches/2.0.x/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml
openjpa/branches/2.0.x/pom.xml
Modified: openjpa/branches/2.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
URL: http://svn.apache.org/viewvc/openjpa/branches/2.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java?rev=1144556&r1=1144555&r2=1144556&view=diff
==============================================================================
--- openjpa/branches/2.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java (original)
+++ openjpa/branches/2.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java Sat Jul 9 00:27:17 2011
@@ -5211,6 +5211,18 @@ public class DBDictionary
}
/**
+ * Return parameter marker for INSERT and UPDATE statements.
+ * Usually it is <code>?</code> but some database-specific types might require customization.
+ *
+ * @param col column definition
+ * @param val value to be inserted/updated
+ * @return parameter marker
+ */
+ public String getMarkerForInsertUpdate(Column col, Object val) {
+ return "?";
+ }
+
+ /**
* Validate that the given name is no longer than given maximum length.
* If the given name is indeed longer then raises an UserException with the
* given message key otherwise returns the same name.
Modified: openjpa/branches/2.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java
URL: http://svn.apache.org/viewvc/openjpa/branches/2.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java?rev=1144556&r1=1144555&r2=1144556&view=diff
==============================================================================
--- openjpa/branches/2.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java (original)
+++ openjpa/branches/2.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java Sat Jul 9 00:27:17 2011
@@ -18,6 +18,8 @@
*/
package org.apache.openjpa.jdbc.sql;
+import java.io.Reader;
+import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.AccessController;
@@ -38,7 +40,6 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import org.apache.openjpa.jdbc.identifier.DBIdentifier;
import org.apache.openjpa.jdbc.kernel.JDBCFetchConfiguration;
@@ -52,8 +53,10 @@ import org.apache.openjpa.jdbc.schema.Pr
import org.apache.openjpa.jdbc.schema.Sequence;
import org.apache.openjpa.jdbc.schema.Table;
import org.apache.openjpa.jdbc.schema.ForeignKey.FKMapKey;
+import org.apache.openjpa.lib.jdbc.DelegatingConnection;
import org.apache.openjpa.lib.jdbc.DelegatingDatabaseMetaData;
import org.apache.openjpa.lib.jdbc.DelegatingPreparedStatement;
+import org.apache.openjpa.lib.jdbc.LoggingConnectionDecorator;
import org.apache.openjpa.lib.util.J2DoPrivHelper;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.meta.JavaTypes;
@@ -77,6 +80,14 @@ public class OracleDictionary
private static Blob EMPTY_BLOB = null;
private static Clob EMPTY_CLOB = null;
+ private Method _setClob = null;
+ private Boolean _isJDBC4 = null;
+
+ /**
+ * Type constructor for XML column, used in INSERT and UPDATE statements.
+ */
+ public String xmlTypeMarker = "XMLType(?)";
+
private static final Localizer _loc = Localizer.forPackage
(OracleDictionary.class);
@@ -105,6 +116,14 @@ public class OracleDictionary
*/
public boolean useSetFormOfUseForUnicode = true;
+ /**
+ * If true, OpenJPA will attempt to use a Reader-based JDBC 4.0 method to
+ * set Clob or XML data. This allows XMLType and Clob values larger than 4000 bytes
+ * to be used if OpenJPA is used with a Java 6.0 JRE along with a JDBC 4.0 Oracle
+ * driver (ojdbc6.jar).
+ */
+ public boolean supportsSetClob = false;
+
// some oracle drivers have problems with select for update; warn the
// first time locking is attempted
private boolean _checkedUpdateBug = false;
@@ -239,7 +258,11 @@ public class OracleDictionary
}
// select of an xml column requires ".getStringVal()"
// suffix. eg. t0.xmlcol.getStringVal()
+ if (!supportsSetClob) {
getStringVal = ".getStringVal()";
+ } else {
+ getStringVal = ".getClobVal()";
+ }
} else if (metadataClassName.startsWith("com.ddtek.")
|| url.indexOf("jdbc:datadirect:oracle:") != -1
|| "Oracle".equals(driverName)) {
@@ -569,6 +592,74 @@ public class OracleDictionary
}
@Override
+ public void setClobString(PreparedStatement stmnt, int idx, String val,
+ Column col)
+ throws SQLException {
+ if (!useSetStringForClobs && supportsSetClob) {
+ // If the JRE and underlying driver support JDBC, reflectively call
+ // the JDBC setClob method which supports streams larger than 4000 bytes.
+ if (val != null && supportsJDBC4(stmnt.getConnection())) {
+ if (setClob(stmnt, idx, val)) {
+ return;
+ }
+ }
+ }
+ super.setClobString(stmnt, idx, val, col);
+ }
+
+ /**
+ * This method reflectively calls the JDBC 4 setClob method which takes a
+ * reader as a parameter. This allows support for clobs and XML values larger than 4000 bytes.
+ * @param stmnt
+ * @param idx
+ * @param val
+ * @return
+ */
+ private boolean setClob(PreparedStatement stmnt, int idx, String val) {
+ if (_setClob == null) {
+ try {
+ _setClob = PreparedStatement.class.getMethod(
+ "setClob", int.class, Reader.class, long.class);
+ if (_setClob == null) {
+ return false;
+ }
+ } catch (Throwable t) {
+ log.warn(_loc.get("oracle-set-clob-unsupported"), t);
+ return false;
+ }
+ }
+ try {
+ PreparedStatement inner = stmnt;
+ PreparedStatement outer = stmnt;
+ if (stmnt instanceof DelegatingPreparedStatement) {
+ inner = (PreparedStatement) ((DelegatingPreparedStatement)stmnt).getInnermostDelegate();
+ outer = (PreparedStatement) ((DelegatingPreparedStatement)stmnt).getDelegate();
+ }
+ _setClob.invoke(inner, new Object[] { idx, new StringReader(val), (long)val.length() } );
+ // Direct invocation of setClob on the Oracle driver bypasses OpenJPA's built-in
+ // parameter logging via decorators. Log the parameter directly.
+ LoggingConnectionDecorator.logStatementParameter(outer, idx, "Clob", val);
+ } catch (Throwable t) {
+ log.warn(_loc.get("oracle-set-clob-failed"), t);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Oracle requires special handling of XML column.
+ * Unless the value length is less or equal to 4000 bytes,
+ * the parameter marker must be decorated with type constructor.
+ */
+ @Override
+ public String getMarkerForInsertUpdate(Column col, Object val) {
+ if (supportsSetClob && col.isXML() && val != RowImpl.NULL) {
+ return xmlTypeMarker;
+ }
+ return super.getMarkerForInsertUpdate(col, val);
+ }
+
+ @Override
public String getClobString(ResultSet rs, int column)
throws SQLException {
if (_driverBehavior != BEHAVE_ORACLE)
@@ -1261,6 +1352,44 @@ public class OracleDictionary
return updateSuccessCnt;
}
+ /**
+ * Oracle drivers, at least in versions 10.2.0.4 and 11.2.0.1, incorrectly return a driver major version from
+ * {@link DatabaseMetaData#getJDBCMajorVersion()}.
+ */
+ protected boolean supportsJDBC4(Connection conn) {
+ if (_isJDBC4 != null) {
+ return _isJDBC4;
+ }
+ if (_driverBehavior != BEHAVE_ORACLE) {
+ _isJDBC4 = false;
+ return _isJDBC4;
+ }
+ try {
+ Connection inner = getOracleConnection(conn);
+ Method getClientInfo = Connection.class.getMethod(
+ "getClientInfo", new Class[0]);
+ getClientInfo.invoke(inner, new Object[0]);
+ _isJDBC4 = true;
+ } catch (Throwable t) {
+ if (t instanceof SQLException) {
+ // OK, we are on JDBC 4.
+ _isJDBC4 = true;
+ } else {
+ _isJDBC4 = false;
+ }
+ }
+ return _isJDBC4;
+ }
+
+ private Connection getOracleConnection(Connection conn) throws Exception {
+ Connection orclConn = conn;
+ if (conn instanceof DelegatingConnection) {
+ DelegatingConnection dc = (DelegatingConnection)conn;
+ orclConn = dc.getInnermostDelegate();
+ }
+ return orclConn;
+ }
+
@Override
public boolean isFatalException(int subtype, SQLException ex) {
String errorState = ex.getSQLState();
Modified: openjpa/branches/2.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/RowImpl.java
URL: http://svn.apache.org/viewvc/openjpa/branches/2.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/RowImpl.java?rev=1144556&r1=1144555&r2=1144556&view=diff
==============================================================================
--- openjpa/branches/2.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/RowImpl.java (original)
+++ openjpa/branches/2.0.x/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/RowImpl.java Sat Jul 9 00:27:17 2011
@@ -760,8 +760,10 @@ public class RowImpl
buf.append(dict.getColumnDBName(_cols[i]));
if (_types[i] == RAW)
buf.append(" = ").append(_vals[i]);
- else
- buf.append(" = ?");
+ else {
+ buf.append(" = ");
+ buf.append(dict.getMarkerForInsertUpdate(_cols[i], _vals[i]));
+ }
hasVal = true;
}
@@ -791,7 +793,7 @@ public class RowImpl
if (_types[i] == RAW)
vals.append(_vals[i]);
else
- vals.append("?");
+ vals.append(dict.getMarkerForInsertUpdate(_cols[i], _vals[i]));
hasVal = true;
}
Modified: openjpa/branches/2.0.x/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties
URL: http://svn.apache.org/viewvc/openjpa/branches/2.0.x/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties?rev=1144556&r1=1144555&r2=1144556&view=diff
==============================================================================
--- openjpa/branches/2.0.x/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties (original)
+++ openjpa/branches/2.0.x/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties Sat Jul 9 00:27:17 2011
@@ -216,4 +216,9 @@ sequencesql-override: Going to override
the property. This will allow openJPA to detect a difference between the DB2 default \
string and the string set in the property and will further allow openJPA to use the \
string defined by the property rather than the default string for DB2.
-
+oracle-set-clob-unsupported: The JRE or JDBC level in use does not support the function \
+ necessary to support storing large Clob or XML values. An alternate method will be used \
+ to store the data, but this may result in failure if the data is larger than 4000 bytes.
+oracle-set-clob-failed: Invocation of methods to support storing large Clob or XML values \
+ resulted in an error. An alternate method will be used to store the data, but this may \
+ result in failure if the data is larger than 4000 bytes.
Modified: openjpa/branches/2.0.x/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/LoggingConnectionDecorator.java
URL: http://svn.apache.org/viewvc/openjpa/branches/2.0.x/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/LoggingConnectionDecorator.java?rev=1144556&r1=1144555&r2=1144556&view=diff
==============================================================================
--- openjpa/branches/2.0.x/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/LoggingConnectionDecorator.java (original)
+++ openjpa/branches/2.0.x/openjpa-lib/src/main/java/org/apache/openjpa/lib/jdbc/LoggingConnectionDecorator.java Sat Jul 9 00:27:17 2011
@@ -278,6 +278,17 @@ public class LoggingConnectionDecorator
}
/**
+ * Allows direct recording of a statement parameter for logging purposes,
+ * provided the prepared statement implements LoggingPreparedStatement.
+ */
+ public static void logStatementParameter(PreparedStatement stmnt, int index, String type, Object val) {
+ if (stmnt instanceof LoggingPreparedStatement) {
+ LoggingPreparedStatement lcstmnt = (LoggingPreparedStatement) stmnt;
+ lcstmnt.setLogParameter(index, type, val);
+ }
+ }
+
+ /**
* Interface that allows customization of what to do when
* {@link SQLWarning}s occur.
*/
@@ -1477,7 +1488,7 @@ public class LoggingConnectionDecorator
setLogParameter(index, "(short) " + val);
}
- private void setLogParameter(int index, String type, Object val) {
+ protected void setLogParameter(int index, String type, Object val) {
if (shouldTrackParameters())
setLogParameter(index, "(" + type + ") " + val);
}
Modified: openjpa/branches/2.0.x/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml
URL: http://svn.apache.org/viewvc/openjpa/branches/2.0.x/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml?rev=1144556&r1=1144555&r2=1144556&view=diff
==============================================================================
--- openjpa/branches/2.0.x/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml (original)
+++ openjpa/branches/2.0.x/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml Sat Jul 9 00:27:17 2011
@@ -3756,6 +3756,35 @@ on database configuration, e.g. encoding
CLOB to persist with the embedded method. Defaults to 4000 characters.
</para>
</listitem>
+ <listitem id="OracleDictionary.SupportsSetClob">
+ <para>
+<literal>SupportsSetClob</literal>: If true, OpenJPA will attempt to use a
+Reader-based JDBC 4.0 method to set Clob or XML data. This allows XMLType
+and Clob values larger than 4000 bytes to be used if OpenJPA is used
+with a Java 6.0 JRE along with a JDBC 4.0 Oracle driver (e.g. ojdbc6.jar).
+It is expected that this property will be used in conjunction with
+<literal>XMLValueHandler</literal>, see <xref linkend="ref_guide_xmlmapping"/>.
+That is, it is expected that the XML field in question will be annotated with
+the <literal>XMLValueHandler</literal> annotation. This allows a user to
+indicate that OpenJPA should use JAXB class metadata to perform the to/from
+database value retrieval and storage. In addition, using this strategy
+indicates there is an XMLType used to store the value in the database. However,
+it is possible for a user to use the <literal>SupportsSetClob</literal> and
+managing their own XML string, mapped to a string field. In this case,
+additional actions need to be taken. First, the XML field will need to be
+annotated with the <literal>Lob</literal> annotation. Second, the
+<literal>MaxEmbeddedClobSize</literal> dictionary property will need to be
+set to -1. Finally, a user may need to set the
+<literal>openjpa.jdbc.SchemaFactory</literal> property to <literal>native</literal>,
+see <xref linkend="ref_guide_schema_info_factory"/>.
+This property is necessary to allow OpenJPA to detect the XML column type. While
+a user may have an XMLType specified in the column definition, OpenJPA cannot count
+on this data to detect whether the column is really an XML column in the
+database. (The table could have been created manually with separate DDL instead
+of using OpenJPA's mapping tool). By enabling OpenJPA's native schema factory
+the database column type can be detected as XMLType.
+ </para>
+ </listitem>
<listitem id="OracleDictionary.UseSetFormOfUseForUnicode">
<para>
<literal>UseSetFormOfUseForUnicode</literal>: Prior to Oracle 10i, statements
Modified: openjpa/branches/2.0.x/pom.xml
URL: http://svn.apache.org/viewvc/openjpa/branches/2.0.x/pom.xml?rev=1144556&r1=1144555&r2=1144556&view=diff
==============================================================================
--- openjpa/branches/2.0.x/pom.xml (original)
+++ openjpa/branches/2.0.x/pom.xml Sat Jul 9 00:27:17 2011
@@ -50,7 +50,7 @@
<site.deploy.url>scp://people.apache.org/home/${user.name}/public_html/openjpa/${project.version}/staging-site</site.deploy.url>
<!-- the test settings can be overridden my specific profiles -->
<test.jvm.maxpermsize>512m</test.jvm.maxpermsize>
- <test.jvm.maxheapsize>1024m</test.jvm.maxheapsize>
+ <test.jvm.maxheapsize>768m</test.jvm.maxheapsize>
<test.jvm.arguments>-Xmx${test.jvm.maxheapsize} -XX:MaxPermSize=${test.jvm.maxpermsize}</test.jvm.arguments>
<surefire.jvm.args>${test.jvm.arguments}</surefire.jvm.args>
<dbcp.maxActive>10</dbcp.maxActive>