home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / xampp / xampp-cocoon-addon-1.4.9-installer.exe / sql-transformer.xml < prev    next >
Encoding:
Extensible Markup Language  |  2004-07-12  |  20.1 KB  |  538 lines

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!--
  3.   Copyright 1999-2004 The Apache Software Foundation
  4.  
  5.   Licensed under the Apache License, Version 2.0 (the "License");
  6.   you may not use this file except in compliance with the License.
  7.   You may obtain a copy of the License at
  8.  
  9.       http://www.apache.org/licenses/LICENSE-2.0
  10.  
  11.   Unless required by applicable law or agreed to in writing, software
  12.   distributed under the License is distributed on an "AS IS" BASIS,
  13.   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.   See the License for the specific language governing permissions and
  15.   limitations under the License.
  16. -->
  17. <!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.0//EN" "document-v10.dtd">
  18. <document>
  19.   <header>
  20.     <title>SQL Transformer</title>
  21.     <version>0.9</version>
  22.     <authors>
  23.       <person name="Sven Beauprez" email="Sven.Beauprez@the-ecorp.com"/>
  24.       <person name="Davanum Srinivas" email="dims@yahoo.com"/>
  25.     </authors>
  26.   </header>
  27.   <body>
  28.     <s1 title="Introduction">
  29.       <p>
  30.         The purpose of the SQLTransformer is to query a database and translate
  31.         the result to XML. To retrieve the information from the database, you
  32.         are not restricted to use simple SQL statements (e.g. select, insert,
  33.         update), it is also possible to use stored procedures. In combination
  34.         with other transformers (e.g. FilterTransformer), this one can be very
  35.         powerful.
  36.       </p>
  37.       <ul>
  38.         <li>Name: sql</li>
  39.         <li>Class: org.apache.cocoon.transformation.SQLTransformer</li>
  40.         <li>Cacheable: no</li>
  41.       </ul>
  42.     </s1>
  43.     <s1 title="Basic functionality">
  44.       <p>
  45.         To be able to query a database, we need XML that describes exactly what
  46.         we want to do. The general structure of this input XML is as follows:
  47.       </p>
  48.       <source>
  49.       <![CDATA[
  50.   <page>
  51.     <execute-query xmlns="http://apache.org/cocoon/SQL/2.0"> 
  52.       <query>
  53.       <!-- here comes the SQL statement or stored procedure -->
  54.       </query>
  55.     </execute-query>
  56.   </page>
  57.    ]]></source>
  58.       <p>
  59.         Nothing prevents you from putting other XML around the
  60.         <code>execute-query</code> element. Any element not in the SQL namespace
  61.         will stay untouched. The format of the SQL statement or the stored
  62.         procedure is exactly the same as if you would call it directly from java
  63.         with a prepared statement or a callable statement.
  64.       </p>
  65.       <p>The query element has the following optional attributes:</p>
  66.       <ol>
  67.         <li>
  68.           <strong>name</strong>:
  69.           Naming a query implicates naming the corresponding rowset (see below).
  70.           When you have a sequence of queries you want to execute, it can be
  71.           handy give them a name. To process the retrieved data of a certain
  72.           query, you can use another transformer to check the name of the rowset
  73.           and to execute the necessary business logic on it.
  74.           <br/>
  75.           usage: <code><query name="myName"></code>
  76.         </li>
  77.         <li>
  78.           <strong>isstoredprocedure</strong>:
  79.           When you want to use stored procedures, you have to explicitly add
  80.           this attribute to the query element. By default, the transformer
  81.           assumes that you want to execute a SQL statement.
  82.           <br/>
  83.           usage: <code><query isstoredprocedure="true"></code>
  84.         </li>
  85.       </ol>
  86.       <p>Here is an example of how the input XML might look like:</p>
  87.       <source>
  88.       <![CDATA[
  89.   <page>
  90.    <title>Hello</title>
  91.    <content>
  92.     <para>This is my first Cocoon page filled with sql data!</para>
  93.     <execute-query xmlns="http://apache.org/cocoon/SQL/2.0"> 
  94.      <query name="department">
  95.           select id,name from department_table 
  96.      </query>
  97.     </execute-query>
  98.    </content>
  99.   </page>
  100.    ]]></source>
  101.       <p>
  102.         You can use the file generator to retrieve the XML from the filesystem.
  103.         To invoke the SQLTransformer you have to add following to the sitemap:
  104.       </p>
  105.       <source>
  106.       <![CDATA[
  107.   <map:transform type="sql">
  108.     <map:parameter name="use-connection" value="personnel"/>
  109.     <map:parameter name="show-nr-of-rows" value="true"/> 
  110.     <map:parameter name="clob-encoding" value="UTF-8"/> 
  111.   </map:transform>
  112.    ]]></source>
  113.       <p>
  114.         The <code>use-connection</code> parameter defines which connection,
  115.         defined under the datasources element in <code>cocoon.xconf</code>, the
  116.         SQLTransformer has to use to retrieve the data.
  117.       </p>
  118.       <p>
  119.         The <code>show-nr-of-rows</code> instructs the transformer to count the
  120.         number of rows in the resultset explicitly and to set the result as
  121.         attribute to the rowset element. This attribute is only useful in
  122.         combination with a sql statement, not with stored procedures. If a
  123.         stored procedure returns a resultset and you want to know how many rows
  124.         it contains, you have to count the number of rows in another transformer
  125.         or your stored procedure has to return it also (last solution is the
  126.         best one).
  127.       </p>
  128.       <p>
  129.         The <code>clob-encoding</code> parameter defines what encoding should be
  130.         used in getting content from CLOB columns.
  131.       </p>
  132.       <p>The output XML will look as follows:</p>
  133.       <source>
  134.       <![CDATA[
  135.   <page>
  136.    <title>Hello</title>
  137.    <content>
  138.     <para>This is my first Cocoon page filled with sql data!</para>
  139.     <rowset nrofrows="2" name="department" 
  140.             xmlns="http://apache.org/cocoon/SQL/2.0">
  141.       <row>
  142.         <id>1</id>
  143.         <name>Programmers</name>
  144.       </row>
  145.       <row>
  146.         <id>2</id>
  147.         <name>Loungers</name>
  148.       </row>
  149.     </rowset>
  150.    </content>
  151.   </page>
  152.    ]]></source>
  153.       <p>
  154.         If you use this in combination with the <code>simple-sql2html.xsl</code>
  155.         stylesheet,
  156.       </p>
  157.       <source>
  158.       <![CDATA[
  159.   <map:transform src="stylesheets/simple-sql2html.xsl"/>
  160.    ]]></source>
  161.       <p>you will get a more visually attractive page.</p>
  162.       <p>See below for a more in depth example with stored procedures.</p>
  163.       <p>
  164.         By now you should be able to use the SQLTransformer, but there are some
  165.         more options you might find useful...
  166.       </p>
  167.     </s1>
  168.     <s1 title="Advanced functionality">
  169.       <s2 title="Substitution">
  170.         <p>
  171.           Sometimes you need more information before you can execute a query,
  172.           e.g. the name of the user that is currently logged on your site. This
  173.           information is only available at runtime and hence can only be
  174.           substituted in the query when available.
  175.         </p>
  176.         <p>
  177.           To pass this information to the SQL statement, the input XML has to
  178.           look like this:
  179.         </p>
  180.         <source>
  181.         <![CDATA[
  182.   <page xmlns:sql="http://apache.org/cocoon/SQL/2.0">
  183.     <execute-query xmlns="http://apache.org/cocoon/SQL/2.0">
  184.       <query>
  185.        select id,name from employee_table where name =
  186.                 '<sql:substitute-value sql:name="username"/>'
  187.       </query>
  188.     </execute-query>
  189.   </page>
  190.      ]]></source>
  191.         <p>
  192.           The substitution is done by the SQLTransformer before it executes the
  193.           query (before it calls the method <code>prepareStatement</code>!). For
  194.           this, the transformer has to be given the necessary values via the
  195.           sitemap (as parameter):
  196.         </p>
  197.         <source>
  198.         <![CDATA[
  199.   <map:transform type="sql">
  200.     <map:parameter name="use-connection" value="personnel"/>
  201.     <map:parameter name="show-nr-of-rows" value="true"/> 
  202.     <map:parameter name="username" value="Stefano Mazzocchi"/>
  203.   </map:transform>
  204.      ]]></source>
  205.         <p>
  206.           Whenever the transformer encounters a <code>substitute-value</code>
  207.           element for which the attribute <code>name</code> contains the value
  208.           <code>username</code>, it will replace this element with the value
  209.           <code>Stefano Mazzocchi</code>.
  210.         </p>
  211.         <p>The output XML will be as follow:</p>
  212.         <source>
  213.         <![CDATA[
  214.   <page xmlns:sql="http://apache.org/cocoon/SQL/2.0">
  215.     <rowset nrofrows="1" xmlns="http://apache.org/cocoon/SQL/2.0">
  216.       <row>
  217.         <id>2</id>
  218.         <name>Stefano Mazzocchi</name>
  219.       </row>
  220.     </rowset>
  221.   </page>
  222.      ]]></source>
  223.         <p>
  224.           It is also possible to use substitution in combination with stored
  225.           procedures.
  226.         </p>
  227.       </s2>
  228.       <s2 title="Ancestors">
  229.         <p>This functionality is best described by a simple example.</p>
  230.         <p>Take following input XML:</p>
  231.         <source>
  232.         <![CDATA[
  233.   <page xmlns:sql="http://apache.org/cocoon/SQL/2.0">
  234.     <execute-query xmlns="http://apache.org/cocoon/SQL/2.0">
  235.      <query name="department">
  236.           select id,name from department_table
  237.      </query>
  238.      <execute-query>
  239.       <query name="employee">
  240.        select id,name from employee_table where department_id =
  241.                   <ancestor-value sql:name="id" sql:level="1"/>
  242.       </query>
  243.      </execute-query>
  244.     </execute-query>
  245.   </page>
  246.      ]]></source>
  247.         <p>
  248.           The first query will retrieve all <code>id</code>'s and
  249.           <code>name</code>'s from the <code>department_table</code> table. For
  250.           each <code>id</code> that comes from the
  251.           <code>department_table</code>, the second query, in which the
  252.           <code>ancestor-value</code> element will be replaced by the
  253.           <code>id</code>, will be executed. The above example will be
  254.           transformed to the following XML:
  255.         </p>
  256.         <source>
  257.         <![CDATA[
  258.   <page xmlns:sql="http://apache.org/cocoon/SQL/2.0">
  259.     <rowset nrofrows="2" name="department" 
  260.             xmlns="http://apache.org/cocoon/SQL/2.0">
  261.       <row>
  262.         <id>1</id>
  263.         <name>Programmers</name>
  264.         <rowset nrofrows="2" name="employee">
  265.           <row>
  266.             <id>1</id>
  267.             <name>Donald Ball</name>
  268.           </row>
  269.           <row>
  270.             <id>2</id>
  271.             <name>Stefano Mazzocchi</name>
  272.           </row>
  273.         </rowset>
  274.       </row>
  275.       <row>
  276.         <id>2</id>
  277.         <name>Loungers</name>
  278.         <rowset nrofrows="1" name="employee">
  279.           <row>
  280.             <id>3</id>
  281.             <name>Pierpaolo Fumagalli</name>
  282.           </row>
  283.         </rowset>
  284.       </row>
  285.     </rowset>
  286.   </page>
  287.      ]]></source>
  288.       </s2>
  289.       <s2 title="in- and out-parameters">
  290.         <p>
  291.           Stored procedures can return data as a parameter. To make use of this
  292.           functionality in java, you have to register these parameters as
  293.           <em>out parameters</em>. Since this information is application
  294.           specific, the SQLTransformer uses reflection to retrieve the data in
  295.           the right format. For this, an extra element is needed in the input
  296.           XML:
  297.         </p>
  298.         <source>
  299.         <![CDATA[
  300.   <out-parameter sql:nr="1" sql:name="code"
  301.                  sql:type="java.sql.Types.INTEGER"/>
  302.      ]]></source>
  303.         <p>where:</p>
  304.         <ol>
  305.           <li>
  306.             <strong>nr</strong>:
  307.             The targeted parameter number that will return data of a certain
  308.             type.
  309.           </li>
  310.           <li>
  311.             <strong>type</strong>:
  312.             The type of data that will be returned (defined in
  313.             <code>java.sql.Types</code> or in database specific drivers, e.g.
  314.             <code>oracle.jdbc.driver.OracleTypes</code>). Once the stored
  315.             procedure returns data in the parameters, the stored procedure tries
  316.             to process them. If the returned parameter is an instance of
  317.             <code>ResultSet</code>, it will be translated to XML as we saw
  318.             before. In all the other situations the SQLTransformer will convert
  319.             the parameter to a string.
  320.           </li>
  321.         </ol>
  322.         <p>
  323.           This is an example of how to call an oracle stored procedure and
  324.           process it with the SQLTransformer:
  325.         </p>
  326.         <source>
  327.         <![CDATA[
  328.   <page xmlns:sql="http://apache.org/cocoon/SQL/2.0">
  329.     <execute-query xmlns="http://apache.org/cocoon/SQL/2.0">
  330.       <query isstoredprocedure="true" name="namesearch">
  331.           begin QUICK_SEARCH.FIND_NAME('<sql:substitute-value
  332.                           sql:name="username"/>',?,?,?); end;
  333.       </query>
  334.       <out-parameter sql:nr="1" sql:name="code"
  335.                      sql:type="java.sql.Types.INTEGER"/>
  336.       <out-parameter sql:nr="2" sql:name="nrofrows"
  337.                      sql:type="java.sql.Types.INTEGER"/>
  338.       <out-parameter sql:nr="3" sql:name="resultset"
  339.                      sql:type="oracle.jdbc.driver.OracleTypes.CURSOR"/>
  340.     </execute-query>
  341.   </page>
  342.      ]]></source>
  343.         <p>
  344.           The SQLTransformer will create 3 elements, respectively
  345.           <code>code</code>, <code>nrofrows</code> and <code>resultset</code>
  346.           under the element <code>namesearch</code>. Since the type
  347.           <code>oracle.jdbc.driver.OracleTypes.CURSOR</code> corresponds to a
  348.           <code>ResultSet</code>, a <code>rowset</code> element will be created,
  349.           containing all the data of the resultset. It is also possible to use
  350.           an <em>in-parameter</em> element, e.g.
  351.           <code><in-parameter sql:nr="1" sql:value="1"/></code>. This
  352.           functionality is only provided to be complete, because it is available
  353.           in Java itself. You can also use the <em>in-parameter</em> in
  354.           combination with a SQL statement. Used in combination with an
  355.           <em>out-parameter</em>, a <em>?-parameter</em> can be an
  356.           <em>in-parameter</em> and an <em>out-parameter</em> at the same time.
  357.         </p>
  358.       </s2>
  359.     </s1>
  360.     <s1 title="Combined with other transformers">
  361.       <s2 title="Filtertransformer">
  362.         <p>
  363.           When you query a database and it returns too many rows to process at
  364.           once, you might want to take a block of elements, process this block
  365.           and ignore the rest for now. You can best compare it to a search on
  366.           Google: they only return 10 results in one time, for more results you
  367.           have to click on another block (page). It wouldn't be wise to process
  368.           more than 10 elements in the pipeline if you only need to display 10
  369.           elements.
  370.         </p>
  371.         <p>
  372.           Assume that a query returns 56 row elements (by using the
  373.           SQLTransformer) and that you only want to display the first 10
  374.           elements:
  375.         </p>
  376.         <p>Output XML from the SQLTransformer:</p>
  377.         <source>
  378.         <![CDATA[
  379.   <rowset nrofrows="56" name="test"
  380.           xmlns="http://apache.org/cocoon/SQL/2.0">
  381.     <row>
  382.       <!-- db record -->
  383.     </row>
  384.     <row>
  385.       <!-- db record -->
  386.     </row>
  387.  
  388.     ...
  389.  
  390.     <row>
  391.       <!-- db record -->
  392.     </row>
  393.   </rowset>
  394.      ]]></source>
  395.         <p>
  396.           By adding following lines to the sitemap, just under the
  397.           SQLTransformer, you restrict the results to 10 elements in the first
  398.           block:
  399.         </p>
  400.         <source>
  401.         <![CDATA[
  402.   <map:transform type="filter">
  403.     <map:parameter name="element-name" value="row"/>
  404.     <map:parameter name="count" value="10"/>
  405.     <map:parameter name="blocknr" value="1"/>
  406.   </map:transform>
  407.      ]]></source>
  408.         <p>output XML:</p>
  409.         <source>
  410.         <![CDATA[
  411.   <rowset nrofrows="56" name="test"
  412.           xmlns="http://apache.org/cocoon/SQL/2.0">
  413.     <block id="1">
  414.       <row>
  415.         <!-- db record -->
  416.       </row>
  417.  
  418.       <!-- total of 10 rows -->
  419.  
  420.       <row>
  421.         <!-- db record -->
  422.       </row>
  423.     </block>
  424.     <block id="2"/>
  425.     <block id="3"/>
  426.     <block id="4"/>
  427.     <block id="5"/>
  428.     <block id="6"/>
  429.   </rowset>
  430.      ]]></source>
  431.         <p>
  432.           To make it more dynamically, put something like
  433.           <code>{reqCount}</code> and <code>{reqBlock}</code> in the values for
  434.           <em>count</em> and <em>blocknr</em> respectively. These can be
  435.           parameters from the request and they can be passed to the sitemap with
  436.           an action.
  437.         </p>
  438.         <p>
  439.           The FilterTransformer is a standalone component; you don't need to use
  440.           it in combination with the SQLTransformer.
  441.         </p>
  442.       </s2>
  443.       <s2 title="WriteDOMSessionTransformer">
  444.         <p>
  445.           If you only use the FilterTransformer in combination with the
  446.           SQLTransformer, you have to query the database each time the user
  447.           wants to see another part of the result. You can better store the
  448.           result in the session after the first request and retrieve the result
  449.           from the session for the subsequent requests. This can be done by
  450.           using a selector, which checks if the data is available in the session
  451.           or not.
  452.         </p>
  453.         <p>
  454.           WriteDOMSessionTransformer can build a DOM starting from a given
  455.           element (which will be the root of the DOM tree) and store it in the
  456.           session. If you want to store the result of a query, you have to add
  457.           following to the sitemap:
  458.         </p>
  459.         <source>
  460.         <![CDATA[
  461.   <map:transform type="writeDOMsession">
  462.     <map:parameter name="dom-name" value="DBresult"/>
  463.     <map:parameter name="dom-root-element" value="rowset"/>
  464.   </map:transform>
  465.      ]]></source>
  466.         <p>
  467.           The transformer will build a DOM tree with <code>rowset</code> as root
  468.           element and will store it in the session with the name
  469.           <code>DBresult</code>.
  470.         </p>
  471.         <note>
  472.           Most of the times, it is not smart to keep the output XML of the
  473.           SQLTransformer in the session. Check if it is better to do the
  474.           necessary transformations first, so that you get a smaller DOM, and
  475.           then put the result in the session. You probably will be able to use
  476.           the FilterTransformer on the transformed XML also.
  477.         </note>
  478.         <p>
  479.           The WriteDOMSessionTransformer is a standalone component, you don't
  480.           need to use it in combination with the SQLTransformer.
  481.         </p>
  482.       </s2>
  483.       <s2 title="ReadDOMSessionTransformer">
  484.         <p>
  485.           Simply transforms a DOM to SAX events, which can be used further on in
  486.           the pipeline. Once you stored the result of a query in the session
  487.           with the WriteDOMSessionTransformer, you can read it again with the
  488.           ReadDOMSessionTransformer:
  489.         </p>
  490.         <source>
  491.         <![CDATA[
  492.   <map:transform type="readDOMsession">
  493.     <map:parameter name="dom-name" value="DBresult"/>
  494.     <map:parameter name="trigger-element" value="users"/>
  495.     <map:parameter name="position" value="after"/>
  496.   </map:transform>
  497.      ]]></source>
  498.         <p>
  499.           In this example the SAX events, that come from the DOM tree stored in
  500.           the session with name <code>DBresult</code>, will be added after the
  501.           <code>users</code> element. This means as soon that the transformer
  502.           encounters the end element <code>users</code>, it will start to
  503.           generate SAX events from the DOM tree. There are three possible
  504.           positions, <code>before</code>, <code>in</code> and
  505.           <code>after</code>:
  506.         </p>
  507.         <ol>
  508.           <li>
  509.             <strong><code>before</code></strong> means that when the transformer
  510.             encounters the <code>users</code> element, it will FIRST translate
  511.             the DOM tree to SAX events and THEN it will continue to forward the
  512.             other SAX events (starting with <code>users</code>).
  513.           </li>
  514.           <li>
  515.             <strong><code>in</code></strong> means that the transformer will
  516.             forward the start element event for <code>users</code> and that it
  517.             IMMEDIATELY starts to generate SAX events from the DOM tree. After
  518.             that, it will continue to forward the child elements of users and
  519.             then all the other elements.
  520.           </li>
  521.           <li>
  522.             <strong><code>after</code></strong> means that the transformer
  523.             starts to generate SAX events from the DOM tree just after it has
  524.             forwarded the end element <code>users</code>.
  525.           </li>
  526.         </ol>
  527.         <p>
  528.           The ReadDOMSessionTransformer is a standalone component, you don't
  529.           need to use it in combination with the WriteDOMSessionTransformer.
  530.         </p>
  531.       </s2>
  532.       <p>That's it,</p>
  533.       <p>Sven Beauprez</p>
  534.     </s1>
  535.   </body>
  536. </document>
  537.  
  538.