我的方向和要求
- 实体应存储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