XML-DBMS: Frequently asked questionsVersion 1.x: How do I use ToRootTable, PseudoRoot, IgnoreRoot, etc.?When to use ToRootTable, ToClassTable, PseudoRoot, IgnoreRoot, etc. is
is one of the most confusing parts of the version 1.x mapping language.
Fortunately, it is gone in version 2.0. I'll try to explain. XML-DBMS needs to know the columns in the primary (candidate) key in the
root table. It needs this information so it can build SELECT statements
over the root table when retrieving data. XML-DBMS can get this
information from two places: the PseudoRoot element and the ToRootTable
element. Which one you use depends on whether you use an ignored root
element or not. Root element is significantFor example, suppose you want to send a single sales order in an XML
document:
<SalesOrder Number="123">
<OrderDate>1/1/01</OrderDate>
<Customer>
...
</Customer>
<LineItem Number = "1">
<Quantity>2</Quantity>
<Part Number = "abc">
...
</Part>
</LineItem>
...
</SalesOrder>
In this case, the root element (<SalesOrder>) is significant and you don't
want to ignore it. Map the SalesOrder element type using a
<ToRootTable> element in the <ClassMap> element. Notice that the
candidate key information is inside the <ToRootTable> element.
<ClassMap>
<ElementType Name="SalesOrder" />
<ToRootTable>
<Table Name="Orders" />
<CandidateKey Generate="No" >
<Column Name="OrderNum" />
</CandidateKey>
</ToRootTable>
...
</ClassMap>
Root element is not significantNow suppose you want to send multiple sales orders in a single XML
document:
<Orders>
<SalesOrder>
...
</SalesOrder>
<SalesOrder>
...
</SalesOrder>
<SalesOrder>
...
</SalesOrder>
</Orders>
In this case, you don't care about the <Orders> element -- it's not mapped
to anything in the database -- so you can map it with an <IgnoreRoot>
element:
<IgnoreRoot>
<ElementType Name="Orders" />
...
</IgnoreRoot>
However, you do care about the <SalesOrder> element, so you need to declare
this as a "pseudo-root" element. It is called a pseudo-root element
because it would be the root element if XML allowed me to have multiple
root elements in the same document. Here's the declaration. Notice that
the candidate key information is in the <PseudoRoot> element:
<IgnoreRoot>
<ElementType Name="Orders" />
<PseudoRoot>
<ElementType Name="SalesOrder">
<CandidateKey Generate="No" >
<Column Name="OrderNum" />
</CandidateKey>
</PseudoRoot>
</IgnoreRoot>
In the <ClassMap> element for the <SalesOrder> element, use the
<ToClassTable> element, which does not contain candidate key
information:
<ClassMap>
<ElementType Name="SalesOrder" />
<ToClassTable>
<Table Name="Orders" />
</ToClassTable>
...
</ClassMap>
Retrieving documents and root table declarationsThis leads us to one more problem that is commonly encountered -- a
NullPointerException in DBMSToDOM:
java.lang.NullPointerException
at de.tudarmstadt.ito.xmldbms.Map.getRootTableMap(Map.java:428)
at de.tudarmstadt.ito.xmldbms.DBMSToDOM.retrieveTableData (DBMSToDOM.java:360)
at de.tudarmstadt.ito.xmldbms.DBMSToDOM.retrieveDocument (DBMSToDOM.java:271)
Although the NullPointerException is due to a bug, the fix isn't much
more helpful -- it throws an InvalidMapException telling you that your
map is incorrect. The problem is that if you want to use a given table as a root table,
that table/element type must be mapped with a ToRootTable element (if
there is no ignored root) or a PseudoRoot element (if there is an
ignored root). For example, suppose you map the <Part> element in the sales order example
using <ToClassTable>. This is OK as long as <Part> is nested inside
<LineItem>. However, if you just want to retrieve information from the
Parts table -- so <Part> is the root element -- you will get the above
NullPointerException. How to fix this depends on whether the XML document you want to create has
an ignored root element or not. If it does not have an ignored root,
then map <Part> with <ToRootTable>.
<ClassMap>
<ElementType Name="Part" />
<ToRootTable>
<Table Name="Parts" />
<CandidateKey Generate="No" >
<Column Name="PartNum" />
</CandidateKey>
</ToRootTable>
...
</ClassMap>
If it does have an ignored root, add a <PseudoRoot> element for
<Part>:
<IgnoreRoot>
<ElementType Name="Orders" />
<PseudoRoot>
<ElementType Name="SalesOrder">
<CandidateKey Generate="No" >
<Column Name="OrderNum" />
</CandidateKey>
</PseudoRoot>
<PseudoRoot>
<ElementType Name="Part">
<CandidateKey Generate="No" >
<Column Name="PartNum" />
</CandidateKey>
</PseudoRoot>
</IgnoreRoot>
and map <Part> using <ToClassTable>:
<ClassMap>
<ElementType Name="Part" />
<ToClassTable>
<Table Name="Parts" />
</ToClassTable>
...
</ClassMap>
Notice two things. First, an <IgnoreRoot> element can have multiple
<PseudoRoot> children. Second, the generated file will use <Orders> as
its root, which is probably not what you want. The solution is to use a
neutrally-named root element, such as <JustARootElement> and map this as
the ignored root element, or create different map files for each case
(yech). What should I do?For maximum flexibility, the best thing to do is:
If you are not using an ignored root element, use <ToRootTable> in
the <ClassMap> elements for all class elements.
If you are using an ignored root element, declare all class elements
as pseudo-root elements. That is, include a <PseudoRoot> element for
each class element and use <ToClassTable> in the <ClassMap> for each
class element.
Version 2.0Fortunately, all this is fixed in version 2.0, so if you can use version 2.0, you should. In particular, version 2.0 has the following fixes:
Any element can be a root element without a special declaration.
Ignored root elements don't need to be declared. When transferring
data from an XML document to the database, XML-DBMS simply ignores
top-level elements (they may be nested more than one level deep) until
it finds an element type that has been mapped.
When transferring data from the database to an XML document, you can
declare the chain (one or more levels deep) of ignored root elements
(called "wrapper elements") that you want to use to wrap the results.
Back to the XML-DBMS FAQs
|