我的方向和要求
- 實體應存儲XML作爲一個字符串(java.lang.String中)
- 數據庫應在XDB.XMLType列XML堅持
- 允許索引和更高效的xpath/ExtractValue/xquery類型查詢
- 鞏固一打左右的部分解決方案,我發現過去一週
- 工作環境
- 的Oracle 11g R2 x64的
- 休眠4.1.x的
- 的Java 1.7.x 64
- 的Windows 7專業版64位
分步解決方案
1步:找到xmlparserv2。罐(〜1350KB)
此jar需要編譯步驟2中,並且被包括在Oracle安裝位置: %ORACLE_11G_HOME%/ LIB/xmlparserv2.jar
步驟1.5:查找xdb6.jar( 〜257kb)
如果您使用的是Oracle 11gR2 11.2.0.2或更高版本,或者存儲爲BINARY XML,這一點至關重要。
爲什麼?
第2步:創建XMLType列
使用Oracle 11g的一個休眠用戶類型和Hibernate 4.x的,這是很容易,它的聲音。
public class HibernateXMLType implements UserType, Serializable {
static Logger logger = Logger.getLogger(HibernateXMLType.class);
private static final long serialVersionUID = 2308230823023l;
private static final Class returnedClass = String.class;
private static final int[] SQL_TYPES = new int[] { oracle.xdb.XMLType._SQL_TYPECODE };
@Override
public int[] sqlTypes() {
return SQL_TYPES;
}
@Override
public Class returnedClass() {
return returnedClass;
}
@Override
public boolean equals(Object x, Object y) throws HibernateException {
if (x == null && y == null) return true;
else if (x == null && y != null) return false;
else return x.equals(y);
}
@Override
public int hashCode(Object x) throws HibernateException {
return x.hashCode();
}
@Override
public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException {
XMLType xmlType = null;
Document doc = null;
String returnValue = null;
try {
//logger.debug("rs type: " + rs.getClass().getName() + ", value: " + rs.getObject(names[0]));
xmlType = (XMLType) rs.getObject(names[0]);
if (xmlType != null) {
returnValue = xmlType.getStringVal();
}
} finally {
if (null != xmlType) {
xmlType.close();
}
}
return returnValue;
}
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException {
if (logger.isTraceEnabled()) {
logger.trace(" nullSafeSet: " + value + ", ps: " + st + ", index: " + index);
}
try {
XMLType xmlType = null;
if (value != null) {
xmlType = XMLType.createXML(getOracleConnection(st.getConnection()), (String)value);
}
st.setObject(index, xmlType);
} catch (Exception e) {
throw new SQLException("Could not convert String to XML for storage: " + (String)value);
}
}
@Override
public Object deepCopy(Object value) throws HibernateException {
if (value == null) {
return null;
} else {
return value;
}
}
@Override
public boolean isMutable() {
return false;
}
@Override
public Serializable disassemble(Object value) throws HibernateException {
try {
return (Serializable)value;
} catch (Exception e) {
throw new HibernateException("Could not disassemble Document to Serializable", e);
}
}
@Override
public Object assemble(Serializable cached, Object owner) throws HibernateException {
try {
return (String)cached;
} catch (Exception e) {
throw new HibernateException("Could not assemble String to Document", e);
}
}
@Override
public Object replace(Object original, Object target, Object owner) throws HibernateException {
return original;
}
private OracleConnection getOracleConnection(Connection conn) throws SQLException {
CLOB tempClob = null;
CallableStatement stmt = null;
try {
stmt = conn.prepareCall("{ call DBMS_LOB.CREATETEMPORARY(?, TRUE)}");
stmt.registerOutParameter(1, java.sql.Types.CLOB);
stmt.execute();
tempClob = (CLOB)stmt.getObject(1);
return tempClob.getConnection();
} finally {
if (stmt != null) {
try {
stmt.close();
} catch (Throwable e) {}
}
}
}
第3步:在實體中註釋字段。
我使用spring/hibernate的註釋,而不是映射文件,但我想象的語法將是類似的。
@Type(type="your.custom.usertype.HibernateXMLType")
@Column(name="attribute_xml", columnDefinition="XDB.XMLTYPE")
private String attributeXml;
步驟4:與在應用程序服務器/ junit的誤差作爲Oracle JAR
的結果處理包括在類路徑來解決編譯%ORACLE_11G_HOME%/ LIB/xmlparserv2.jar(1350KB)後錯誤,你現在得到的運行時錯誤從應用服務器...
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd<Line 43, Column 57>: XML-24509: (Error) Duplicated definition for: 'identifiedType'
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd<Line 61, Column 28>: XML-24509: (Error) Duplicated definition for: 'beans'
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd<Line 168, Column 34>: XML-24509: (Error) Duplicated definition for: 'description'
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd<Line 180, Column 29>: XML-24509: (Error) Duplicated definition for: 'import'
... more ...
爲什麼錯誤?
xmlparserv2.jar使用JAR Services API(服務提供者機制)來更改用於SAXParserFactory,DocumentBuilderFactory和TransformerFactory的默認javax.xml類。
它是如何發生的?
javax.xml.parsers.FactoryFinder通過依次檢查環境變量%JAVA_HOME%/ lib/jaxp.properties,然後查找META-INF/services下的配置文件來查找自定義實現。 classpath,在使用JDK包含的默認實現(com.sun.org。*)之前。
在xmlparserv2.jar裏面存在一個META-INF/services目錄,javax.xml.parsers.FactoryFinder類選擇這個目錄。文件如下:
META-INF/services/javax.xml.parsers.DocumentBuilderFactory (which defines oracle.xml.jaxp.JXDocumentBuilderFactory as the default)
META-INF/services/javax.xml.parsers.SAXParserFactory (which defines oracle.xml.jaxp.JXSAXParserFactory as the default)
META-INF/services/javax.xml.transform.TransformerFactory (which defines oracle.xml.jaxp.JXSAXTransformerFactory as the default)
解決方案?
將所有3切換回來,否則您會看到奇怪的錯誤。
- 的javax.xml.parsers。*修正錯誤,可見
- javax.xml.transform中。* 修復更微妙的XML解析錯誤,在我的情況
QUICK方案來解決應用程序服務器啓動錯誤:JVM參數
要覆蓋xmlparserv2.jar所做的更改,請將以下JVM屬性添加到您的應用程序服務器啓動參數中。 java.xml.parsers.FactoryFinder邏輯將首先檢查環境變量。
-Djavax.xml.parsers.SAXParserFactory=com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl -Djavax.xml.parsers.DocumentBuilderFactory=com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl -Djavax.xml.transform.TransformerFactory=com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
但是,如果您運行使用@RunWith(SpringJUnit4ClassRunner.class)來或類似的測試情況下,你仍然會遇到錯誤。
更好的解決方案應用服務器啓動錯誤和測試用例錯誤? 2個選項
選項1:使用JVM參數爲應用服務器和@BeforeClass報表測試用例
System.setProperty("javax.xml.parsers.DocumentBuilderFactory","com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");
System.setProperty("javax.xml.parsers.SAXParserFactory","com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl");
System.setProperty("javax.xml.transform.TransformerFactory","com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl");
如果你有大量的測試用例,這將成爲痛苦。即使你把它放在超級。
選項2:在編譯/運行時類路徑爲您的項目創建自己的服務提供者定義文件,這將覆蓋包括在xmlparserv2.jar
在Maven的Spring項目,覆蓋xmlparserv2.jar由%PROJECT_HOME%/ src目錄/主/資源目錄中創建下列文件中的設置:
%PROJECT_HOME%/src/main/resources/META-INF/services/javax.xml.parsers.DocumentBuilderFactory (which defines com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl as the default)
%PROJECT_HOME%/src/main/resources/META-INF/services/javax.xml.parsers.SAXParserFactory (which defines com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl as the default)
%PROJECT_HOME%/src/main/resources/META-INF/services/javax.xml.transform.TransformerFactory (which defines com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl as the default)
這些文件是由兩個應用程序服務器(無需JVM參數)引用,並解決任何單元測試的問題,而不需要任何代碼更改。
完成。
您是否嘗試過治療'XMLType'作爲'CLOB' ? – 2012-07-20 13:33:05
我做到了。它引發一個異常。 – a1ex07 2012-07-20 14:16:43
到目前爲止沒有解決? – Tarion 2013-01-31 12:36:39