mirror of https://github.com/apache/openjpa.git
OPENJPA-1691: Support XML strings longer than 4000 bytes on Oracle.
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@1004043 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
8fa232c933
commit
5a98879567
|
@ -355,6 +355,7 @@ public class DBDictionary
|
||||||
protected Log log = null;
|
protected Log log = null;
|
||||||
protected boolean connected = false;
|
protected boolean connected = false;
|
||||||
protected boolean isJDBC3 = false;
|
protected boolean isJDBC3 = false;
|
||||||
|
protected boolean isJDBC4 = false;
|
||||||
protected final Set<String> reservedWordSet = new HashSet<String>();
|
protected final Set<String> reservedWordSet = new HashSet<String>();
|
||||||
// reservedWordSet subset that CANNOT be used as valid column names
|
// reservedWordSet subset that CANNOT be used as valid column names
|
||||||
// (i.e., without surrounding them with double-quotes)
|
// (i.e., without surrounding them with double-quotes)
|
||||||
|
@ -423,9 +424,11 @@ public class DBDictionary
|
||||||
try {
|
try {
|
||||||
metaData = conn.getMetaData();
|
metaData = conn.getMetaData();
|
||||||
try {
|
try {
|
||||||
// JDBC3-only method, so it might throw a
|
// JDBC3-only method, so it might throw an
|
||||||
// AbstractMethodError
|
// AbstractMethodError
|
||||||
isJDBC3 = metaData.getJDBCMajorVersion() >= 3;
|
int JDBCMajorVersion = metaData.getJDBCMajorVersion();
|
||||||
|
isJDBC3 = JDBCMajorVersion >= 3;
|
||||||
|
isJDBC4 = JDBCMajorVersion >= 4;
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
// ignore if not JDBC3
|
// ignore if not JDBC3
|
||||||
}
|
}
|
||||||
|
@ -5437,4 +5440,16 @@ public class DBDictionary
|
||||||
}
|
}
|
||||||
return conversionKey;
|
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 "?";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
package org.apache.openjpa.jdbc.sql;
|
package org.apache.openjpa.jdbc.sql;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.StringReader;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
|
@ -105,6 +106,11 @@ public class OracleDictionary
|
||||||
*/
|
*/
|
||||||
public boolean useSetFormOfUseForUnicode = true;
|
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
|
// some oracle drivers have problems with select for update; warn the
|
||||||
// first time locking is attempted
|
// first time locking is attempted
|
||||||
private boolean _checkedUpdateBug = false;
|
private boolean _checkedUpdateBug = false;
|
||||||
|
@ -237,9 +243,9 @@ public class OracleDictionary
|
||||||
timestampTypeName = "DATE"; // added oracle 9
|
timestampTypeName = "DATE"; // added oracle 9
|
||||||
supportsXMLColumn = false;
|
supportsXMLColumn = false;
|
||||||
}
|
}
|
||||||
// select of an xml column requires ".getStringVal()"
|
// select of an xml column requires ".getStringVal()" (for values <= 4000 bytes only)
|
||||||
// suffix. eg. t0.xmlcol.getStringVal()
|
// or ".getClobVal()" suffix. eg. t0.xmlcol.getClobVal()
|
||||||
getStringVal = ".getStringVal()";
|
getStringVal = ".getClobVal()";
|
||||||
} else if (metadataClassName.startsWith("com.ddtek.")
|
} else if (metadataClassName.startsWith("com.ddtek.")
|
||||||
|| url.indexOf("jdbc:datadirect:oracle:") != -1
|
|| url.indexOf("jdbc:datadirect:oracle:") != -1
|
||||||
|| "Oracle".equals(driverName)) {
|
|| "Oracle".equals(driverName)) {
|
||||||
|
@ -249,6 +255,7 @@ public class OracleDictionary
|
||||||
driverVendor = VENDOR_OTHER;
|
driverVendor = VENDOR_OTHER;
|
||||||
}
|
}
|
||||||
cacheDriverBehavior(driverVendor);
|
cacheDriverBehavior(driverVendor);
|
||||||
|
guessJDBCVersion(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -560,6 +567,16 @@ public class OracleDictionary
|
||||||
public void setClobString(PreparedStatement stmnt, int idx, String val,
|
public void setClobString(PreparedStatement stmnt, int idx, String val,
|
||||||
Column col)
|
Column col)
|
||||||
throws SQLException {
|
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)
|
if (!useSetStringForClobs && val.length() == 0)
|
||||||
stmnt.setClob(idx, getEmptyClob());
|
stmnt.setClob(idx, getEmptyClob());
|
||||||
else {
|
else {
|
||||||
|
@ -1310,4 +1327,36 @@ public class OracleDictionary
|
||||||
row.setBlob(col, getEmptyBlob());
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -758,8 +758,10 @@ public class RowImpl
|
||||||
buf.append(dict.getColumnDBName(_cols[i]));
|
buf.append(dict.getColumnDBName(_cols[i]));
|
||||||
if (_types[i] == RAW)
|
if (_types[i] == RAW)
|
||||||
buf.append(" = ").append(_vals[i]);
|
buf.append(" = ").append(_vals[i]);
|
||||||
else
|
else {
|
||||||
buf.append(" = ?");
|
buf.append(" = ");
|
||||||
|
buf.append(dict.getMarkerForInsertUpdate(_cols[i], _vals[i]));
|
||||||
|
}
|
||||||
hasVal = true;
|
hasVal = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -789,7 +791,7 @@ public class RowImpl
|
||||||
if (_types[i] == RAW)
|
if (_types[i] == RAW)
|
||||||
vals.append(_vals[i]);
|
vals.append(_vals[i]);
|
||||||
else
|
else
|
||||||
vals.append("?");
|
vals.append(dict.getMarkerForInsertUpdate(_cols[i], _vals[i]));
|
||||||
hasVal = true;
|
hasVal = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import javax.persistence.Query;
|
||||||
|
|
||||||
import junit.textui.TestRunner;
|
import junit.textui.TestRunner;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
|
import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
|
||||||
import org.apache.openjpa.jdbc.sql.DBDictionary;
|
import org.apache.openjpa.jdbc.sql.DBDictionary;
|
||||||
import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
|
import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
|
||||||
|
@ -64,9 +65,13 @@ public class TestXMLCustomerOrder
|
||||||
private static final double ORDER_2_AMOUNT = 1000;
|
private static final double ORDER_2_AMOUNT = 1000;
|
||||||
private static final boolean ORDER_2_DELIVERED = false;
|
private static final boolean ORDER_2_DELIVERED = false;
|
||||||
|
|
||||||
|
private static boolean firstTestExecuted;
|
||||||
|
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
Object clearOrDropTables = (firstTestExecuted) ? CLEAR_TABLES : DROP_TABLES;
|
||||||
|
firstTestExecuted = true;
|
||||||
setUp(Customer.class, Customer.CustomerKey.class, Order.class,
|
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
|
// skip test if dictionary has no support for XML column type
|
||||||
setTestsDisabled(!dictionarySupportsXMLColumn());
|
setTestsDisabled(!dictionarySupportsXMLColumn());
|
||||||
|
@ -347,10 +352,11 @@ public class TestXMLCustomerOrder
|
||||||
private USAAddress createUSAAddress(String name) {
|
private USAAddress createUSAAddress(String name) {
|
||||||
USAAddress address = new ObjectFactory().createUSAAddress();
|
USAAddress address = new ObjectFactory().createUSAAddress();
|
||||||
address.setName(name);
|
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.setCity("San Jose");
|
||||||
address.setState("CA");
|
address.setState("CA");
|
||||||
address.setZIP(new Integer("95141"));
|
address.setZIP(95141);
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1490,8 +1490,8 @@ to <literal>"FOR UPDATE"</literal>.
|
||||||
<literal>GetStringVal</literal>:
|
<literal>GetStringVal</literal>:
|
||||||
A special function to return the value of an XML
|
A special function to return the value of an XML
|
||||||
column in a select statement. For example, Oracle uses
|
column in a select statement. For example, Oracle uses
|
||||||
<literal>".getStringVal()"</literal>, as in,
|
<literal>".getClobVal()"</literal>, as in
|
||||||
<literal>"select t0.xmlcol.getStringVal() from xmltab t0"</literal>.
|
<literal>"SELECT t0.xmlcol.getClobVal() FROM xmltab t0"</literal>.
|
||||||
Defaults to the empty string.
|
Defaults to the empty string.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
@ -1505,14 +1505,6 @@ Defaults to the empty string.
|
||||||
InClauseLimit
|
InClauseLimit
|
||||||
</secondary>
|
</secondary>
|
||||||
</indexterm>
|
</indexterm>
|
||||||
<indexterm>
|
|
||||||
<primary>
|
|
||||||
JDBC
|
|
||||||
</primary>
|
|
||||||
<secondary>
|
|
||||||
GetStringVal
|
|
||||||
</secondary>
|
|
||||||
</indexterm>
|
|
||||||
<literal>InClauseLimit</literal>:
|
<literal>InClauseLimit</literal>:
|
||||||
The maximum number of elements in an <literal>IN</literal> clause. OpenJPA
|
The maximum number of elements in an <literal>IN</literal> clause. OpenJPA
|
||||||
works around cases where the limit is exceeded. Defaults to -1 meaning
|
works around cases where the limit is exceeded. Defaults to -1 meaning
|
||||||
|
|
|
@ -2633,6 +2633,10 @@ SQL Server 2005
|
||||||
</listitem>
|
</listitem>
|
||||||
</itemizedlist>
|
</itemizedlist>
|
||||||
<para>
|
<para>
|
||||||
|
See <xref linkend="supported_databases"/> for possible database-specific
|
||||||
|
restrictions.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
Annotate the entity property using the XMLValueHandler strategy:
|
Annotate the entity property using the XMLValueHandler strategy:
|
||||||
</para>
|
</para>
|
||||||
<programlisting>
|
<programlisting>
|
||||||
|
|
|
@ -1206,6 +1206,14 @@ openjpa.ConnectionRetainMode: always
|
||||||
</example>
|
</example>
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</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>
|
</itemizedlist>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|
Loading…
Reference in New Issue