You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by mt...@apache.org on 2010/10/03 22:46:38 UTC

svn commit: r1004043 - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/ openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xmlmapping/query/ openjpa-project/src/doc/manual/

Author: mtylenda
Date: Sun Oct  3 20:46:38 2010
New Revision: 1004043

URL: http://svn.apache.org/viewvc?rev=1004043&view=rev
Log:
OPENJPA-1691: Support XML strings longer than 4000 bytes on Oracle.

Modified:
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/RowImpl.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xmlmapping/query/TestXMLCustomerOrder.java
    openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml
    openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_mapping.xml
    openjpa/trunk/openjpa-project/src/doc/manual/supported_databases.xml

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java?rev=1004043&r1=1004042&r2=1004043&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java Sun Oct  3 20:46:38 2010
@@ -355,6 +355,7 @@ public class DBDictionary
     protected Log log = null;
     protected boolean connected = false;
     protected boolean isJDBC3 = false;
+    protected boolean isJDBC4 = false;
     protected final Set<String> reservedWordSet = new HashSet<String>();
     // reservedWordSet subset that CANNOT be used as valid column names
     // (i.e., without surrounding them with double-quotes)
@@ -423,9 +424,11 @@ public class DBDictionary
             try {
                 metaData = conn.getMetaData();
                 try {
-                    // JDBC3-only method, so it might throw a 
+                    // JDBC3-only method, so it might throw an
                     // AbstractMethodError
-                    isJDBC3 = metaData.getJDBCMajorVersion() >= 3;
+                    int JDBCMajorVersion = metaData.getJDBCMajorVersion();
+                    isJDBC3 = JDBCMajorVersion >= 3;
+                    isJDBC4 = JDBCMajorVersion >= 4;
                 } catch (Throwable t) {
                     // ignore if not JDBC3
                 }
@@ -5437,4 +5440,16 @@ public class DBDictionary
         }
         return conversionKey;
     }
+
+    /**
+     * 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 "?";
+    }
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java?rev=1004043&r1=1004042&r2=1004043&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/OracleDictionary.java Sun Oct  3 20:46:38 2010
@@ -19,6 +19,7 @@
 package org.apache.openjpa.jdbc.sql;
 
 import java.io.InputStream;
+import java.io.StringReader;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.security.AccessController;
@@ -105,6 +106,11 @@ public class OracleDictionary
      */
     public boolean useSetFormOfUseForUnicode = true;
 
+    /**
+     * Type constructor for XML column, used in INSERT and UPDATE statements.
+     */
+    public String xmlTypeMarker = "XMLType(?)";
+
     // some oracle drivers have problems with select for update; warn the
     // first time locking is attempted
     private boolean _checkedUpdateBug = false;
@@ -237,9 +243,9 @@ public class OracleDictionary
                     timestampTypeName = "DATE"; // added oracle 9
                     supportsXMLColumn = false;
                 }
-                    // select of an xml column requires ".getStringVal()"
-                    // suffix. eg. t0.xmlcol.getStringVal()
-                    getStringVal = ".getStringVal()";
+                // select of an xml column requires ".getStringVal()" (for values <= 4000 bytes only)
+                // or ".getClobVal()" suffix. eg. t0.xmlcol.getClobVal()
+                getStringVal = ".getClobVal()";
             } else if (metadataClassName.startsWith("com.ddtek.")
                 || url.indexOf("jdbc:datadirect:oracle:") != -1
                 || "Oracle".equals(driverName)) {
@@ -249,6 +255,7 @@ public class OracleDictionary
                 driverVendor = VENDOR_OTHER;
         }
         cacheDriverBehavior(driverVendor);
+        guessJDBCVersion(conn);
     }
 
     /**
@@ -560,6 +567,16 @@ public class OracleDictionary
     public void setClobString(PreparedStatement stmnt, int idx, String val,
         Column col)
         throws SQLException {
+        if (col.isXML()) {
+            if (isJDBC4) {
+                // This JDBC 4 method handles values longer than 4000 bytes.
+                stmnt.setClob(idx, new StringReader(val), val.length());
+            } else {
+                // This method is limited to 4000 bytes.
+                setCharacterStream(stmnt, idx, new StringReader(val), val.length(), col);
+            }
+            return;
+        }
         if (!useSetStringForClobs && val.length() == 0)
             stmnt.setClob(idx, getEmptyClob());
         else {
@@ -1310,4 +1327,36 @@ public class OracleDictionary
             row.setBlob(col, getEmptyBlob());
         }
     }
+
+    /**
+     * 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 (col.isXML() && val != RowImpl.NULL) {
+            return xmlTypeMarker;
+        }
+        return super.getMarkerForInsertUpdate(col, val);
+    }
+
+    /**
+     * 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 void guessJDBCVersion(Connection conn) {
+        if (_driverBehavior != BEHAVE_ORACLE) {
+            return;
+        }
+        isJDBC4 = true;
+        try {
+            conn.getClientInfo();   // Try to call a JDBC 4 method.
+        } catch (SQLException e) {
+            // OK, we are on JDBC 4.
+        } catch (Throwable t) {
+            // Most likely an AbstractMethodError from JDBC 3 driver.
+            isJDBC4 = false;
+        }
+    }
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/RowImpl.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/RowImpl.java?rev=1004043&r1=1004042&r2=1004043&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/RowImpl.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/RowImpl.java Sun Oct  3 20:46:38 2010
@@ -758,8 +758,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;
         }
 
@@ -789,7 +791,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/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xmlmapping/query/TestXMLCustomerOrder.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xmlmapping/query/TestXMLCustomerOrder.java?rev=1004043&r1=1004042&r2=1004043&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xmlmapping/query/TestXMLCustomerOrder.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/xmlmapping/query/TestXMLCustomerOrder.java Sun Oct  3 20:46:38 2010
@@ -26,6 +26,7 @@ import javax.persistence.Query;
 
 import junit.textui.TestRunner;
 
+import org.apache.commons.lang.StringUtils;
 import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
 import org.apache.openjpa.jdbc.sql.DBDictionary;
 import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
@@ -64,9 +65,13 @@ public class TestXMLCustomerOrder
     private static final double ORDER_2_AMOUNT = 1000;
     private static final boolean ORDER_2_DELIVERED = false;
 
+    private static boolean firstTestExecuted;
+
     public void setUp() {
+        Object clearOrDropTables = (firstTestExecuted) ? CLEAR_TABLES : DROP_TABLES;
+        firstTestExecuted = true;
         setUp(Customer.class, Customer.CustomerKey.class, Order.class,
-            EAddress.class, DROP_TABLES);  // test create table DDL for XML column
+            EAddress.class, clearOrDropTables);  // test create table DDL for XML column but only once to save time.
 
         // skip test if dictionary has no support for XML column type
         setTestsDisabled(!dictionarySupportsXMLColumn());
@@ -347,10 +352,11 @@ public class TestXMLCustomerOrder
     private USAAddress createUSAAddress(String name) {
         USAAddress address = new ObjectFactory().createUSAAddress();
         address.setName(name);
-        address.getStreet().add("12500 Monterey");
+        // Use a 4000-byte value so the entire XML string is longer than 4000 bytes - ensure Oracle handles this.
+        address.getStreet().add(StringUtils.repeat("12500 Mont", 400));
         address.setCity("San Jose");
         address.setState("CA");
-        address.setZIP(new Integer("95141"));
+        address.setZIP(95141);
         return address;
     }
 

Modified: openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml?rev=1004043&r1=1004042&r2=1004043&view=diff
==============================================================================
--- openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml (original)
+++ openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml Sun Oct  3 20:46:38 2010
@@ -1490,8 +1490,8 @@ to <literal>"FOR UPDATE"</literal>.
 <literal>GetStringVal</literal>: 
 A special function to return the value of an XML 
 column in a select statement.  For example, Oracle uses 
-<literal>".getStringVal()"</literal>, as in, 
-<literal>"select t0.xmlcol.getStringVal() from xmltab t0"</literal>.
+<literal>".getClobVal()"</literal>, as in 
+<literal>"SELECT t0.xmlcol.getClobVal() FROM xmltab t0"</literal>.
 Defaults to the empty string.
                     </para>
                 </listitem>
@@ -1505,14 +1505,6 @@ Defaults to the empty string.
                             InClauseLimit
                         </secondary>
                     </indexterm>
-                    <indexterm>
-                        <primary>
-                            JDBC
-                        </primary>
-                        <secondary>
-                            GetStringVal
-                        </secondary>
-                    </indexterm>
 <literal>InClauseLimit</literal>: 
 The maximum number of elements in an <literal>IN</literal> clause.  OpenJPA
 works around cases where the limit is exceeded.  Defaults to -1 meaning 

Modified: openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_mapping.xml
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_mapping.xml?rev=1004043&r1=1004042&r2=1004043&view=diff
==============================================================================
--- openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_mapping.xml (original)
+++ openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_mapping.xml Sun Oct  3 20:46:38 2010
@@ -2633,6 +2633,10 @@ SQL Server 2005
                   </listitem>
               </itemizedlist>
               <para>
+See <xref linkend="supported_databases"/> for possible database-specific
+restrictions.
+              </para>
+              <para>
 Annotate the entity property using the XMLValueHandler strategy:
 		      </para>
 <programlisting>

Modified: openjpa/trunk/openjpa-project/src/doc/manual/supported_databases.xml
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-project/src/doc/manual/supported_databases.xml?rev=1004043&r1=1004042&r2=1004043&view=diff
==============================================================================
--- openjpa/trunk/openjpa-project/src/doc/manual/supported_databases.xml (original)
+++ openjpa/trunk/openjpa-project/src/doc/manual/supported_databases.xml Sun Oct  3 20:46:38 2010
@@ -1206,6 +1206,14 @@ openjpa.ConnectionRetainMode: always
             </example>
                     </para>
                 </listitem>
+                <listitem>
+                    <para>
+Mapping persistent attributes to <link linkend="ref_guide_xmlmapping">XML columns</link> requires
+a JDBC 4 compliant driver if XML strings are longer than 4000 bytes, as counted in database.
+Otherwise an <literal>ORA-01461: can bind a LONG value only for insert into a LONG column</literal>
+error may result.
+                    </para>
+                </listitem>
             </itemizedlist>
         </section>
     </section>