최대 절전 모드에서 Oracle XMLType 열 사용
Oracle XMLType 열을 최대 절전 모드 엔티티 클래스에 매핑해야 합니다.구현을 포함하는 효과적인(그리고 잘 알려진) 솔루션이 있습니다.UserType하지만 Oracle xml 파서를 가져와야 하기 때문에 사용할 수 없습니다.
xml 열의 값을 문자열로 액세스하고 엔티티를 조작하는 코드에 변환을 맡겨도 괜찮지만 값을 읽고 데이터베이스에 쓸 수 있는 방법을 찾을 수 없습니다.제가 지금까지 시도한 것은:
- 을 엔티클의속으로 합니다.
String결과 - 값이 다음과 같이 읽힙니다.null재이정한경인Serializable"합니다. "deserialize" 음없 " 발합생 " 니다 가예 " 할수 " ."not ▁i합 " ▁"▁des 다발 " ▁getize▁exception " - 용사를 합니다.
@Formula주석)CAST xmlCol as varchar2(1000)- 되지 않음) 결 - 값이지저되 - 용사를 합니다.
@Loader퍼팅 팅CASTSELECT그것은 가장 유망한 시도였습니다. 값은 성공적으로 읽고 저장되었지만 xml 열을 포함하는 엔티티 모음을 로드하는 것과 관련하여, 저는 다음과 같은 이점을 얻었습니다.nullin (sqlin)을 사용하지 않습니다.@Loader이 기본표다같경은인 경우LEFT JOINed)
제가 하는 또 을 xml로 입니다.String) + (쓰기용) + 추가@Formula하지만, 제게는 그것이 더러운 해킹처럼 보이고, 저는 제가 선택의 여지가 없는 한 그렇게 하고 싶지 않습니다.
마지막으로 DB Schema(보기 + 트리거, 열 데이터 유형 변경과 같은 하나 이상의 옵션)를 변경하는 것이 가장 마지막으로 수행할 수 있는 작업이지만, 이것은 저에게도 좋은 옵션이 아닙니다.
제가 놓친 것이 있는지, 아니면 (3) 일을 할 수 있는 방법이 있는지 궁금합니다.
나의 방향과 요구사항
- 엔티티는 XML을 문자열(java.lang)로 저장해야 합니다.문자열)
- 데이터베이스는 XDB.XMLType 열에 XML을 유지해야 합니다.
- 인덱싱 및 보다 효율적인 xpath/ExtractValue/xquery 유형 쿼리 허용
- 지난 주에 찾은 12개 정도의 부분적인 솔루션
- 작업 환경
- Oracle 11gr2 x64
- 최대 절전 모드 4.1.x
- Java 1.7.x x64
- 윈도우 7 Prox64
단계별 솔루션
1단계: xmlparserv2.jar(~1350kb) 찾기
이 jar는 2단계를 컴파일하는 데 필요하며 여기에서 오라클 설치에 포함됩니다. %ORACLE_11G_HOME%/LIB/xmlparserv2.jar
1.5단계: xdb6.jar(~257kb) 찾기
이는 Oracle 11gR2 11.2.0.2 이상을 사용하거나 BINARY XML로 저장하는 경우에 매우 중요합니다.
왜요?
- 11.2.0.2+에서는 기본적으로 SECUREFILE BINARY XML을 사용하여 XMLType 열이 저장되지만 이전 버전은 BASICFILE CLOB로 저장됩니다.
- 이전 버전의 xdb*.jar가 바이너리 xml을 제대로 디코딩하지 못하고 자동으로 실패함
- Google Oracle Database 11g 릴리스 2 JDBC 드라이버 및 다운로드 xdb6.jar
- 바이너리 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단계: 엔티티의 필드에 주석을 달습니다.
매핑 파일이 아닌 스프링/하이버네이트 주석을 사용하고 있지만 구문은 비슷할 것으로 예상됩니다.
@Type(type="your.custom.usertype.HibernateXMLType")
@Column(name="attribute_xml", columnDefinition="XDB.XMLTYPE")
private String attributeXml;
4단계: Oracle JAR로 인한 appserver/junit 오류 처리
%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(Service Provider Mechanism)를 사용하여 SAXParserFactory, DocumentBuilderFactory 및 TransformerFactory에 사용되는 기본 javax.xml 클래스를 변경합니다.
어떻게 그런 일이 일어났습니까?
javax.xml.parsers.FactoryFinder는 환경 변수 %JAVA_를 이 순서대로 확인하여 사용자 지정 구현을 찾습니다.HOME%/lib/jaxp.properties는 클래스 경로의 META-INF/services 아래에 있는 구성 파일에 대해 JDK(com.sun.org 에 포함된 기본 구현을 사용합니다.*).
xmlparserv2.jar 내부에는 javax.xml.parserver가 저장하는 META-INF/services 디렉토리가 있습니다.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.capters.눈에 보이는 오류를 수정합니다.
- javax.xml.vmdk.*는 더 분석 오류를 합니다.보다 미묘한 XML 구문 분석 오류 수정
- 내 경우, Apache Commons 구성 읽기/쓰기를 사용
응용 프로그램 서버 시작 오류를 해결하는 빠른 해결 방법: 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(Spring)를 사용하여 테스트 사례를 실행하는 경우JUNit4ClassRunner.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에 포함된 파일을 재정의합니다.
메이븐 스프링 프로젝트에서 %PROJECT_에 다음 파일을 생성하여 xmlparserv2.jar 설정을 재정의합니다.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)
이러한 파일은 두 애플리케이션 서버에서 모두 참조되며(JVM 인수가 필요하지 않음) 코드 변경 없이 장치 테스트 문제를 해결합니다.
다 했어요.
이에 대한 훨씬 더 간단한 해결책이 존재합니다.Column Transformer 주석을 사용하면 됩니다.
@ColumnTransformer(read = "to_clob(data)", write = "?")
@Column( name = "data", nullable = false, columnDefinition = "XMLType" )
private String data;`
Celso의 답변을 더욱 단순화하기 위해 Oracle의 내장 함수를 사용하여 사용자 지정 함수를 생성하지 않아도 됩니다.
XMLType.createxml(?)
NULL을 처리할 수 있습니다.
그래서 다음 주석들과 Celso의 맞춤 방언 수업이 잘 작동합니다.
@Lob
@ColumnTransformer(read = "NVL2(EVENT_DETAILS, (EVENT_DETAILS).getClobVal(), NULL)", write = "XMLType.createxml(?)")
@Column(name = "EVENT_DETAILS")
private String details;
사용자 지정 방언에서 clob을 xmltype으로 등록해야 할 수도 있습니다.따라서 효과적으로 다음을 얻을 수 있습니다.
public class OracleDialectExtension extends org.hibernate.dialect.Oracle10gDialect {
public OracleDialectExtension() {
super();
registerColumnType(Types.CLOB, "xmltype");
}
@Override
public boolean useInputStreamToInsertBlob() {
return false;
}
}
최대 절전 모드 구성의 세션 팩토리 속성 목록에서 사용자 지정 사투리를 설정해야 합니다.
<property name="hibernate.dialect"><!-- class path to custom dialect class --></property>
운이 따르지 않고 여러 가지 접근법을 시도한 후, 저는 다음과 같은 생각을 해냈습니다.
엔티티 클래스에서:
@ColumnTransformer(read = "NVL2(EVENT_DETAILS, (EVENT_DETAILS).getClobVal(), NULL)", write = "NULLSAFE_XMLTYPE(?)")
@Lob
@Column(name="EVENT_DETAILS")
private String details;
"EVENT_DETAILS" 주변의 괄호에 유의하시기 바랍니다.테이블 이름을 지정하지 않으면 최대 절전 모드에서 왼쪽에 테이블 이름을 추가하여 열 이름을 다시 쓰지 않습니다.
NULLSAFE_XMLTYPE 함수를 생성해야 합니다. 이 함수를 사용하면 null 값을 삽입할 수 있습니다(@ColumnTransformer에서 쓰기 변환에 대한 물음표가 정확히 하나만 제한되며 XMLType(NULL)에서는 예외가 발생하기 때문입니다).나는 다음과 같은 기능을 만들었습니다.
create or replace function NULLSAFE_XMLTYPE (TEXT CLOB) return XMLTYPE IS
XML XMLTYPE := NULL;
begin
IF TEXT IS NOT NULL THEN
SELECT XMLType(TEXT) INTO XML FROM DUAL;
END IF;
RETURN XML;
end;
persistence.xml 파일에서:
<property name="hibernate.dialect" value="mypackage.CustomOracle10gDialect" />
사용자 지정 방언("useInputStreamToInsertBlob" 메서드를 재정의하지 않으면 "ORA-01461: LONG 열에 삽입하는 경우에만 LONG 값을 바인딩할 수 있음" 오류가 발생합니다.):
package mypackage;
import org.hibernate.dialect.Oracle10gDialect;
public class CustomOracle10gDialect extends Oracle10gDialect {
@Override
public boolean useInputStreamToInsertBlob() {
//This forces the use of CLOB binding when inserting
return false;
}
}
이것은 Hibernate 4.3.6 및 Oracle 11.2.0.1.0(ojdbc6-11.1.0.7.0.jar 포함)을 사용하는 경우에 사용할 수 있습니다.
Matt M의 솔루션은 표준 Maven 저장소에 없는 많은 해킹과 라이브러리 사용을 포함하기 때문에 시도하지 않았다는 것을 인정해야 합니다.
Kamuffel의 솔루션이 저의 시작점이었지만 큰 XML을 삽입하려고 할 때 ORA-01461 오류가 발생했습니다. 그래서 저는 저만의 사투리를 만들어야 했습니다.또한 TO_CLOB(XML_COLUM) 접근 방식에 문제가 있음을 발견했습니다("ORA-19011: 문자열 버퍼가 너무 작음" 오류가 발생할 수 있음).이렇게 하면 XMLTYPE 값이 먼저 VARCHAR2로 변환된 다음 CLOB로 변환되므로 큰 XML을 읽을 때 문제가 발생합니다.그래서 조사한 결과 XML_COLUM.getClobVal()을 대신 사용하기로 결정했습니다.
저는 인터넷에서 이 정확한 해결책을 찾지 못했습니다.그래서 StackOverflow 계정을 만들어서 다른 사람에게 도움이 될 수도 있을 것 같아서 게시하기로 했습니다.
나는 XML String을 구성하기 위해 JAXB를 사용하고 있지만 이 경우에는 관련이 없다고 생각합니다.
Hibernate 3.6.*에서 Hibernate 5.4로 마이그레이션할 때 문제가 발생했습니다. Oracle xmlparserv2 이전에 dbUnitmaven 종속성을 추가하여 해결했습니다. dbUnit에는 xercesImpplas 임시 종속성이 있습니다.이렇게 하면 앱 서버 구성을 방해할 필요가 없으며 장치 테스트가 잘 실행됩니다.
언급URL : https://stackoverflow.com/questions/11578697/using-oracle-xmltype-column-in-hibernate
'programing' 카테고리의 다른 글
| 모든 월 이름을 나열하는 방법(예: 콤보)? (0) | 2023.06.11 |
|---|---|
| ID 집합으로 여러 문서를 업데이트합니다.몽구스 (0) | 2023.06.11 |
| 왜 'eval'을 사용하는 것이 나쁜 관행입니까? (0) | 2023.06.06 |
| Python + Selenium WebDriver를 사용하여 쿠키를 저장하고 로드하는 방법 (0) | 2023.06.06 |
| 레일 위에서 루비 배우기 (0) | 2023.06.06 |