programing

최대 절전 모드에서 Oracle XMLType 열 사용

iphone6s 2023. 6. 11. 10:24
반응형

최대 절전 모드에서 Oracle XMLType 열 사용

Oracle XMLType 열을 최대 절전 모드 엔티티 클래스에 매핑해야 합니다.구현을 포함하는 효과적인(그리고 잘 알려진) 솔루션이 있습니다.UserType하지만 Oracle xml 파서를 가져와야 하기 때문에 사용할 수 없습니다.
xml 열의 값을 문자열로 액세스하고 엔티티를 조작하는 코드에 변환을 맡겨도 괜찮지만 값을 읽고 데이터베이스에 쓸 수 있는 방법을 찾을 수 없습니다.제가 지금까지 시도한 것은:

  1. 을 엔티클의속으로 합니다.String결과 - 값이 다음과 같이 읽힙니다.null 재이정한경인 Serializable"합니다. "deserialize" 음없 " 발합생 " 니다 가예 " 할수 " ."not ▁i합 " ▁"▁des 다발 " ▁getize▁exception "
  2. 용사를 합니다.@Formula주석)CAST xmlCol as varchar2(1000) - 되지 않음) 결 - 값이지저되
  3. 용사를 합니다.@Loader 퍼팅 팅CASTSELECT그것은 가장 유망한 시도였습니다. 값은 성공적으로 읽고 저장되었지만 xml 열을 포함하는 엔티티 모음을 로드하는 것과 관련하여, 저는 다음과 같은 이점을 얻었습니다.null in (sqlin)을 사용하지 않습니다.@Loader이 기본표다같경은인 경우LEFT JOIN ed)

제가 하는 또 을 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로 저장하는 경우에 매우 중요합니다.

왜요?

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

반응형