In quest’articolo cercherò di fare una panoramica su come Oracle XMLDB supporta XML Schema.
Ovviamente darò per scontata la conoscenza degli schemi XML, per una prima infarinatura sull’argomento si può leggere questo mio articolo pubblicato su Computer Programming nel 2002.
La prima cosa da fare per utilizzare un qualunque schema in XMLDB è registrarlo nel DB.
Quest’operazione viene realizzata mediante un’apposita procedura del package XMLSCHEMA:
SQL> BEGIN 2 DBMS_XMLSCHEMA.registerSchema( 3 SCHEMAURL => 'https://oracleitalia.wordpress.com/xsd/TestSchema.xsd', 4 SCHEMADOC => bfilename('FILE_DIR','TestSchema.xsd'), 5 CSID => nls_charset_id('WE8MSWIN1252')); 6 END; 7 / Procedura PL/SQL completata correttamente.
Abbiamo passato alla procedura:
Bisogna sottolineare che la URL fornita in SCHEMAURL non è reale, a quell’indirizzo non c’è nessun bisogno che ci sia effettivamente lo schema o qualunque altra cosa. E’ solo il nome con cui referenzieremo il nostro schema d’ora in poi.
Ecco gli altri parametri che si possono utilizzare in fase di registrazione di uno schema:
Ecco lo schema che ho registrato (appunto quello dell’articolo suddetto)…
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:complexType name="COMPLETE_NAME_TYPE"> <xs:sequence> <xs:element name="SURNAME" type="xs:string"/> <xs:element name="NAME" type="xs:string"/> </xs:sequence> </xs:complexType> <xs:element name="TEAM"> <xs:complexType> <xs:sequence> <xs:element name="TRAINER" minOccurs="1" maxOccurs="1"> <xs:complexType> <xs:sequence> <xs:element name="TR_COMPL_NAME" type="COMPLETE_NAME_TYPE"/> <xs:element name="NATIONALITY" type="xs:string"/> </xs:sequence> <xs:attribute name="fifa_id" use="required"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:length value="10"/> </xs:restriction> </xs:simpleType> </xs:attribute> </xs:complexType> </xs:element> <xs:element name="PLAYERS" minOccurs="1" maxOccurs="1"> <xs:complexType> <xs:sequence> <xs:element name="PLAYER" minOccurs="23" maxOccurs="23"> <xs:complexType> <xs:sequence> <xs:element name="COMPL_NAME" type="COMPLETE_NAME_TYPE"/> <xs:element name="CLUB" type="xs:string"/> </xs:sequence> <xs:attribute name="number" use="required"> <xs:simpleType> <xs:restriction base="xs:integer"> <xs:minInclusive value="1"/> <xs:maxInclusive value="23"/> </xs:restriction> </xs:simpleType> </xs:attribute> <xs:attribute name="age" type="xs:integer" use="required"/> <xs:attribute name="goalkeeper" type="xs:string" fixed="YES"/> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> <xs:attribute name="country" use="required"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:enumeration value="KOREA"/> <xs:enumeration value="JAPAN"/> <xs:enumeration value="CHINA"/> <xs:enumeration value="SAUDI ARABIA"/> <xs:enumeration value="SOUTH AFRICA"/> <xs:enumeration value="CAMEROON"/> <xs:enumeration value="SENEGAL"/> <xs:enumeration value="TUNISIA"/> <xs:enumeration value="NIGERIA"/> <xs:enumeration value="COSTA RICA"/> <xs:enumeration value="USA"/> <xs:enumeration value="MEXICO"/> <xs:enumeration value="ARGENTINA"/> <xs:enumeration value="PARAGUAY"/> <xs:enumeration value="ECUADOR"/> <xs:enumeration value="BRAZIL"/> <xs:enumeration value="FRANCE"/> <xs:enumeration value="POLAND"/> <xs:enumeration value="SWEDEN"/> <xs:enumeration value="SPAIN"/> <xs:enumeration value="RUSSIA"/> <xs:enumeration value="PORTUGAL"/> <xs:enumeration value="DENMARK"/> <xs:enumeration value="CROATIA"/> <xs:enumeration value="ITALY"/> <xs:enumeration value="ENGLAND"/> <xs:enumeration value="SLOVENIA"/> <xs:enumeration value="TURKEY"/> <xs:enumeration value="BELGIUM"/> <xs:enumeration value="GERMANY"/> <xs:enumeration value="AUSTRALIA"/> <xs:enumeration value="IRELAND REPUBLIC"/> </xs:restriction> </xs:simpleType> </xs:attribute> </xs:complexType> <xs:unique name="NUMBERING"> <xs:selector xpath="./PLAYERS/PLAYER"/> <xs:field xpath="@number"/> </xs:unique> </xs:element> </xs:schema>
Ed ecco gli oggetti di DB che sono stati automaticamente creati:
SQL> select object_name, object_type, to_char(last_ddl_time,'hh24miss') 2 from user_objects 3 where last_ddl_time>trunc(sysdate); OBJECT_NAME OBJECT_TYPE TO_CHA ----------------------------------- ------------------------------ ------ COMPLETE_NAME_TYPE641_T TYPE 002858 TRAINER643_T TYPE 002859 PLAYER645_T TYPE 002859 PLAYER646_COLL TYPE 002859 PLAYERS644_T TYPE 002900 TEAM642_T TYPE 002900 TEAM647_TAB TABLE 002905 SYS_NTZYupEfMzS22Q1xVs8KDURg== TABLE 002906 SYS_C009978 INDEX 002901 SYS_LOB0000070864C00010$$ LOB 002900 SYS_LOB0000070864C00004$$ LOB 002900 SYS_C009979 INDEX 002901 SYS_XDBPD$651_L LOB 002901 SYS_XDBPD$650_L LOB 002901 SYS_XDBPD$649_L LOB 002901 SYS_XDBPD$648_L LOB 002901 EXTRADATA652_L LOB 002901 NAMESPACES653_L LOB 002901 SYS_C009980 INDEX 002901 TEAM647_TAB$xd TRIGGER 002905 Selezionate 20 righe. SQL> desc COMPLETE_NAME_TYPE641_T COMPLETE_NAME_TYPE641_T Þ NOT FINAL Nome Nullo? Tipo ----------------------------------------- -------- ---------------------------- SYS_XDBPD$ XDB.XDB$RAW_LIST_T SURNAME VARCHAR2(4000 CHAR) NAME VARCHAR2(4000 CHAR) SQL> desc TRAINER643_T Nome Nullo? Tipo ----------------------------------------- -------- ---------------------------- SYS_XDBPD$ XDB.XDB$RAW_LIST_T fifa_id VARCHAR2(10 CHAR) TR_COMPL_NAME COMPLETE_NAME_TYPE641_T NATIONALITY VARCHAR2(4000 CHAR) SQL> desc TEAM647_TAB Nome Nullo? Tipo ----------------- -------- ------------ TABLE of SYS.XMLTYPE( XMLSchema "https://oracleitalia.wordpress.com/xsd/TestSchema.xsd" Element "TEAM") STORAGE Object-relational TYPE "TEAM642_T"
Normalmente abbiamo già qualche tabella in cui inserire i nostri dati XML, a che serve dunque questa generata automaticamente?
Se i file XML arrivano in XMLDB attraverso protocolli, come FTP o HTTP, che non supportano la specifica della tabella di DB in cui inserirli, essi vengono automaticamente inseriti in queste tabelle di default.
L’elenco degli schemi registrati si ottiene dalla vista di dizionario DBA_XML_SCHEMAS (o ALL_ o USER_):
SQL> select owner, SCHEMA_URL from all_xml_schemas; OWNER SCHEMA_URL ---------- ---------------------------------------------------------- MAXR https://oracleitalia.wordpress.com/xsd/TestSchema.xsd XDB http://xmlns.oracle.com/xdb/stats.xsd XDB http://xmlns.oracle.com/xdb/xdbconfig.xsd XDB http://xmlns.oracle.com/xs/dataSecurity.xsd XDB http://xmlns.oracle.com/xs/aclids.xsd XDB http://xmlns.oracle.com/xdb/XDBSchema.xsd XDB http://xmlns.oracle.com/xdb/XDBResource.xsd XDB http://www.w3.org/2001/csx.xml.xsd XDB http://xmlns.oracle.com/xdb/csx.xmltr.xsd XDB http://xmlns.oracle.com/xdb/acl.xsd XDB http://xmlns.oracle.com/xdb/dav.xsd XDB http://xmlns.oracle.com/xdb/XDBResConfig.xsd XDB http://xmlns.oracle.com/xdb/XDBStandard.xsd XDB http://xmlns.oracle.com/xdb/log/xdblog.xsd XDB http://xmlns.oracle.com/xdb/log/ftplog.xsd XDB http://xmlns.oracle.com/xdb/log/httplog.xsd XDB http://www.w3.org/2001/xml.xsd XDB http://xmlns.oracle.com/xdb/xmltr.xsd XDB http://xmlns.oracle.com/xdb/XDBFolderListing.xsd XDB http://www.w3.org/1999/xlink.xsd XDB http://www.w3.org/1999/csx.xlink.xsd XDB http://www.w3.org/2001/XInclude.xsd XDB http://www.w3.org/2001/csx.XInclude.xsd
Ma che ce ne facciamo adesso di questo schema che abbiamo registrato?
Innanzitutto possiamo, in fase di creazione di una tabella di XMLTYPE, definire che le istanze XML che saranno inserite in tabella dovranno essere conformi a questo scema:
CREATE TABLE TEAMS OF XMLType XMLSCHEMA "https://oracleitalia.wordpress.com/xsd/TestSchema.xsd" ELEMENT "TEAM";
Proviamo adesso ad inserire in TEAMS un XML che viola lo schema:
SQL> Insert into TEAMS values (XMLType(' 2 <TEAM country="ABCD"> 3 <TRAINER fifa_id="ITA1234567"> 4 <TR_COMPL_NAME> 5 <SURNAME>Trapattoni</SURNAME> 6 <NAME>Giovanni</NAME> 7 </TR_COMPL_NAME> 8 <NATIONALITY>Italy</NATIONALITY> 9 </TRAINER> 10 <PLAYERS> 11 <PLAYER number="1" age="23" goalkeeper="YES"> 12 <COMPL_NAME> 13 <SURNAME>Buffon</SURNAME> 14 <NAME>Gianluigi</NAME> 15 </COMPL_NAME> 16 <CLUB>Juventus</CLUB> 17 </PLAYER> 18 </PLAYERS> 19 </TEAM> 20 ')); Insert into TEAMS values (XMLType(' * ERRORE alla riga 1: ORA-31038: Valore enumeration non valido: "ABCD"
L’errore è dovuto al fatto che ho cercato di inserire un documento XML avente country=”ABCD” che non rientra tra i valori previsti nello schema.
In verità la validazione eseguita da Oracle al momento dell’insert è parziale (a meno che non si utilizzino XMLType archiviati in formato binario).
Viene controllato che non esistano nel documento elementi non previsti e che ci siano tutti gli elementi obbligatori.
Ad esempio l’insert seguente funziona:
SQL> Insert into TEAMS values (XMLType(' 2 <TEAM country="ITALY"> 3 <TRAINER fifa_id="ITA1234567"> 4 <TR_COMPL_NAME> 5 <SURNAME>Trapattoni</SURNAME> 6 <NAME>Giovanni</NAME> 7 </TR_COMPL_NAME> 8 <NATIONALITY>Italy</NATIONALITY> 9 </TRAINER> 10 <PLAYERS> 11 <PLAYER number="1" age="23" goalkeeper="YES"> 12 <COMPL_NAME> 13 <SURNAME>Buffon</SURNAME> 14 <NAME>Gianluigi</NAME> 15 </COMPL_NAME> 16 <CLUB>Juventus</CLUB> 17 </PLAYER> 18 <PLAYER number="1" age="23" goalkeeper="YES"> 19 <COMPL_NAME> 20 <SURNAME>Buffon</SURNAME> 21 <NAME>Gianluigi</NAME> 22 </COMPL_NAME> 23 <CLUB>Juventus</CLUB> 24 </PLAYER> 25 </PLAYERS> 26 </TEAM> 27 ')); Creata 1 riga.
Nonostante il fatto che violi per due motivi lo schema:
1) Ci sono solo due elementi PLAYER anziché i 23 previsti
2) L’univocità dell’attributo number è violata
Per verificare se un docuemnto inserito è valido oppure no posso utilizzare, ad esempio, la funzione XMLISVALID:
SQL> select xmlisvalid(OBJECT_VALUE) valido 2 from teams; VALIDO ---------- 0 0 0 1 0
Che restituisce come valido (1) solo il documento completo di tutti gli elementi corretti mostrato nel listato 2 dell’articolo citato all’inizio.
Un modo più “parlante” per validare i documenti è chiamare la procedura schemaValidate di XMLType. Questa solleva un errore molto più utile a risolvere gli eventuali problemi.
Ecco un esempio di procedura che chiama la validazione su titti i record presenti in tabella e stampa l’errore per quelli non validi:
SQL> declare 2 cursor c is 3 select rownum, OBJECT_VALUE from TEAMS; 4 begin 5 for d in c loop 6 begin 7 d.OBJECT_VALUE.schemaValidate(); 8 exception 9 when others then 10 dbms_output.put_line('Riga '||d.rownum||':'); 11 dbms_output.put_line(sqlerrm); 12 dbms_output.put_line('------------------------------'); 13 end; 14 end loop; 15 end; 16 / Riga 1: ORA-31154: documento XML non valido ORA-19202: Errore durante l'elaborazione XML LSX-00213: solo 1 occorrenze della parte "PLAYER", il minimo Þ 23 ------------------------------ Riga 2: ORA-31154: documento XML non valido ORA-19202: Errore durante l'elaborazione XML LSX-00213: solo 1 occorrenze della parte "PLAYER", il minimo Þ 23 ------------------------------ Riga 3: ORA-31154: documento XML non valido ORA-19202: Errore durante l'elaborazione XML LSX-00213: solo 2 occorrenze della parte "PLAYER", il minimo Þ 23 ------------------------------ Riga 5: ORA-31154: documento XML non valido ORA-19202: Errore durante l'elaborazione XML LSX-00287: chiave duplicata "1.0" ------------------------------ Procedura PL/SQL completata correttamente.
Che succede quando uno schema cambia nel tempo?
In Oracle9i era una tragedia. Una volta associata una tabella allo schema questo non poteva più essere cambiato, quindi bisognava droppare tutto, ri-registrare lo schema, ri-creare la tabella e reinserire tutti i record.
Tutto questo a mano…
In Oracle10g è stata introdotta la procedura COPYEVOLVE del package DBMS_XMLSCHEMA.
Questa procedura consente un aggiornamento “semiautomatico” dello schema.
Praticamente copia i documenti XML in una tabella temporanea, droppa tutti gli oggetti collegati allo schema e lo stesso schema, ri-registra lo schema (e quindi crea tutti i nuovi oggetti collegati) e reinserisce tutti i docuemnti.
Ovviamente i vecchi documenti continueranno ad essere validi anche rispetto al nuovo schema solo de i due schemi sono “compatibili”.
La procedura non si pu definire completamente automatica perché una serie di oggetti eventualmente presenti sulle vecchie tabelle devono essere creati a mano sulle nuove (indici, constraint, trigger).
In oracle11g è stata introdotta la cosidetta “In place copy evolution” ovvero la possibilità di modificare lo schema senza copiare i documenti in una tabella d’appoggio, droppare la vecchia tabella etc…
Questa procedura, non sempre applicabile, esegue un aggiornamento al volo senza spostare i documenti, quindi è molto più rapida della precedente.
Dopo che uno schema è stato creato, registrato, utilizzato e modificato più volte arriva il momento in cui bisogna cancellarlo.
Per farlo è sufficiente utilizzare la seguente:
SQL> BEGIN 2 DBMS_XMLSCHEMA.deleteSchema( 3 SCHEMAURL => 'https://oracleitalia.wordpress.com/xsd/TestSchema.xsd', 4 DELETE_OPTION => dbms_xmlschema.DELETE_CASCADE_FORCE); 5 END; 6 / Procedura PL/SQL completata correttamente. SQL> desc teams; ERROR: ORA-24372: oggetto non valido per descrizione
Che, come si vede, oltre a droppare lo schema droppa anche tutti gli oggetti dipendenti.
Questo comportamento si può ovviamente modificare impostando opportunamente il parametro DELETE_OPTION.
Alla prossima,
Massimo
Tag: oracle, XML Schema
10 marzo 2010 alle 14:56 |
Ciao, un bellissimo esempio e soprattutto molto chiaro, devo premettere che solo ora sto facendo dei test con xml quindi sono nuovo per questo argomento, volevo chiederti ma caricando così i dati poi si dovrebbero vedere nei campi della tab teams facendo una select?
grazie 1000
alberto
10 marzo 2010 alle 17:55 |
Ciao,
sì il file xml viene caricato in tabella in un campo di tipo xmltype.
Poi puoi leggerlo come un unico oggetto facendo semplicemente select nomecampo from tabella, oppure puoi leggerne porzioni utilizzando le tabte funzionalità di query xml messe a disposizione da XMLDB. Ci sono un paio di articoli sul blog che parlano di questo
11 marzo 2010 alle 14:15 |
Ciao Massimo,
grazie 1000 per le dritte….
ho visto che ci 6 anche sul sito di Oracle DBA… 😉
16 aprile 2010 alle 18:50 |
Buona sera Massimo, ho cercato di adattare il suo esempio al problema che devo risolvere ma rimangono dei quesiti (bloccanti).
Prefazione :
1) Necessita di collegarmi ad un applicativo tramite Web Services pubblico.
2) Invio di informazioni in formato XML tremite protocollo SOAP.
3) Ricezione di informazioni dall’applicativo sempre in formato XML tremite protocollo SOAP.
Ambiente di lavoro :
1) Forms 6i
2) Developer Siute 10g
2.1) Forms 10g
2.2) JDeveloper 10g
3) Application Server 10g
Cosa ho fatto :
1) Creato lo Stub da file WSDL
2) Depoyato classi java e importate nella mia forms
3) Creato nel Repositori gli schemi Xsd con la definizione dei file Xml
Dubbi :
1) E’ sufficente la creazione dello Stub per poter invire le informazioni al Web Services ?
2) Una volta caricato gli schemi Xsd sul Repositori come faccio a capire quanti “ELEMENT” devo creare nella mia tabella.
3) Come popolo la tabella contenete lo schema :
La rigrazio.
Cordiali Saluti Davide.
17 aprile 2010 alle 00:13 |
Ciao,
l’utilizzo di uno stub statico è in genere il modo più semplice per creare un ws client.
Quanto alla parte XML, sinceramente non sono sicuro di avere capito i tuoi dubbi.
Una volta creato lo stub non devi costruire il documento XML SOAP per effettuare la chiamata né effettuare esplicitamente il parsing del documento XML SOAP che ottieni come risultato.
Tu lavori sui dati elementari utilizzando appunto lo stub che si occupa di confezionare e leggere le buste SOAP.
Ti consiglio di dare una sguardo a quest’articolo ed in particolare al caso numero uno (“1. Static Stub Client “) all’interno del paragrafo “Creating Web Service Clients”.
ciao,
massimo
20 aprile 2010 alle 09:23 |
Grazie Massimo per il supporto, il mio dubbio relativo ai file XML nasce dal fatto che i metodi che ho deployato, con JDeveloper, ed in seguito importato nella mia forms sono così strutturati :
FUNCTION XXXXXXXX (
obj ORA_JAVA.JOBJECT,
a0 ORA_JAVA.JOBJECT) RETURN ORA_JAVA.JOBJECT;
Comunque sto studiando l’articolo che mi hai segnalato sperando di capirci qualcosa in più, da profano di Web Services è proprio un bel “casino”.
Grazie per aver accettato l’invito.
Ciao.
Davide.
13 marzo 2015 alle 18:11 |
Gentile Massimo seguo sempre con interesse il suo blog e più di una volta mi ha aiutato molto.
Anche oggi ho bisogno del suo gentile aiuto.Ho il problema inverso dell’esempio riportato ovvero devo leggere con una select un file xml (multirecord) e la mia versione di db è la 10g 10.1.0.2.0 .
Non riesco a trovare un comando compatibile, riesco ad utilizzare il comando extractValue ma quando arrivo su più nodi che si ripetono fallisce la select.
Potrebbe postare una select compatibile con la mia versione di db per la lettura del seguente file di esempio
Tom
California
Los angeles
California1
Los angeles1
Jim
California
Los angeles
18 marzo 2015 alle 17:43 |
Ciao Amedeo, la funzione EXTRACTVALUE può restituire il valore di un solo nodo.
Per estrarre un frammento XML (composto eventualmente di più nodi) devi utilizzare la funzione EXTRACT, con gli stessi parametri della EXTRACTVALUE.
Ciao,
Massimo