Hướng đi và yêu cầu của tôi
- Thực thể phải lưu trữ XML dưới dạng một chuỗi (java.lang.String)
- Cơ sở dữ liệu phải tồn tại XML trong cột XDB.XMLType
- Cho phép lập chỉ mục và các truy vấn loại xpath / ExtractValue / xquery hiệu quả hơn
- Tổng hợp một tá hoặc nhiều giải pháp từng phần mà tôi đã tìm thấy trong tuần trước
- Môi trường làm việc
- Oracle 11g r2 x64
- Hibernate 4.1.x
- Java 1.7.x x64
- Windows 7 Pro x64
Giải pháp từng bước
Bước 1:Tìm xmlparserv2.jar (~ 1350kb)
Jar này là bắt buộc để biên dịch bước 2 và được bao gồm trong cài đặt oracle ở đây:% ORACLE_11G_HOME% / LIB / xmlparserv2.jar
Bước 1.5:Tìm xdb6.jar (~ 257kb)
Điều này rất quan trọng nếu bạn đang sử dụng Oracle 11gR2 11.2.0.2 trở lên hoặc lưu trữ dưới dạng BINARY XML.
Tại sao?
- Trong 11.2.0.2+, cột XMLType được lưu trữ bằng SECUREFILE BINARYXML theo mặc định, trong khi các phiên bản trước đó sẽ được lưu trữ dưới dạng BASICFILECLOB
- Các phiên bản cũ hơn của xdb * .jar không giải mã đúng cách xml nhị phân và không thành công
- Google Cơ sở dữ liệu Oracle 11g Phiên bản 2 Trình điều khiển JDBC và tải xuống xdb6.jar
- Chẩn đoán và giải pháp cho vấn đề giải mã XML nhị phân được nêu tại đây
Bước 2:Tạo Kiểu người dùng ngủ đông cho Cột kiểu XML
Với Oracle 11g và Hibernate 4.x, điều này dễ dàng hơn nhiều.
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) {}
}
}
}
Bước 3:Chú thích trường trong thực thể của bạn.
Tôi đang sử dụng chú thích với spring / hibernate, không phải tệp ánh xạ, nhưng tôi tưởng tượng cú pháp sẽ tương tự.
@Type(type="your.custom.usertype.HibernateXMLType")
@Column(name="attribute_xml", columnDefinition="XDB.XMLTYPE")
private String attributeXml;
Bước 4:Xử lý lỗi máy chủ ứng dụng / junit do Oracle JAR
Sau khi bao gồm% ORACLE_11G_HOME% / LIB / xmlparserv2.jar (1350kb) trong classpath của bạn để giải quyết lỗi biên dịch, bây giờ bạn nhận được lỗi thời gian chạy từ máy chủ ứng dụng của mình ...
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 ...
TẠI SAO LỖI?
Xmlparserv2.jar sử dụng API dịch vụ JAR (Cơ chế nhà cung cấp dịch vụ) để thay đổi các lớp javax.xml mặc định được sử dụng cho SAXParserFactory, DocumentBuilderFactory và TransformerFactory.
NÓ ĐÃ XẢY RA NHƯ THẾ NÀO?
Javax.xml.parsers.FactoryFinder tìm kiếm các triển khai tùy chỉnh bằng cách kiểm tra, theo thứ tự này, các biến môi trường,% JAVA_HOME% / lib / jaxp.properties, sau đó cho các tệp cấu hình trong META-INF / dịch vụ trên classpath, trước khi sử dụng triển khai mặc định được bao gồm trong JDK (com.sun.org. *).
Bên trong xmlparserv2.jar tồn tại một thư mục META-INF / services mà lớp javax.xml.parsers.FactoryFinder chọn. Các tệp như sau:
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)
GIẢI PHÁP?
Đổi lại cả 3, nếu không bạn sẽ thấy những lỗi lạ.
- javax.xml.parsers. * sửa các lỗi có thể nhìn thấy
- javax.xml.transform. * sửa các lỗi phân tích cú pháp XML tinh vi hơn
- trong trường hợp của tôi, với cấu hình dấu phẩy apache đọc / viết
GIẢI PHÁP NHANH để giải quyết lỗi khởi động máy chủ ứng dụng:Đối số JVM
Để ghi đè các thay đổi do xmlparserv2.jar thực hiện, hãy thêm các thuộc tính JVM sau vào đối số khởi động máy chủ ứng dụng của bạn. Logic java.xml.parsers.FactoryFinder sẽ kiểm tra các biến môi trường trước tiên.
-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
Tuy nhiên, nếu bạn chạy các trường hợp thử nghiệm bằng @RunWith (SpringJUnit4ClassRunner.class) hoặc tương tự, bạn vẫn sẽ gặp lỗi.
GIẢI PHÁP TỐT HƠN cho lỗi khởi động máy chủ ứng dụng VÀ lỗi trường hợp thử nghiệm? 2 tùy chọn
Tùy chọn 1:Sử dụng đối số JVM cho máy chủ ứng dụng và câu lệnh @BeforeClass cho các trường hợp thử nghiệm của bạn
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");
Nếu bạn có nhiều trường hợp thử nghiệm, điều này sẽ trở nên khó khăn. Ngay cả khi bạn xếp nó vào hàng cao cấp.
Tùy chọn 2:Tạo tệp định nghĩa Nhà cung cấp dịch vụ của riêng bạn trong classpath biên dịch / thời gian chạy cho dự án của bạn, tệp này sẽ ghi đè những tệp có trong xmlparserv2.jar
Trong một dự án mùa xuân maven, ghi đè cài đặt xmlparserv2.jar bằng cách tạo các tệp sau trong thư mục% PROJECT_HOME% / src / main / resources:
%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)
Các tệp này được tham chiếu bởi cả máy chủ ứng dụng (không yêu cầu đối số JVM) và giải quyết bất kỳ vấn đề kiểm tra đơn vị nào mà không yêu cầu bất kỳ thay đổi mã nào.
Đã xong.