Tải bản đầy đủ (.pdf) (89 trang)

Building Oracle XML Applications phần 7 potx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (510.21 KB, 89 trang )

emps emp_list; Holds a list of Employees
emp emp_t; Holds a single Employee
BEGIN
Insert the master
INSERT INTO dept( deptno, dname )
VALUES (:new.deptno, :new.dname );

Insert the details, using value of :new.deptno as the DEPTNO
emps := :new.employees;
FOR i IN 1 emps.COUNT LOOP
emp := emps(i);
INSERT INTO emp(deptno, empno, ename, sal)
VALUES (:new.deptno, emp.empno, emp.ename, emp.sal);
END LOOP;
END;
This trigger inserts the :new.deptno and :new.dname values into the dept table, then loops over
the collection of <Employees> being inserted and inserts the values of each one as a new row in
the emp table, using the value of :new.deptno as the DEPTNO foreign key value in the emp table.
With the department view in place, and the <DepartmentList> datagram transformed into
canonical format in the deptemp-transformed.xml file, we can insert the transformed document
using OracleXML like this:
java OracleXML putXML
-user xmlbook/xmlbook
-fileName deptemp-transformed.xml
department
This successfully inserts two rows into the department object view, with each row inserted in the
object view translating into one row in the dept table and one or more rows added to the emp
table.
12.2.6 Inserting Datagrams with Document Fragments
The <product-list> datagram in Example 12.25 contains the nested structure for <weight>,
<image>, and <dimensions>, as well as an embedded document fragment of well-formed HTML


in its <features> element.
Example 12.25. Product List Datagram with Nested Structure
<product-list>
<product>
<sku>1234567</sku>
<class>CD Player</class>
<manufacturer>Sony</manufacturer>
<model>D-F411</model>
<weight>
<units>g</units>
<amount>260</amount>
</weight>
<image>
<small>df-411.gif</small>
<large>df-411-large.gif</large>
</image>
<dimensions>
<units>mm</units>
<width>133</width>
<length>152</length>
<height>33</height>
</dimensions>
<price>159.95</price>
<features>
<ul>
<li>
<b>SteadySound</b>, The Next Generation Of Skip Protection, surpasses
Sony's current 20-second Buffer Memory system.
</li>
<li>

<b>Synthesized Digital AM/FM Stereo Tuner</b> precisely locks in the
most powerful signal for accurate, drift-free reception.
</li>
<li>
New <b>Compact Design</b> has attractive yet functional styling
</li>
</ul>
</features>
</product>
</product-list>
Since we've dealt with some of these situations in previous examples, we'll move a little faster
through this example to get to the new nuance—the handling of the nested document fragment.
To capture the nested structure of <weight>, <image>, and <dimensions>, we create three
appropriate user-defined types:
CREATE TYPE weight_type as object(
units VARCHAR2(5),
amount NUMBER
);

CREATE TYPE dimension_type as object(
units VARCHAR2(5),
width NUMBER,
length NUMBER,
height NUMBER
);

CREATE TYPE imagegroup_type as object(
small VARCHAR2(200),
large VARCHAR2(200)
);

Then we make use of these types in appropriate column names of a table to store the product
datagrams we receive:
CREATE TABLE product(
sku NUMBER PRIMARY KEY,
class VARCHAR2(80),
manufacturer VARCHAR2(80),
model VARCHAR2(80),
weight weight_type,
image imagegroup_type,
dimensions dimension_type,
price NUMBER,
features VARCHAR2(4000) /* Could be a CLOB if you need > 4K */
);
If we can make the assumption that the features blurb of well-formed HTML will not exceed 4096
characters (4K), then we can use a VARCHAR2(4000) column to store these. If this is not a safe
assumption, then it would be better to use a CLOB column that can store up to two gigabytes of
document fragment. For this example, we'll stick with the VARCHAR2(4000) column for
features.
Since we want to store the <features> element's nested well-formed HTML content as a textual
document fragment, we must transform the resulting element tree:
<ul>
<li>
<b>SteadySound</b>, The Next Generation Of Skip Protection, surpasses
Sony's current 20-second Buffer Memory system.
</li>
<li>
<b>Synthesized Digital AM/FM Stereo Tuner</b> precisely locks in the
most powerful signal for accurate, drift-free reception.
</li>
<li>

New <b>Compact Design</b> has attractive yet functional styling
</li>
</ul>
by parsing the <product-list> datagram into a single text string. We can do that by leveraging
the xmlMarkup XSLT extension function that we will build in Chapter 16
, as part of an identity
transformation on the original <product-list> datagram. For now, suffice it to say that an XSLT
transformation can invoke methods on user-defined Java objects when some functionality is
more easily implemented in Java than in pure XSLT.
Example 12.26
shows the insert-product.xsl XSLT transformation we need.
Example 12.26. Transforming Document Fragment to Literal
XML Markup

<! insert-product.xsl >
<xsl:stylesheet version="1.0"
xmlns:xsl="
xmlns:ext="
exclude-result-prefixes="ext">
<! Start with the identity Transformation >
<xsl:include href="identity.xsl"/>
<!
| Use our xmlMarkup( ) extension function to write
| out the features nested XML content as literal
| XML text markup.
+ >
<xsl:template match="features">
<features>
<xsl:value-of select="ext:xmlMarkup(*)"/>
</features>

</xsl:template>
</xsl:stylesheet>
This example uses <xsl:include> to include the template for the identity transformation and
augments this with an <xsl:template> that matches the <features> element:
<xsl:template match="features">
<features>
<xsl:value-of select="ext:xmlMarkup(*)"/>
</features>
</xsl:template>
This template outputs the <features> element of each <product> as a textual "printout" of the
nested elements within it. So, the result of transforming the <product-list> datagram using the
insert-product.xsl stylesheet:
oraxsl product-list.xml insert-product.xsl product-to-insert.xml
is a product-to-insert.xml file that looks like Example 12.27
.
Example 12.27. Results of Quoting the XML Markup for
Product Features

<?xml version = '1.0' encoding = 'UTF-8'?>
<product-list>
<product>
<sku>1234567</sku>
<class>CD Player</class>
<manufacturer>Sony</manufacturer>
<model>D-F411</model>
<weight>
<units>g</units>
<amount>260</amount>
</weight>
<image>

<small>df-411.gif</small>
<large>df-411-large.gif</large>
</image>
<dimensions>
<units>mm</units>
<width>133</width>
<length>152</length>
<height>33</height>
</dimensions>
<price>159.95</price>
<features>&#60;ul>
&#60;li>
&#60;b>SteadySound&#60;/b>, The Next Generation Of Skip Protection surpasses
Sony's current 20-second Buffer Memory system.
&#60;/li>
&#60;li>
&#60;b>Synthesized Digital AM/FM Stereo Tuner&#60;/b> precisely locks in
the
most powerful signal for accurate, drift-free reception.
&#60;/li>
&#60;li>
New &#60;b>Compact Design&#60;/b> has attractive yet functional styling
&#60;/li>
&#60;/ul>
</features>
</product>
</product-list>
In this example, all the opening angle bracket characters in the content of the <features>
element have been replaced by the numerical character entity &#60; to represent a literal
less-than sign, which is synonymous with the named character entity &lt;.

Now we can use the following command to insert the transformed document into the product
table:
java OracleXML putXML
-user xmlbook/xmlbook
-fileName product-to-insert.xml
-rowTag product
-ignoreCase
product
To complete the round-trip into the database and back out, let's look at how we would
dynamically serve a <product-list> datagram out of our product table for a product with a
particular SKU number on request over the Web.
Just as we used an XSLT transformation to convert the nested content of the <features> element
to a text fragment of XML markup on the way into the database, we'll use XSLT again on the way
out to turn the text fragment back into nested elements of the datagram we serve. We'll use a
similar transformation that performs the identity transformation on all elements of the document
except <features>, which we'll handle in a special way. Example 12.28
shows the required
transformation.
Example 12.28. Transforming Document Fragment Text into
Elements

<! features-frag-to-elts.xsl >
<xsl:stylesheet xmlns:xsl=" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:include href="identity.xsl"/>
<!
| <features> is a column with embedded XML markup in its
| corresponding column in the database. By disabling
| the output escaping it will be included verbatim
| (i.e. angle-brackets intact instead of &lt; and &gt;)

| in the resulting document
+ >
<xsl:template match="features">
<features>
<xsl:value-of select="." disable-output-escaping="yes"/>
</features>
</xsl:template>
</xsl:stylesheet>
Example 12.28
uses the disable-output-escaping="yes" attribute on <xsl:value-of> to
request that the XSLT Processor include the text of the features document fragment verbatim in
the transformed output instead of "escaping" the less-than signs in the text with a character
entity like lt; or #60;.
To serve the <product-list> datagram, we just create a simple XSQL page with a SELECT *
FROM PRODUCT query, and associate it with the features-frag-to-elts.xsl transformation in its
<?xml-stylesheet?> instruction:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="features-frag-to-elts.xsl"?>
<xsql:query xmlns:xsql="urn:oracle-xsql" connection="xmlbook"
rowset-element="product-list" row-element="product" id-attribute=""
tag-case="lower">

SELECT *
FROM product
WHERE sku = {@sku}

</xsql:query>
When a request comes in for this XSQL page with an appropriate sku parameter like:
http://yourserver/product.xsql?sku=1234567
this delivers a <product-list> datagram that looks exactly like what we started with at the

beginning of this section.
You can use the output of one XSQL page as the input of another to format a dynamic XML
datagram including document fragments as an attractive web page. Assuming that
show-product.xsl is an XSLT stylesheet that transforms a <product-list> datagram into
eye-catching HTML, you can use the <xsql:include-xsql> tag to include the output of the
product.xsql page as part of the input data for another XSQL page called show-product.xsql.
Simply create a show-product.xsql page that looks like:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="show-product.xsl"?>
<xsql:include-xsql href="product.xsql?sku={@sku}"
xmlns:xsql="urn:oracle-xsql" />
This associates the show-product.xsl stylesheet to the page. Now a web request for:
http://yourserver/show-product.xsql?sku=1234567
uses the show-product.xsl stylesheet to transform the output of the product.xsql page, a
<product-list> datagram, into a lovely web page.
12.3 Storing Posted XML Using XSQL Servlet
We've seen that the general steps for inserting XML into the database are as follows:
1. Choose the table or view you want to use for inserting the XML information.
2. Create an XSL transformation that transforms the inbound document into the canonical
format for this table or view.
3. Transform the inbound document into the canonical format for the table or view into which
it will be inserted.
4. Insert the transformed document into your table or view with the OracleXML utility.
The Oracle XML SQL Utility works well for inserting XML documents you have in front of you in
operating system files. However, if you need to have other computers post live XML information
to your web site for insertion into your database, you'll need to use a slight twist on this approach.
12.3.1 Storing Posted XML Using XSQL Pages
The Oracle XSQL Servlet supports the <xsql:insert-request> action element, which you can
include in any XSQL page to automate these steps:
1. Read a posted XML document from the HTTP request.

2. Transform it into the canonical format for insertion using any XSLT transformation you
provide.
3. Insert the transformed document into the table or view of your choice.
4. Indicate the status of the operation by replacing the <xsql:insert-request> action
element with an <xsql-status> element to show how many rows were inserted or to
report an error.
Behind the scenes, the insert_request action handler makes programmatic use of the Oracle
XSLT Processor to do the transformation and the Oracle XML SQL Utility to do the insert, so
everything we've learned earlier applies here.
Given the name of the table or view to use as the insert target and the name of the XSLT
transformation to use, you add the following tag anywhere in your XSQL page:
<xsql:insert-request table="table_or_view_name"
transform="transformname.xsl"/>
to transform and insert the posted XML document.
For example, recall the Moreover.com news feed from Example 12.3
and the
moreover-to-newsstory.xsl transformation we created in Example 12.5
. The XSQL page in
Example 12.29
is all you need to insert posted XML news stories instead over the Web into your
newsstory table.
Example 12.29. XSQL Page to Insert Posted XML News Stories
<?xml version="1.0"?>
<! SimpleNewsInsert.xsql >
<xsql:insert-request connection="xmlbook" xmlns:xsql="urn:oracle-xsql"
table="newsstory"
transform="moreover-to-newsstory.xsl"/>
One tag, that's it! No custom servlet to write, no XML to parse, and no transformation to do
manually. Deploying a new XSQL page to insert posted XML is as easy as copying the .xsql file to
a directory on your web server.

We can test the SimpleNewsInsert.xsql page by using the XSQL command-line utility with the
command:
xsql SimpleNewsInsert.xsql posted-xml=SomeFileToPost.xml
or, if the source of XML is coming directly from a URL, we can do this:
xsql SimpleNewsInsert.xsql posted-xml=
Setting the posted-xml command-line parameter to the name of an XML file or URL returning
XML causes the XSQL page processor to treat that XML source as the posted XML source for
insert.
We can also test SimpleNewsInsert.xsql using any client program that can post an XML document
full of news stories. One approach is to use JavaScript in an HTML page to post some XML to the
server. In our example, we'll post XML the user types into a <TEXTAREA> so you can see what's
going on, but the technique used in the example applies to any XML.
The Internet Explorer 5.0 browser includes support for an XMLHttpRequest object that makes
quick work of the task from the browser client. Example 12.30
shows the JavaScript code of the
PostXMLDocument() function, which does the job of posting any XML document you pass in to the
URL you pass as a parameter.
Example 12.30. Function to Post XML Document to a Web
Server from IE5

// PostXMLDocument.js
// Uses HTTP POST to send XML Document "xmldoc" to URL "toURL"
function PostXMLDocument (xmldoc, toURL)
{
// Create a new XMLHttpRequest Object (IE 5.0 or Higher)
var xmlhttp = new ActiveXObject ("Microsoft.XMLHTTP");
// Open a synchronous HTTP Request for a POST to URL "toUrl"
xmlhttp.open("POST", toURL , /* async = */ false );
// Could set HTTP Headers Here (We don't need to in this example)
// xmlhttp.setRequestHeader("some-header-param","some value");

// Send the request with in-memory XML Document "xmldoc" as body
xmlhttp.send(xmldoc);
// Return the response from the request (assumes it is an XML Doc)
return xmlhttp.responseXML;
}
The function does the following:
1. Creates an XMLHttpRequest Object
2. Opens the request, indicating a method of POST
3. Sends the request, passing the XML document as the request body
4. Returns the XML document sent back by the server as a response

If the XSQL Servlet encounters an <xsql:insert-request> action
element and there is no posted XML document in the current
request, it will replace the action element in the data page with
the following innocuous <xsql-status> element:
<xsql-status action="xsql:insert-request"
result="No Posted Document to Process" />
This will also be the case if any of the following is true:
• The XSQL page was requested as an HTTP GET.
• The MIME type of the HTTP POSTed document was not
text/xml, or the request didn't contain a valid XML
document.
• The request contained XML that was not well-formed.

We can use the PostXMLDocument( ) function in an HTML page by including the
PostXMLDocument.js JavaScript file in a <SCRIPT> tag like this:
<SCRIPT src="PostXMLDocument.js"></SCRIPT>
This is just what we've done in the newsstory.html page in Example 12.31
. It includes a
<TEXTAREA> containing some sample XML that you can edit, a submit button that invokes the

parseXMLinTextAreaAndPostIt( ) in its onclick event, and a <DIV> named StatusArea where
the results returned from the server will be displayed.
Example 12.31. NewsStory.html Page Posts XML to
SimpleNewsInsert.xsql

<HTML>
<HEAD>
<SCRIPT src="PostXMLDocument.js"></SCRIPT>
<SCRIPT>
function parseXMLinTextAreaAndPostIt( ){
// Create a new XML Parser Object
var xmldoc = new ActiveXObject ("Microsoft.XMLDOM");
// Do the parsing synchronously
xmldoc.async = false;
// Parse the text in the TEXTAREA as XML
xmldoc.loadXML(xmldocText.value);
// Post the parsed XML document to the SimpleNewsInsert.xsql Page
var response = PostXMLDocument(xmldoc, "SimpleNewsInsert.xsql");
// Display the XML text of the response in the "StatusArea" DIV
StatusArea.innerText = response.documentElement.xml;
}
</SCRIPT>
</HEAD>
<BODY>
<b>Type in an XML Document in Moreover.com News Format to Post:<b><br>
<TEXTAREA rows="7" style="width:100%" cols="70" name="xmldocText">
<moreovernews>
<article>
<url> </url>
<headline_text> Oracle Releases XML Parser </headline_text>

<source> Oracle </source>
</article>
</moreovernews></TEXTAREA>
<INPUT TYPE="button" Value="Post XML Document"
onclick=" parseXMLinTextAreaAndPostIt( )">
<br>Response:<hr>
<DIV id="StatusArea" style="font-family:monospace"></DIV>
</BODY>
</HTML>
Figure 12.6
shows the newsstory.html page in action. It allows the user to type in an XML
document in Moreover.com news format and post it to the SimpleNewsInsert.xsql page for
processing.
Figure 12.6. Posting XML to an XSQL page from IE5

We can see that clicking on the button has posted the <moreovernews> datagram to the
SimpleNewsInsert.xsql page. The text of the XML response datagram returned from the server is
displayed in the status area of the web page, confirming the successful insertion of one news
article.
In a real application, your client code could search the XML response using XPath expressions.
The returned XML datagram might include elements or attributes to signal whether the request
was successful or not, as well as other useful information.
In Internet Explorer 5.0, the selectNodes( ) function on any node of the document can assist
with this task. For example, we can add this code:
// Try to find the rows attribute on <xsql-status rows="xx"/>
var result = response.selectSingleNode("//xsql-status/@rows");
if ( result != null ) {
alert("Successfully posted " + result.text + " Stories ");
}
else {

// Try to message element child of an <xsql-error>
result = response.selectSingleNode("//xsql-error/message");
alert(result.text);
}
to the newsstory.html page just after the line that reads:
StatusArea.innerText = response.documentElement.xml;
you can see how easy it is to search the response for information and to do something with the
data sent back in the XML response from the server.
You may be wondering what happens if the user makes a mistake in typing the XML. The answer:
you get an error. On the client side, in this case, since the xmldoc.loadXML( ) method is
attempting to parse the text in the <TEXTAREA> into an XML document. However, if you used
another method to post an XML document to the server that is not well-formed, the server will
likely just ignore it; this is what the XSQL Servlet will do if you post ill-formed XML.
We can prevent this from happening by adding a little code to check for parse errors and showing
the user any errors it finds. Add the following code to the newsstory.html page:
err = xmldoc.parseError;
// Stop and show any parse error to the user
if (err != 0) {
StatusArea.innerText = "Your XML document is not well-formed.\n" +
err.srcText + "\n" + "Line " + err.line + ", Pos " + err.linepos +
"\n" + err.reason;
return;
}
just after the line that reads:
xmldoc.loadXML(xmldocText.value);
If the user makes a mistake in the XML document, a helpful error is displayed in the status area
on the browser, as shown in Figure 12.7
.
Figure 12.7. XML parse error displayed in the browser


Let's return now to posting news stories directly from the Moreover.com XML news feed. Using
the XSQL command-line utility we can insert the entirety of the live XML news feed using the
command:
xsql SimpleNewsInsert.xsql posted-xml=
page?index_xml+xml
This will treat the XML document retrieved from the provided URL as the posted XML to the
SimpleNewsInsert.xsql page, transform the results for insert, then perform the insert into the
newsstory table, returning the resulting data page that indicates the successful insert of 30 news
stories:
<?xml version = '1.0'?>
<! SimpleNewsInsert.xsql >
<xsql-status action="xsql:insert-request" rows="30"/>
However, let's say that due to the nature of this news feed, news stories stay in the feed for a few
days. If we want to avoid inserting the same story over and over again, we can easily do that by
making sure we don't insert a story unless its title and URL are a unique combination in our
newsstory table.
As we've done in previous examples, let's implement this behavior using a database INSTEAD OF
INSERT trigger. In the code of the trigger, we can check for the uniqueness of the news story and
only insert it if it is unique; otherwise, we'll just ignore it.
Since INSTEAD OF triggers can only be defined on database views in Oracle8i, we need to create
the newsstoryview as follows:
CREATE VIEW newsstoryview AS
SELECT *
FROM newsstory
Then we can create the INSTEAD OF INSERT trigger on the new newsstoryview with this code:
CREATE OR REPLACE TRIGGER insteadOfIns_newsstoryview
INSTEAD OF INSERT ON newsstoryview FOR EACH ROW
DECLARE
notThere BOOLEAN := TRUE;
tmp VARCHAR2(1);

CURSOR chk IS SELECT 'x'
FROM newsstory
WHERE title = :new.title
AND url = :new.url;
BEGIN
OPEN chk;
FETCH chk INTO tmp;
notThere := chk%NOTFOUND;
CLOSE chk;
IF notThere THEN
INSERT INTO newsstory(title,url,source)
VALUES (:new.title,:new.url,:new.source);
END IF;
END;
Here we are assuming that the uniqueness of a story is defined by the combination of its TITLE
and its URL columns.

To check for existing news stories quickly, we can create a
unique index on the (TITLE,URL) combination with the
command:
CREATE UNIQUE INDEX newsstory_unique_title_url
ON newsstory(title,url);

Finally, the only thing left to do is to change the <xsql:insert-request> action element in
SimpleNewsInsert.xsql above to use the newsstoryview instead of the newsstory table by
changing the line to read table="newsstoryview" instead of table="newsstory":
<xsql:insert-request table="newsstoryview"
transform="moreover-to-newsstory.xsl"/>
Now, only unique news stories from the Moreover.com XML news feed will be inserted. Duplicate
entries will be ignored when they are posted.

Our SimpleNewsInsert.xsql is a page with just a single <xsql:insert-request> action element.
Of course, the <xsql:insert-request> tag can be combined with other XSQL action elements in
a page like <xsql:query> to first insert any posted XML document and then return some data
from queries. For example, the following XSQL page inserts any news stories from the posted XML
document (if any) and then returns an XML datagram showing the two most recently added
(max-rows="2") news stories from the newsstory table:
<?xml version="1.0"?>
<page connection="xmlbook" xmlns:xsql="urn:oracle-xsql">
<xsql:insert-request table="newsstoryview"
transform="moreover-to-newsstory.xsl"/>
<lateststories>
<xsql:query tag-case="lower" max-rows="2"
rowset-element="" row-element="story" >
select *
from newsstoryview
order by id desc
</xsql:query>
</lateststories>
</page>
If this XSQL page is saved in a file called submitNewsStories.xsql, then browsing the URL:
http://yourserver/submitNewsStories.xsql
from Internet Explorer 5.0 produces the results shown in Figure 12.8
.
Figure 12.8. Browsing the raw data page for
submitNewsStories.xsql


In this case, there was "No Posted Document to Process" since a browser uses the HTTP GET
method to retrieve a page whose address you type into the "Address" field, and a GET method has
no request body, just URL parameters. However, if this new submitNewsStories.xsql page were

the target of an HTTP POST with an XML message body as in the newsstory.html page in Example
12.31, the raw XML message you see in the browser would be returned as the XML response
datagram to the requester. The requester could use XPath expressions to programmatically
process the two latest news stories.
As a final observation in this section, we see above that the raw XML datagram is returned to the
browser because our XSQL page has no <?xml-stylesheet?> processing instruction to associate
an XSL stylesheet with it. However, with the addition of one extra line in the XSQL page:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="latest-news-stories.xsl"?>
<page connection="xmlbook" xmlns:xsql="urn:oracle-xsql">
<! etc >
</page>
we could have an XSLT transformation called latest-news-stories.xsl format the <story>
elements in the data page on the server and return a nicely formatted HTML page to present the
results.
12.3.2 Posting and Inserting HTML Form Parameters
Sometimes, it's convenient to accept posted information as a set of HTML <FORM> parameters
since this is the native way browsers use to post information to a server. Consider the following
simple HTML page, which allows users to submit new news stories interactively from their
browser, as shown in Figure 12.9
.
Figure 12.9. HTML form collecting data to be inserted

Every HTML form specifies a URL to use as the "submit action" for the form. Typically, this action
URL refers to some kind of server-side program that processes the set of HTML <FORM>
parameters sent by the browser in the HTTP POST request:
<FORM METHOD="post" ACTION="/cgi-bin/somescript">
We can also use an XSQL page as the action URL for an HTML form:
<FORM METHOD="post" ACTION="/mydir/somepage.xsql">
This way, when the form is submitted by the user, the XSQL Servlet receives an HTTP POST

request for the XSQL page with a message body containing all the HTML <FORM> parameters. This
is not technically the same as receiving a posted XML document in the message body as we did in
the previous section, but the XSQL Servlet allows the <xsql:insert-request> action element to
work equally well in both cases.
In the case of an HTML form submission, if the XSQL Servlet encounters an
<xsql:insert-request> action in your page, it synthesizes an XML document representing the
HTTP request. This request document contains all of the HTTP request parameters, session
variables, and cookies for the current request, and has the general form:
<request>
<parameters>
<param1>value1</param1>
<param2>value2</param2>
:
</parameters>
<session>
<name1>val1</name1>
<name2>val2</name2>
:
</session>
<cookies>
<cookiename1>value1</cookiename1>
:
</cookies>
</parameters>
Multiple Parameters with the Same Name
If multiple parameters are posted with the same name, they will
automatically be "rowified" to make subsequent processing easier. For
example, a request that posts or includes the following parameters:
• id=101
• name=Steve

• id=102
• name=Sita
• operation=update
will create a "rowified" set of parameters:
<request>
<parameters>
<row>
<id>101</id>
<name>Steve</name>
</row>
<row>
<id>102</id>
<name>Sita</name>
</row>
<operation>update</operation>
</parameters>
:
</request>
Once this <request> document has been created, the processing of the <xsql:insert-request>
tag behaves just as if an actual XML document had been posted. The value of the transform
attribute can be the name of an XSLT transformation to be used for transforming the synthesized
<request> document into the canonical format required for insertion into the table or view you
specify in the table attribute.
So to get the HTML <FORM> parameters inserted into the newsstoryview, we need to create an
XSLT stylesheet to transform the <request> document into the canonical format for the
newsstoryview. It helps to have an example of an actual <request> document our HTML form
produces when it is submitted. The easiest way to achieve this is to temporarily change the action
URL of our HTML form to point to an XSQL page that will echo back the request document to the
browser. The XSQL Servlet supports an <xsql:include-request-params> tag that does exactly
this:

<?xml version="1.0"?>
<xsql:include-request-params xmlns:xsql="urn:oracle-xsql"/>
The <xsql:include-request-params> includes the synthesized <request> document for your
posted form and returns it verbatim to the browser for inspection. If we name this file
echoPostedParams.xsql, we can modify the source code of our earlier NewsForm.html page to
have echoPostedParams.xsql as the action URL of the HTML form, like this:
<html>
<body>
Insert a new news story
<form action="echoPostedParams.xsql" method="post">
<b>Title</b><input type="text" name="title_field" size="30"><br>
<b>URL</b><input type="text" name="url_field" size="30"><br>
<br>
<input type="submit">
</form>
<body>
</html>
If we browse the form, enter in some values into the Title and URL form fields, and submit the
form, the echoPostedParams.xsql page includes the <request> XML document for the current
form posting and returns the page back to your browser. From there, you can View Source and
save the source file to disk to work with as you build your XSLT transformation. In this case, we
get back a <request> document that looks like this:
<request>
<parameters>
<title_field>Test Story</title_field>
<url_field></url_field>
</parameters>
<session/>
<cookies/>
</parameters>

This reflects the two input fields in the HTML form, title_field and url_field. We need to
transform this request document into the following document:
<ROWSET>
<ROW>
<TITLE>Test Story</TITLE>
<URL></URL>
<SOURCE>Some Source</SOURCE>
</ROW>
</ROWSET>
The simple stylesheet in Example 12.32
will perform this transformation.
Example 12.32. Transforming Request Datagram to
ROWSET/ROW Format

<?xml version = '1.0'?>
<! request-to-newsstory.xsl >
<ROWSET xsl:version="1.0" xmlns:xsl="
<xsl:for-each select="request/parameters">
<ROW>
<TITLE><xsl:value-of select="title_field"/></TITLE>
<URL><xsl:value-of select="url_field"/></URL>
<SOURCE>User-Submitted</SOURCE>
</ROW>
</xsl:for-each>
</ROWSET>
Note that this stylesheet is nearly identical to Example 12.5. The only difference is that we're
looping over request/parameters instead of over moreovernews/article elements.
Now we just need an XSQL page—let's call it insertnewsform.xsql —with an
<xsql:insert-request> tag that refers to request-to-newsstory.xsl and provides a table name
of newsstoryview:

<xsql:insert-request
table="newsstoryview"
transform="request-to-newsstory.xsl"/>
This, again, is just a slight variation on what we used for inserting XML posted in the
Moreover.com news format into the newsstory table. By modifying our NewsForm.html source to
put the insertnewsform.xsql as the action URL as shown in Example 12.33
, we'll finish the
process.
Example 12.33. Inserting Posted HTML Form Parameters with
XSQL

<html>
<body>
Insert a new news story
<form action="insertnewsform.xsql" method="post">
<b>Title</b><input type="text" name="title_field" size="30"><br>
<b>URL</b><input type="text" name="url_field" size="30"><br>
<br>
<input type="submit">
</form>
<body>
</html>
If we let a user fill out and post the form as is, the user will get the following raw XML as a
response from the insertnewform.xsql page:
<xsql-status action="xsql:insert-request" rows="1"/>
This is surely not going to win awards for user-friendliness. However, we can use two familiar
techniques to improve on this:
• Enhance insertnewsform.xsql to return the five latest news stories from the
newsstoryview as part of the response by adding an <xsql:query> tag to the page as
follows:

• <?xml version="1.0"?>
• <?xml-stylesheet type="text/xsl" href="lateststories.xsl"?>
• <page connection="xmlbook" xmlns:xsql="urn:oracle-xsql">
• <xsql:insert-request table="newsstoryview"
• transform="request-to-newsstory.xsl"/>
• <xsql:query tag-case="lower" max-rows="5"
• rowset-element="" row-element="story" >
• SELECT *
• FROM newsstoryview
• ORDER BY ID DESC
• </xsql:query>
</page>
• Associate a lateststories.xsl stylesheet to the insertnewsform.xsql page to transform the
five latest news stories in the XSQL data page into an HTML document before returning it
to the user:
• <html xmlns:xsl=" xsl:version="1.0">
• <head>
• <title>Latest Stories</title>
• </head>
• <body>
• <h2>Thanks for your Story!</h2>
• Here's a list of the latest stories we've received
• <table border="0" cellspacing="0">
• <xsl:for-each select="page/story">
• <tr>
• <td><a href="{url}"><xsl:value-of select="title"/></a></td>
• </tr>
• </xsl:for-each>
• </table>
• </body>

</html>
Now, when the user posts a new news story, the raw XML datagram returned by the
insertnewsform.xsql page will be replaced by the HTML page, as shown in Figure 12.10
.
Figure 12.10. Transformed results of posting a new news
story


12.4 Inserting Datagrams Using Java
In this section, we'll study how the previous techniques can be applied from within your own Java
programs.
12.4.1 Inserting Arbitrary XML Using Java
We've learned so far that the Oracle XML SQL Utility can be used to insert XML datagrams into
database tables and views. We used the command-line oraxsl utility to transform the XML
datagram into canonical <ROWSET>/<ROW> format before feeding it to the OracleXML utility for
insertion into the database. Later, we saw how a simple <xsql:insert-request> action element
could be used in an XSQL page to accomplish the same thing. The XSQL Servlet is able to
automate these steps since both the Oracle XSLT Processor (oraxsl) and the Oracle XML SQL
Utility (OracleXML) can be used programmatically by any Java program.
The API for the Oracle XSLT Processor comprises two simple-to-use objects, XSLStylesheet and
XSLProcessor, and the API for the Oracle XML SQL Utility is even simpler. The OracleXMLSave
object takes care of inserting XML into the database for us. Here we'll look at an example of using
these three objects in a simple Java program to insert the contents of a live Moreover.com news
story datagram fetched directly over the Web into our newsstoryview.
Example 12.34
does the following to accomplish this feat:
1. Parses the live XML news feed from Moreover.com by calling the parse() method of a
DOMParser object, passing it a string URL to the live news feed:
2. // Create a DOM Parser to Parse the News Document
3. DOMParser dp = new DOMParser( );

4. dp.parse( theNews );
5. // Parse the document at the URL specified in theURLString
XMLDocument moreoverNewsDoc = dp.getDocument( );
6. Searches the XML datagram retrieved to count how many articles were received by using
the selectNodes() method on the XML Document object, passing the XPath expression of
moreover/article:
7. // Search for a list of all the matching articles and print the count
8. NodeList nl = moreoverNewsDoc.selectNodes("moreovernews/article");
9. int articleCount = nl.getLength( );
System.out.println("Received " + articleCount + " articles ");
10. Constructs a new XSLStylesheet object by finding the moreover-to-newsstory.xsl
transformation source file in the CLASSPATH using getResourceAsStream( ):
11. // Load the XSL Stylesheet from the top-level directory on CLASSPATH
12. InputStream xslstream = Object.class.getResourceAsStream("/"+theXSL);
XSLStylesheet transform = new XSLStylesheet(xslstream,null);
13. Constructs an XSLProcessor object and calls processXSL on it to transform the incoming
XML news feed document into the canonical format that OracleXMLSave understands
using the XSL transformation:
14. // Create an instance of XSLProcessor to perform the transformation
15. XSLProcessor processor = new XSLProcessor( );
16. // Transform moreoverNewsDoc by theXSL and get result as a DOM Document
Fragment
17. DocumentFragment df = processor.processXSL(transform, moreoverNewsDoc);
18. // Create a new XML Document and append the fragment to it
19. Document result = new XMLDocument( );
result.appendChild(df);
20. Constructs an OracleXMLSave object, passing it a JDBC connection and the name of the
newsstoryview we want to use for the insert operation:
21. // Pass the transformed document (now in canonical format) to OracleXMLSave
22. Connection conn = Examples.getConnection( );

OracleXMLSave oxs = new OracleXMLSave(conn,"newsstoryview");
23. Calls insertXML on the OracleXMLSave object, passing the transformed XML document in
canonical format:
int rowsInserted = oxs.insertXML( result );
24. Commits the transaction and closes the connection:
25. conn.commit( );
conn.close( );
The result is the MoreoverIntoNewsStory.java program, which could be used to periodically pick
up news feeds over the Web and dump them into your database. The full source is shown in
Example 12.34.
Example 12.34. Programmatically Inserting News Stories
import oracle.xml.parser.v2.*;
import java.io.InputStream;
import org.w3c.dom.*;
import java.net.URL;
import java.sql.Connection;
import oracle.xml.sql.dml.OracleXMLSave;
import Examples;

public class MoreoverIntoNewsstory {

public static void main( String[] arg ) throws Exception {

String theNews = "
theXSL = "moreover-to-newsstory.xsl";

// Create a DOM Parser to parse the news document
DOMParser dp = new DOMParser( );
dp.parse( theNews );
// Parse the document at the URL specified in theURLString

XMLDocument moreoverNewsDoc = dp.getDocument( );
// Search for a list of all the matching articles and print the count
NodeList nl = moreoverNewsDoc.selectNodes("moreovernews/article");
int articleCount = nl.getLength( );
System.out.println("Received " + articleCount + " articles ");
// Load the XSL Stylesheet from the top-level directory on CLASSPATH
InputStream xslstream = Object.class.getResourceAsStream("/"+theXSL);
XSLStylesheet transform = new XSLStylesheet(xslstream,null);
// Create an instance of XSLProcessor to perform the transformation
XSLProcessor processor = new XSLProcessor( );
// Transform moreoverNewsDoc by theXSL and get result as a DOM Document Fragment
DocumentFragment df = processor.processXSL(transform, moreoverNewsDoc);
// Create a new XML Document and append the fragment to it
Document result = new XMLDocument( );
result.appendChild(df);
// Pass the transformed document (now in canonical format) to OracleXMLSave
Connection conn = Examples.getConnection( );
OracleXMLSave oxs = new OracleXMLSave(conn,"newsstoryview");
int rowsInserted = oxs.insertXML( result );

×