1. Trang chủ
  2. » Công Nghệ Thông Tin

Oracle XSQL- P23 doc

20 114 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 20
Dung lượng 492,6 KB

Nội dung

<head></head> <body> <xsl:apply-templates/> </body> </html> </xsl:template> <xsl:template match=”request”> <b>Request:</b> <xsl:apply-templates select=”*”/> </xsl:template> <xsl:template match=”session”> <b>Session:</b> <xsl:apply-templates select=”*”/> </xsl:template> <xsl:template match=”cookies”> <b>Cookies:</b> <xsl:apply-templates select=”*”/> </xsl:template> <xsl:template match=”*”> <p> Name: <xsl:value-of select=”name(.)”/> Value: <xsl:value-of select=”.”/> </p> </xsl:template> </xsl:stylesheet> This will give you a listing of all of the parameters, regardless of their names. Exam- ple output for this is shown in Figure 14.11. From here, it is a simple matter to create a set of hidden-form parameters that hold these values. Then you can use easily pass the values on as form submissions, or use them in JavaScript. Here is how you do it: <xsl:template match=”*”> <input type=”hidden”> <xsl:attribute name=”name”><xsl:value-of select=”name(.)”/></xsl:attribute> <xsl:attribute name=”value”><xsl:value-of select=”.”/></xsl:attribute> </input> You’ll see more examples of hidden-form variables and the role they can play in JavaScript and XSLT integration when you work with the price editor later in the chapter. 420 Chapter 14 Figure 14.11 Output of all parameters. Stateless Paging So far, the product catalog doesn’t have a lot of data in it. A list of all of the products in the catalog probably still wouldn’t necessitate a scroll bar in the browser window. However, what if your queries could easily return thousands of rows? Even if your queries are just returning tens of rows, you want your users to be able to navigate through them in a reasonable manner. This is where paging comes in. Instead of show- ing all of the results on one page, you see just a set of results, and then you can navi- gate forward and backward to other sets of results. This section looks at the challenges of pagination from a high level and then shows you how to apply paging to the search screen of the catalog application. Building XSQL Web Applications 421 The solution that will be provided here is a pure XSQL/XSLT solution. The example uses a recursive technique and really stretches the capabilities of XSLT. However, when you find yourself stretching too hard, it is often good to ask, “Is there a better way to do this?” In this case, the answer is probably yes. Challenges of Pagination Paging offers a simple solution to a common problem. What do you do when you get a lot of results back in your search? You don’t want to try to force them all into one browser window. For particularly big searches the browser might run out of memory first. Instead, you want to separate the queries over pages. The next question is: How do you separate a query into a lot of different pages? You definitely don’t want to keep a connection to the database server open. It doesn’t scale. You’ll be keeping connections open for indefinite periods of time for all of the users that come to the site. Besides, there isn’t a way to do it without a custom action handler, anyway. Temporary tables are also a generally bad idea for this kind of thing. You might be able to cobble a solution to the problem, but you’ll create big development headaches for yourself. Not to mention, you’ll need some multiple of storage space to keep all of the pages of results for the queries. As you get more queries, you’ll need more storage space. Fortunately, the xsql:query action offers an easy solution. You use the max-rows and skip-rows attributes to describe your pages of a query. The max-rows attribute sets the size of the page—how many rows you want the query to return. The skip -rows attribute specifies where in the query result set you should start—the position of the page. This does mean, of course, that you will be doing the same query for each page. This may seem expensive and redundant, but it is Oracle’s job to optimize queries and make subsequent requests speedy. Besides, you simply can’t reasonably keep the data- base connection open, and even if you tried to hack together a solution with temporary tables, you still would be doing a query for every page. Pure XSQL Stateless Paging Now it’s time to attack stateless paging. As with most of the problems in this chapter, it is both an XSQL and an XSLT problem. The XSQL code is fairly straightforward. You need to set the skip-rows and max-rows attributes on the query and get the total number of rows from the database. Then, you need to pass the information on the stylesheet by setting stylesheet parameters. Not hard. The XSLT gets a bit more com- plex. Because you don’t know how many pages you are going to have, you have to cal- culate that using XSLT. This requires a recursive template. You’ll probably either love 422 Chapter 14 this technique or hate it. In either case, it is an interesting exercise in how you can solve problems in XSLT that are usually solved in XSLT. First things first, starting with the XSQL. You’ll need to change your query to use skip-rows and max-rows as follows: <xsql:query rowset-element=”PRODUCT_SEARCH” row-element=”PRODUCT” max-rows=”{@max-rows}” skip-rows=”{@skip-rows}”> SELECT p.id AS product_id, p.name AS product_name, p.doc.extract(‘/product/summary/text()’).getStringVal() AS summary, pc.id AS category_id, pc.name AS category FROM product p, product_category pc WHERE contains(doc,’{@search_terms}’)>0 AND pc.id=p.category_id ORDER BY contains(doc,’{@search_terms}’) </xsql:query> This introduces a new parameter: skip-rows. Skip-rows, which is where a given page should start, will be passed as part of the request. Max-rows, which is the maxi- mum number of records that should be displayed for all of the pages, is really more of a constant. It is made a parameter here so that if you wanted to dynamically change it, you could. Maybe you let your users select how big they want their pages to be. For this example, we set the value of this parameter inside the XSQL page with an xsql:set-page-param action: <xsql:set-page-param name=”max-rows” value=”4”/> The skip-rows parameter still needs to be defined. It should be passed in on all requests, including the initial search. This means you need to make a change in the banner.xsl page where the search query form is defined. The new form should look like this: <form action=”prod-search.xsql” method=”post”> <span class=”banner-subtitle”> Search: <input name=”search_terms” size=”30”/> <input type=”hidden” name=”skip-rows” value=”0”/> <input type=”submit” value=”go!”></input> </span> </form> Building XSQL Web Applications 423 Now your query will work for the first page. Regardless of the search, you should get at most four results. The next challenge is giving the user a way to navigate to the other results. This is where the real XSLT adventures start. All of these modifications will take place in prod-search.xslt. Your first step is to create top-level parameters so you can receive the values that you set in prod-search.xsql: <xsl:param name=”search_terms”></xsl:param> <xsl:param name=”row-count”></xsl:param> <xsl:param name=”skip-rows”></xsl:param> <xsl:param name=”max-rows”></xsl:param> Now you need to create a template that recreates the initial form using all hidden parameters: <xsl:template name=”paging-form”> <xsl:param name=”new-skip-rows”></xsl:param> <xsl:param name=”button-text”></xsl:param> <form method=”post” action=”prod-search.xsql”> <input type=”hidden” name=”skip-rows”> <xsl:attribute name=”value”><xsl:value-of select=”number($new-skip- rows)”/></xsl:attribute> </input> <input type=”hidden” name=”search_terms”> <xsl:attribute name=”value”><xsl:value-of select=”$search_terms”/></xsl:attribute> </input> <input type=”submit”> <xsl:attribute name=”value”><xsl:value-of select=”$button- text”/></xsl:attribute> <xsl:if test=”$new-skip-rows=-1”> <xsl:attribute name=”disabled”>yes</xsl:attribute> </xsl:if> </input> </form> </xsl:template> You call this template with two parameters, new-skip-rows and button-text. The new-skip-rows parameter specifies the position in the query that the user should go to on pressing the form’s Submit button. The button-text parameter specifies the text for the button. If the new_skip_rows parameter is set to -1, then the Form button is disabled. You can get around the need to use a Form button with a little JavaScript, but it’s dif- ficult to get around the need to use a form. The query needs to be a POST query because search_terms may have spaces and other funny characters in it. If you tried to pass it as part of a query string, the link would break. In the next section, you’ll see a way to encode values using XSLT extensions. Now that you have your form, you need to link it to some navigational elements. We’ll start simply with just Previous and Next buttons. The problem here is determin- ing what the Previous and Next pages are and whether it is valid to enable the buttons. 424 Chapter 14 If you are at the beginning of the result set, then you shouldn’t display a Previous but- ton; if you are at the end of a result set, you shouldn’t display a Next button. Here is the code that calculates the value of skip-rows for Previous and Next and determines whether the buttons should be disabled: <xsl:template name=”paging”> <table><tr><td> <xsl:choose> <xsl:when test=”number($skip-rows)-number($max-rows)&gt;=0”> <xsl:call-template name=”paging-form”> <xsl:with-param name=”new-skip-rows”><xsl:value-of select=”number($skip-rows)-number($max-rows)”/></xsl:with-param> <xsl:with-param name=”button-text”>&lt;&lt;Previous</xsl:with- param> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:call-template name=”paging-form”> <xsl:with-param name=”new-skip-rows”>-1</xsl:with-param> <xsl:with-param name=”button-text”>&lt;&lt;Previous</xsl:with- param> </xsl:call-template> </xsl:otherwise> </xsl:choose> </td> <td> <xsl:choose> <xsl:when test=”number($skip-rows)+number($max-rows)&lt;number($row- count)”> <xsl:call-template name=”paging-form”> <xsl:with-param name=”new-skip-rows”><xsl:value-of select=”$skip- rows+$max-rows”/></xsl:with-param> <xsl:with-param name=”button-text”> Next&gt;&gt; </xsl:with- param> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:call-template name=”paging-form”> <xsl:with-param name=”new-skip-rows”>-1</xsl:with-param> <xsl:with-param name=”button-text”> Next&gt;&gt; </xsl:with- param> </xsl:call-template> </xsl:otherwise> </xsl:choose> </td></tr></table> </xsl:template> As you can see, you basically repeat the same code for the Previous and the Next buttons. The only differences are the test conditions and the value that you pass to the paging-form template. This yields the results that are shown in Figure 14.12. Building XSQL Web Applications 425 Figure 14.12 Stateless paging with Previous and Next buttons. Now it’s time to spice up the navigation a bit. Instead of just being able to navigate to the Previous or Next page, we want to be able to navigate to any page in our results. The buttons taking us to those results will live between the Previous and Next buttons. The first step is to add the following code right in the middle of the preceding paging code. It should exist between the two cells where the Previous and Next buttons live: <td> <xsl:call-template name=”paging-list”/> </td> The final step is the creation of the paging-list template. The challenge is that you have to somehow create buttons for an indeterminate number of pages. You’ll do this using recursion—you’ll invoke the template from inside the template. Before look- ing at that, though, let’s look at the basic stuff. The start of the template does the basic mechanics of translating a page number into a skip-rows parameter and a button name. The paging-form template is used to actually create the form and the button. <xsl:template name=”paging-list”> <xsl:param name=”counter”>0</xsl:param> <td> 426 Chapter 14 <xsl:choose> <xsl:when test=”number($counter)*number($max-rows)!=number($skip- rows)”> <xsl:call-template name=”paging-form”> <xsl:with-param name=”new-skip-rows”><xsl:value-of select=”number($counter)*number($max-rows)”/></xsl:with-param> <xsl:with-param name=”button-text”><xsl:value-of select=”number($counter)+1”/></xsl:with-param> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:call-template name=”paging-form”> <xsl:with-param name=”new-skip-rows”>-1</xsl:with-param> <xsl:with-param name=”button-text”><xsl:value-of select=”number($counter)+1”/></xsl:with-param> </xsl:call-template> </xsl:otherwise> </xsl:choose> </td> This code is very similar to the code for the Previous and Next buttons. It determines what the new-skip-rows value should be and dynamically determines the name of the button based on the counter. It also disables the button when it isn’t appropriate to use it. In this case, the button for the current page is disabled. If we stopped right here, you would have a single button for the first page. To get buttons for all the pages, you need to recursively call the template. The following code takes care of this. You first have to determine when the template should be invoked. Then, you determine what the new counter value should be. The following code com- pletes the template: <xsl:if test=”number($counter+1)*number($max-rows)&lt;number($row- count)”> <xsl:call-template name=”paging-list”> <xsl:with-param name=”counter”><xsl:value-of select=”number($counter)+1”/></xsl:with-param> </xsl:call-template> </xsl:if> </xsl:template> With all recursion there is the risk of an infinite loop, so it’s important that you ensure that the recursion will stop. This is a combination of controlling when the tem- plate will be invoked and passing to it parameters so that it will violate that criteria at some point. The output of this particular template is shown in Figure 14.13. Now you’ve seen the way to handle stateless paging without the use of an action handler. You’ve also gotten to see XSLT recursion in action. You can do a lot of neat stuff with recursion, but it can lead to confusing and hard to maintain code. When you find yourself using recursion in XSLT, it’s a good time to ask yourself if what you are doing can be better accomplished with an action handler. Building XSQL Web Applications 427 Figure 14.13 Stateless paging with page list. XSQL Data Editor One of our requirements was to give the customer a way to edit prices on the Web. This moves us away from the simple publishing of data. In this section, you’ll design a sim- ple editor that uses the xsql:dml action and an update statement to change data already in the database. The first step is to look at how to design an XSQL editor. Then, you’ll develop the XSQL and the XSLT. JavaScript is heavily used in this example, so some of the editor functionality will be covered in the next section. As with everything in this chapter, we’re using a pure XSQL approach. But while a pure XSQL approach can cover a lot of applications that simply publish data, it comes up short a lot more often when inputting data. This will be covered in more detail in Chapter 18. Editor Architecture There are a lot of ways to design Web-based editors. In all cases, you have at least one component that actually processes the data into the database. The other components surround this action. There’s usually a component that locates the data to be edited, 428 Chapter 14 and then there is an HTML form that actually allows the editing of data itself. In cases in which you are inserting new data, the HTML form is blank and is linked to a com- ponent that inserts data into the database. For this example, we’re going for a simple interface. Instead of having multiple pages through which a user has to navigate, there is a single interface. Figure 14.14 shows what the finished product will look like. The same interface is reloaded any time you save a record. The editor consists of three XSQL pages and a single stylesheet. The stylesheet con- sists of one search form and a separate form for each record returned in the search. One XSQL page, price-editor-query.xsql, is used to find the records that the user wishes to edit and is included in the other two XSQL pages. The price-editor -search.xsql is invoked when the user searches for items to edit, while the price -editor-update.xsql actually updates the items in the database. After updating the items in the database, the same query is performed again and the results are reloaded. Both of the top-level stylesheets are linked to the same stylesheet, price -editor.xsl. The diagram appears in Figure 14.15. This is certainly not the only way to implement an editor in XSQL. A simpler archi- tecture is to have three pages: (1) search, (2) details, and (3) update. However, because we’re pretty familiar with XSQL and XSLT by now, this architecture presents some interesting challenges. At the end of the day, it should be more appeasing to the user, too. Figure 14.14 The price editor. Building XSQL Web Applications 429 [...]... Applications XSQL Errors When an XSQL action has a problem, it creates an xsql-error element Inside the xsql-error element is data about the error that was generated The format is as follows: SQL statement that caused the error error message You can suppress the inclusion of the SQL statement by setting... get the other items regardless The code is the Oracle code for the error, while the action is the type of action that caused the message The error message is the standard message returned by Oracle that you would also get from executing the SQL from SQL*Plus An error is easy to generate in our application Just do a search on the character ‘ This throws Oracle for a loop because this character is placed... formats the status that is returned: row updated for Your editor is complete now There are some calls to JavaScript, but they aren’t strictly necessary for a working editor If someone entered an incorrect value, then Oracle would generate an error You can handle the error... Chapter 14 The first step is to handle the two different top-level elements This is accomplished by using an ‘or’ operator in the select XPath expression at the top level of the stylesheet It will match on documents with either prod-search or price-update as their root element It would be possible to give both XSQL pages the same root element, but it isn’t a good practice Though very similar, the two different... now The next section of the chapter looks in depth at the JavaScript from this example The rest of the root template follows: Price Editor Search Search: ... create a dummy XSQL page like the one that follows, which is saved as price-editor.xsql When called, it doesn’t even make a connection to the database You need to make a change in the stylesheet so that the top template will match against price-editor -dummy:... to start with price -editor-query.xsql because it is included by the other two It does a wildcard search against the product name: SELECT p.id AS product_id, p.name AS product_name, pc.name AS product_cat_name, p.price AS product_price FROM product... price-editor-query.xsql page and also passes the term parameter to the stylesheet: The last XSQL page is responsible for updating the database After... repeats the earlier query so that you can edit more prices that are part of the same query UPDATE product SET price=? WHERE id=? COMMIT . action has a problem, it creates an xsql-error element. Inside the xsql-error element is data about the error that was generated. The format is as follows: <xsql-error code=”code” action=”action”> <statement>SQL. product_name, p .doc. extract(‘/product/summary/text()’).getStringVal() AS summary, pc.id AS category_id, pc.name AS category FROM product p, product_category pc WHERE contains (doc, ’{@search_terms}’)>0 AND. message</message> </xsql-error> You can suppress the inclusion of the SQL statement by setting error-statement to false in the action. You should get the other items regardless. The code is the Oracle code

Ngày đăng: 03/07/2014, 08:20