XQuery nâng cao: Tự xây dựng các hàm chức năng

Thực hành phát triển ứng dụng với XQuery

Các hàm XQuery cho phép bạn xác lập các biểu thức hay dùng một lần và sử dụng chúng sau này. Kết quả cuối cùng sẽ gọn hơn, ổn định và dễ bảo trì hơn. Bài viết này Sử dụng giao diện lập trình API của XQuery cho Java (XQJ) để giới thiệu việc cài đặt các hàm XQuery trong môi trường Java™.

Brian M. Carey, Tư vấn hệ thống TT, Triangle Information Solutions

Brian Carey là một chuyên viên về các hệ thống thông tin, đặc biệt là xây dựng kiến trúc, thiết kế và triển khai các ứng dụng Java doanh nghiệp



21 05 2009

Trước khi bắt đầu

Tìm hiểu về nội dung của bài viết này.

Về bài viết này

XQuery đã nhanh chóng trở thành tiêu chuẩn công nghiệp để truy xuất các tài liệu XML. Tuy nhiên, bất cứ ai làm việc với các tài liệu XML phức tạp sẽ thấy rằng các biểu thức chi tiết XQuery rất khó hiểu. Hơn nữa, một số biểu thức XQuery được lặp đi lặp lại trong rất nhiều hàm xử lý. Các hàm lặp lại là lãng phí và khó để bảo trì.

Các hàm XQuery cung cấp khả năng xử lý XML với ba thuận lợi: tính tái sử dụng, tính dễ đọc hiểu, và tách biệt khỏi các mối liên quan.

Bài viết này sẽ hướng dẫn bạn qua các bước để tạo các hàm XQuery và sử dụng chúng trong môi trường mô phỏng thương mại điện tử (eCommerce).

Mục đích

Bài này sẽ giới thiệu chính xác hàm XQuery là gì, lợi ích của chúng và cách cài đặt chúng. Bài này sẽ hướng dẫn bạn cách cài đặt một hàm XQuery trong môi trường mô phỏng eCommerce bằng cách sử dụng Java (JRE) và gói XQJ của DataDirect. Ở cuối bài viết, bạn sẽ có một ví dụ hoạt động được của một hàm XQuery và tính khả dụng của nó trong môi trường doanh nghiệp.

Yêu cầu đối với người đọc

Bài này dành cho độc giả đã quen với XML, ngôn ngữ Java và XQuery. Để biết thêm các thông tin về XML, lập trình Java, và XQuery, xem Tài nguyên.

Yêu cầu hệ thống

Để thực thi được các ví dụ trong bài viết này, bạn cần cài đặt:

  • Nền tảng Java Java phiên bản tiêu chuẩn (Java Standard Edition platform) (bài này sử dụng phiên bản 1.6)
  • DataDirect XQuery (thư viện của XQJ phải được đặt trong đường dẫn lớp (classpath) của môi trường Java của bạn. Chúng sẽ được tham chiếu bởi các lớp Java mà bạn phát triển trong các ví dụ sau này)

Để biết thêm các thông tin về các sản phẩm trên, xem Tài nguyên. Nếu bạn muốn tải mã ví dụ của bài viết này về, xem Tải về.


XQuery và hàm XQuery

Mục này giới thiệu sơ lược về XQuery và công dụng của các hàm XQuery.

XQuery

XQuery là ngôn ngữ truy vấn tài liệu XML, nó giống như ngôn ngữ truy vấn cơ sở dữ liệu quan hệ SQL. XQuery cho phép các nhà phát triển ứng dụng sử dụng các biểu thức để lấy dữ liệu ra từ các tài liệu XML. Dữ liệu đó có thể là một giá trị hoặc là cả một cấu trúc cây con của tài liệu, chẳng hạn là một thành phần với tất cả các thành phần con của nó.

XQuery sử dụng các biểu thức XPath, liên quan đến các từ khóa FLWOR nổi tiếng: for, let, where, order by, và return. Những từ khóa trên cung cấp phương thức mạnh mẽ để tách và lấy dữ liệu từ tài liệu XML trong hầu hết các trường hợp.

Cấu trúc ngữ pháp của ngôn ngữ XQuery được dựa trên cấu trúc cây của chính tài liệu XML. XQuery xử lý tốt cả các chỉ lệnh (instructions), thuộc tính (attribute) và các thành phần (element).

Xin lưu ý rằng tài liệu XML của bạn phải được tổ chức tốt nhưng không nhất thiết phải hợp lệ để XQuery hoạt động tốt. Một tài liệu XML được tổ chức tốt (well-formed) nghĩa là nó tuân theo chuẩn XML của tổ chức W3C (xem Tài nguyên). Một tài liệu XML là hợp lệ khi nó tuân theo các định kiểu (Type definition DTD) hoặc lược đồ của chính nó.

Để biết toàn bộ các phân tích của XQuery, xem Tài nguyên.

Hàm XQuery là gì

Hàm XQuery giống như các thủ tục trong cơ sở dữ liệu quan hệ, chúng là các thủ tục do người dùng định nghĩa và sử dụng các biểu thức. Với những người theo trường phái Java thì hàm XQuery tương đương với phương thức tĩnh và công cộng trong các lớp tiện ích.

Xem ví dụ về một hàm XQuery trong ví dụ 1 và tôi sẽ giải thích từng phần của nó. Để cho dễ hiểu, tôi đã sử dụng hàm mà bạn sẽ sử dụng nó trong môi trường mô phỏng thương mại điện tử eCommerce sau này.

Ví dụ 1. Một hàm XQuery
declare function local:calculateReceivedIn($delay as xs:integer) 
{
	let $receivedIn := ($delay + $shippingDelay)
	return ($receivedIn)
};

Đây là một hàm tương đối thô sơ, nhung nó sẽ hữu dụng trong ứng dụng thương mại điện tử. Nó thực hiện phép cộng hai giá trị, một là tham số của hàm và một là biến của chương trình (biến toàn cục). Tôi sẽ giải thích cụ thể sau.

Việc khai báo hàm tương đối dễ hiểu với 2 từ declare function. Nó thông báo cho bộ xử lý của XQuery rằng, những dòng mã theo sau là một hàm thay vì là một biểu thức.

Tiếp theo, tên của hàm có hai phần: local:calculateReceivedIn. Phần đứng trước dấu (:) local là không gian tên và phải tuân theo chuẩn XML. Ví dụ này chỉ sử dụng local. Một ứng dụng có thể có vài mô-đun, và hàm XQuery sẽ dùng các không gian tên cụ thể cho từng mô-đun. Phần tiếp theo của tên hàm đơn giản chỉ là tên hàm. Trong trường hợp này, nó là calculateReceivedIn bởi vì nó tính một số để hiện thị cho một thông báo "Received in X days" của kết quả xuất ra.

Tiếp theo là mô tả tham số hay biến. Như thông lệ, các tham số nằm trong ngoặc đơn ngay sau tên hàm. Tên của tham số được bắt đầu bằng dấu $. Đây là quy định của XQuery. Lưu ý rằng bạn cũng phải định nghĩa kiểu dữ liệu. Trong trường hợp này, kiểu dữ liệu là số nguyên integer, nên nó viết như sau as xs:integer. Những ai đã quen với lược đồ XML sẽ nhận ra cú pháp của kiểu dữ liệu. Truy cập Tài nguyên để xem danh sách đầy đủ các kiểu dữ liệu.

Tiếp theo, là phân thân hàm. Nó bắt đầu và kết thúc với dấu ngoặc nhọn { và }. Nó sử dụng các biểu thức chuẩn của XQuery và thực hiện tính receivedIn. Điều này được thực hiện bởi biểu thức let, đây là biểu thức L trong năm biểu thức FLWOR nổi tiếng đã nói ở trên. Tiếp theo là biểu thức cần tính receivedIn bằng tổng của delayshippingDelay. Nhớ rằng biến shippingDelay là biến toàn cục. Nó đã được khai báo sẵn trong mô-đun XQuery nên không cần phải khai báo nó như là một tham số của hàm.

Vậy làm thế nào để gọi một hàm XQuery? Hãy xem một phần của biểu thức XQuery trong ví dụ 2.

Ví dụ 2. Gọi một hàm XQuery
{local:calculateReceivedIn($minnow/availability/shipping/delay)}

Đây là một phần của tài liệu XML trong ứng dụng eCommerce sẽ được trả lại ứng dụng và sử dụng để hiển thị thông tin cho người dùng. Để tính thời gian giao hàng (shipping delay) cho một sản phẩm, bạn gọi hàm ở phía trên bên trong ngoặc { và }. Sử dụng tên đầy đủ (bao gồm không gian tên và tên hàm), truyền số liệu nhận được từ tài liệu XML vào tham biến. Số liệu là giá trị của thành phần <delay> trong tài liệu XML và phải là số nguyên nếu không bộ xử lý XQuery sẽ cảnh báo. Lưu ý rằng bạn nhận giá trị tham biến bằng cách sử dụng một biểu thức XQuery chuẩn. Sau đó, giá trị đó sẽ được lấy từ thành phần <delay> trong tài liệu XML và cộng với giá trị của $shippingDelay. Nó trả lại tổng của hai giá trị trên, và đó là kết quả của đoạn mã trong ví dụ 2.

Các lợi ích khi dùng hàm XQuery

Dùng hàm XQuery có rất nhiều lợi ích. Đầu tiên, bản thân các biểu thức XQuery rất dễ đọc. Hãy xem ví dụ 3.

Ví dụ 3. Biểu thức XQuery đơn thuần
<shipping-info>
   <received-in>
      {$minnow/availability/shipping/delay/@unit} 
      {
         let $delay := $minnow/availability/shipping/delay
         let $receivedIn := ($delay + $shippingDelay)
         return ($receivedIn)
      }
   </received-in>
</shipping-info>

Ví dụ 3 đưa ra toàn bộ cây con của thông tin chuyển hàng. Biểu thức XQuery ở giữa tính $receivedIn, được sử dụng để thay thế hàm XQuery mô tả trong ví dụ 1. Như bạn có thể thấy, nếu không có hàm, đoạn mã trở lên phức tạp hơn một chút và khó đọc hơn cho người không quen. Nếu hàm XQuery thực hiện các phép tính phức tạp hơn thì đoạn mã này có lẽ là không thể đọc nổi khi mới nhìn vào.

Một thuận lợi khác của hàm XQuery là sự độc lập. Hàm cho phép các nhà phát triển độc lập xác định cách mà dữ liệu sẽ được xử lý trước khi trả về trong khi biểu thức XQuery chính có thể được sử dụng để tìm dữ liệu đầu vào cho hàm.

Và cuối cùng, một thuận lợi khác là tính tái sử dụng. Trong ví dụ này, bạn định nghĩa một hàm tính khoảng thời gian dùng để chuyển một món hàng đến tay người tiêu dùng dựa trên thời gian chuyển hàng chuẩn cộng với độ trễ liên quan đến địa chỉ của người dùng. Hãy xem ví dụ 3. Nếu với mỗi loại sản phẩm, bạn thực hiện phép tính thời gian giao hàng như trong ví dụ 3, thì bạn sẽ phải viết lại đoạn mã đó cho tất cả các sản phẩm khác trong biểu thức XQuery. Tuy nhiên, bạn có thể tái sử dụng hàm ở bất cứ đâu trong biểu thức XQuery nếu nó đã được định nghĩa. Nếu biểu thức cần thay đổi, bạn chỉ cần đổi hàm đó một lần.


Xây dụng ứng dụng đầu tiên của bạn

Bây giờ là lúc bắt đầu học với ứng dụng mẫu. Trong phần này, bạn sẽ cài đặt một hàm XQuery trong môi trường thương mại điện tử mô phỏng.

Tìm hiểu vấn đề

Như đã đề cập ở trên, bạn sẽ cài đặt hàm XQuery trong môi trường thương mại điện tử mô phỏng. Trong ví dụ này, bạn có một trang web kinh doanh trực tuyến tên là fishinhole.com. Đây là website mua bán các sản phẩm để đánh bắt cá. Người dùng sẽ duyệt các catalog, đặt mua sản phẩm đến một địa chỉ cụ thể.

Các từ viết tắt hay dùng

  • API: giao diện lập trình ứng dụng (Application programming interface)
  • HTML: Ngôn ngữ đánh dấu siêu văn bản (Hypertext Markup Language)
  • IDE: Môi trường phát triển ứng dụng tích hợp (Integrated development environment)
  • W3C: World Wide Web Consortium
  • XML: Ngôn ngữ đánh dấu mở rộng (Extensible Markup Language)

Thông tin của mỗi sản phẩm trong danh mục (catalog) được lưu trữ một tài liệu XML trong cấu trúc cây phân cấp. Tài liệu XML lưu giữ thông tin về sản phẩm như tên, giá, màu sắc, mô tả và thời gian giao hàng. Ứng dụng Web nhận tài liệu XML và chuyển đổi nó sang dạng HTML phù hợp đối với trình duyệt của người dùng.

Bạn sẽ cài đặt một hàm XQuery để hỗ trợ các trường hợp cụ thể. Trong ví dụ này, một người dùng đã được xác định muốn thực hiện một lệnh tìm kiếm trên trang Web cho sản phẩm cá mồi với hình dạng cá chép và có thể giao hàng trong một số ngày nhất định (ví dụ như 2 ngày hoặc ít hơn). Bạn sẽ sử dụng XQuery để truy xuất tài liệu XML và trả lại những sản phẩm theo yêu cầu đó.

Hàm XQuery ở trong ví dụ 1 được sử dụng để tính thời gian giao hàng dựa trên địa chỉ thực tế của người dùng cộng thời gian giao hàng chuẩn. Điều này là cần thiết vì fishinhole.com giao hàng trên toàn thế giới, và thời gian giao hàng phụ thuộc rất nhiều vào địa chỉ người nhận.

Tệp XML: fishinhole.xml

Hãy xem tệp tài liệu XML mà bạn sẽ truy xuất trong trường hợp này. Ví dụ 4 là một phần nhỏ của tài liệu XML đi kèm với bài viết này. Mặc dù tài liệu XML đã được viết gọn lại để tiết kiệm không gian, ví dụ 4 hiển thị đầy đủ toàn bộ cấu trúc dữ liệu.

Ví dụ 4. Cấu trúc tài liệu XML
 <casting>
  <minnows brand="yohuri" style="deep" size="3">
   <minnow color="midnight blue">
    <international-prices>
     <price denomination="dollar">3.25</price>
     <price denomination="euro">4.25</price>
     <price denomination="canadian-dollar">4</price>
     <price denomination="peso">100</price>
    </international-prices>
    <availability>
     <shipping>
      <delay>0</delay>
     </shipping>
     <regions>
      <shipping-region>United States</shipping-region>
      <shipping-region>European Union</shipping-region>
      <shipping-region>Canada</shipping-region>
      <shipping-region>Mexico</shipping-region>
     </regions>
    </availability>
   </minnow>
  </minnows>
 </casting>
</lures>

Ví dụ 4 chứa các thông tin về một cá mồi. Các thông tin này bao gồm, giá cả <price>, tính sẵn có <availability>, nhãn hiệu <brand>, kích thước <size>, kiểu dáng (style) và màu sắc (color). Trong đó, một số thông tin được mô tả trong các thuộc tính như brand (nhãn hiệu)style (kiểu dáng). Các thông tin khác được mô tả trong các thành phần, như là <price> (giá). Lưu ý rằng, một số thành phần cũng phân loại sản phẩm, như là <casting><minnows>. Đây là cấu trúc dữ liệu được mô tả trong ứng dụng thương mại.

Một thành phần bạn cần quan tâm là <shipping>. Thành phần đó là thành phần cha của thành phần <delay>, để mô tả thời gian giao hàng. Đó là con số được sử dụng trong hàm tính tổng phía trên. Trong ví dụ 4, delay bằng 0, nghĩa là hàng sẽ được giao trong cùng ngày (thời gian giao hàng được tính theo đơn vị ngày).

Bắt đầu với một lớp Java

Bây giờ bạn đã biết bạn cần làm gì, bạn sẽ sử dụng mã Java với XQJ để thực hiện. Bạn sẽ viết một lớp Java đơn giản như là chương trình thử nghiệm hàm XQuery.

Lớp này có tên là XQueryFunctionTester. Nó sẽ có một hàm khởi tạo (constructor), tên của tài liệu XML. Nó cũng sẽ có một phương thức chính main() để bạn thực thi nó. Xem ví dụ 5.

Ví dụ 5. Lớp Java ví dụ
package com.triangleinformationsolutions.article.xquery;

import java.io.FileReader;
import java.util.Properties;

import javax.xml.namespace.QName;

import com.ddtek.xquery3.XQConnection;
import com.ddtek.xquery3.XQException;
import com.ddtek.xquery3.XQExpression;
import com.ddtek.xquery3.XQItemType;
import com.ddtek.xquery3.XQSequence;
import com.ddtek.xquery3.xqj.DDXQDataSource;

public class XQueryFunctionTester {

	private static final String XML_DOCUMENT  = "./fishinhole.xml";
	
	private String filename;

	
	public XQueryFunctionTester(String filename) {
		this.filename = filename;
	}

	
	private void go() {
	}
	
	
	public static void main(String[] args) {
		XQueryFunctionTester tester = new XQueryFunctionTester(XML_DOCUMENT);
		tester.go();
	}
}

Để ý phương thức main(). Nó là điểm bắt đầu của một ứng dụng Java chuẩn. Bạn khai báo XQueryFunctionTester với tên tệp của tài liệu XML mà bạn sẽ xử lý. Lưu ý rằng, tôi đã viết tên tệp XML ở phần gốc của cấu trúc gói lớp Java. Để thực thi lớp này thành công, đó là nơi bạn nên viết tên tệp XML. Tôi cũng chọn việc viết luôn tên tệp tài liệu XML vào mã thay vì sử dụng tham số dòng lệnh cho nó đơn giản. Nếu muốn thay đổi, tôi chỉ việc sửa lại nó và thực hiện lại.

Phương thức go() được sử dụng để thực hiện lệnh truy vấn. Sau đó, XQueryFunctionTester được khởi tạo, điều này giúp bạn tránh được ngữ cảnh tĩnh tạo bởi main(). Bây giờ, tôi để phương thức go() rỗng. Bạn sẽ xem xét nó sau này.

Đồng thời, bạn cũng để ý rằng tôi đã gộp các import mà sẽ cần tới. Đa số chúng là thư viện của XQJ và được yêu cầu cho việc tìm kiếm mà bạn sẽ dùng. Lớp QName chỉ ra một cái tên đạt chuẩn kỹ thuật của XML. Lớp FileReader đọc nội dung tệp XML. Lớp Properties được yêu cầu bởi XQJ. Bạn không sử dụng nó ở đây ngoài việc khởi tạo nó với một hàm khởi tạo rỗng và chuyển nó vào như là một tham số của phương thức.

Trong ví dụ này, một người dùng sẽ tìm tất cả các cá mồi có hình cá chép với thời gian giao hàng nhỏ hơn hoặc bằng một số ngày cụ thể nào đó. Do đó, đoạn mã thực hiện việc tìm kiếm, như trong ví dụ 6.

Ví dụ 6. Các phương thức thực hiện việc tìm kiếm XQuery
private static final int SHIPPING_DELAY_IN_DAYS = 3;
private static final String DOCUMENT_NAME_FIELD = "docName";
private static final String SHIPPING_DELAY_FIELD = "shippingDelay";
private static final String MAXIMUM_DELAY_FIELD = "maximumDelay";


private XQExpression getGenericExpression() throws XQException {
	XQExpression expression = conn.createExpression();
	
	expression.bindString(new QName(DOCUMENT_NAME_FIELD), filename,
		conn.createAtomicType(XQItemType.XQBASETYPE_STRING));

	return expression;
}


private String getAllMinnowsWithMaximumDelay(int maximumDelay) throws Exception {
	XQExpression expression = getGenericExpression(); 

	expression.bindInt(new QName(SHIPPING_DELAY_FIELD), SHIPPING_DELAY_IN_DAYS,
		conn.createAtomicType(XQItemType.XQBASETYPE_INTEGER));	
	
	expression.bindInt(new QName(MAXIMUM_DELAY_FIELD), maximumDelay,
		conn.createAtomicType(XQItemType.XQBASETYPE_INTEGER));
	
	FileReader fileReader = new FileReader(GET_ALL_MINNOWS_WITH_MAX_DELAY_FILE);
	XQSequence results = expression.executeQuery(fileReader);
	
	return results.getSequenceAsString(new Properties());
}

Nói ngắn gọn, hai phương thức trong ví dụ 6 tạo ra một đối tượng XQExpression, thực thi nó, và trả lại kết quả trong chuỗi String đã được định dạng.

Phương thức getGenericExpression() được dùng với mục đích mở rộng sau này. Nếu bạn muốn tạo một phương thức khác với mục đích khác, bạn dùng getGenericExpression() thay vì viết lại mã cho phương thức đó với mỗi mục đích. Phương thức này tạo ra một đối tượng XQExpression và gán giá trị cho trường docName với giá trị là tên của tệp XML.

Phương thức getAllMinnowsWithMaximumDelay() được sử dụng để kiểm tra trong ví dụ này. Đầu tiên, bạn nhận được biểu thức chung định nghĩa ở trên sau đó gắn một biến cụ thể vào giá trị được định nghĩa trong lớp Java. Tham biến gắn vào đầu tiên là SHIPPING_DELAY_IN_DAYS, nó biểu thị thời gian giao hàng đến người dùng dựa trên địa chỉ của họ, phương thức này gán giá trị biến là 3. Biến tiếp theo, MAXIMUM_DELAY_FIELD, là thời gian lớn nhất cần để giao hàng mà người dùng có thể chấp nhận. Giá trị này được truyền vào từ tham số maximumDelay. Cuối cùng, FileReader đọc tệp XQ (bạn sẽ xây dựng tệp này sau). Sau đó bạn sẽ thực hiện lệnh quy vấn với tệp đó và trả lại kết quả như là một đối tượng String.

Xin hãy lưu ý rằng, tệp XQ cũng nằm trong phần gốc của gói lớp. Đó là, nó nằm cùng chỗ với tệp XML.

Để biết chi tiết về XQJ, xem Tài nguyên.

Trong ví dụ 7, bạn quay lại phương thức go() thực hiện lệnh tìm kiếm.

Ví dụ 7. Phương thức go()init()
private DDXQDataSource dataSource;
private XQConnection conn;

public void init() throws XQException {
	dataSource = new DDXQDataSource();
	conn = dataSource.getConnection();
}


private void go() {
	try {
		init();
		
		int delayToUse = 2;
		
		String returnedHtml = getAllMinnowsWithMaximumDelay(delayToUse);
		
		System.out.println(returnedHtml);
	} catch (Exception e) {
		e.printStackTrace();
	}
}

Đầu tiên, phương thức go() gọi phương thức init(), tạo ra nguồn dữ liệu XQJ và một đối tượng kết nối XQConnection. Sau đó, bạn tính toán thời gian giao hàng tối đa mà người dùng chấp nhận. Ở đây là hai ngày. Sau đó bạn gọi phương thức được mô tả phía trên, getAllMinnowsWithMaximumDelay(), và truyền delayToUse như là tham số cho nó. Cuối cùng, bạn in kết quả ra.

Tệp XQuery: getAllMinnowsWithMaxDelay.xq

Bây giờ là lúc xem nội dung tệp XQ, trong ví dụ 8. Tệp này chứa cả biểu thức tìm-và-nhận (search-and-retrieve) XQuery và hàm XQuery. Đây là tệp được tham chiếu bởi lớp Java do bạn tạo ra.

Ví dụ 8. Tệp XQ
declare variable $docName as xs:string external;
declare variable $maximumDelay as xs:integer external;
declare variable $shippingDelay as xs:integer external;

declare function local:calculateReceivedIn($delay as xs:integer) 
{
	let $receivedIn := ($delay + $shippingDelay)
	return ($receivedIn)
}; 

for $minnows in doc($docName)//minnows
return
<div class="filterResults">
 <div class="brand">
	Brand: {data($minnows/@brand)}	
 </div>
 <div class="size">
	Size: {data($minnows/@size)} inches
 </div>
 {
 for $minnow in $minnows/minnow
 where $minnow/availability/shipping/delay<=$maximumDelay
 return
  <div class="singleFilterResult">
   <div class="color">
	Color: {data($minnow/@color)}
   </div>
   <div class="shippingInfo">
    <div class="receivedIn">
    Received in {local:calculateReceivedIn($minnow/availability/shipping/delay)} day(s)
    </div>
   </div>
  </div>
 }
</div>

Mục đích chính của tệp XQuery là để tách các thành phần phù hợp với lệnh truy vấn từ tệp XML và hiển thị nội dung thông tin cho người dùng. Ở đây, bạn sử dụng một loạt thẻ div với các lớp định kiểu được định nghĩa trong tệp bảng định kiểu khác nhau. Bằng cách đó, thông tin lấy được có thể được hiển thị một cách rõ ràng và rành mạch với người dùng.

Để ý ba dòng đầu tiên trong tệp XQ, các tên biến giống như các tên biến mà bạn dùng trong phương thức getAllMinnowsWithMaximumDelay()getGenericExpression(). Trong các phương thức đó, các giá trị được gán cho các biến được khai báo ở đây.

Mấy dòng tiếp theo là hàm mà đã được mô tả và giải thích chi tiết ở trên. Hàm này trả về tổng của thời gian giao hàng (tham số truyền vào) và thời gian giao hàng phụ thuộc vào địa chỉ của người dùng (một trong các biến được khai báo ở phần đầu của tệp).

Tiếp theo là một biểu thức XQuery chuẩn. Biểu thức này trả lại tất cả các cá chép mồi với một thời gian giao hàng nhỏ hơn hoặc bằng số ngày được chỉ ra bởi $maximumDelay.

Cho đến cuối đoạn mã, lưu ý việc thực hiện của hàm XQuery calculateReceivedIn. Hàm này sử dụng một biểu thức chuẩn XQuery để tách giá trị của thành phần <delay> sau đó truyền giá trị đó như là một tham số cho hàm calculateReceivedIn.

Thử nghiệm

Bạn có thể tải các tệp đi kèm bài này (Tải về).

Bạn có thể giải nén tệp này vào một thư mục nào nó. Nếu bạn không thay đổi cấu trúc thư mục, các tệp mã nguồn Java, tài liệu XML và tệp XQ sẽ nằm ở các thư mục tương ứng.

Tiếp theo, bạn biên dịch mã Java. Bạn phải đảm bảo rằng tệp ddxq.jar nằm trong đường dẫn classpath. Thư viện này là một phần của XQJ. Nó nằm ở thư mục /lib của thư mục cài đặt XQJ.

Sau khi biên dịch thành công, bạn di chuyển đến thư mục mà bạn đã giải nén các tệp trong của sổ dòng lệnh và gõ java com.triangleinformationsolutions.article.xquery.XQueryFunctionTester với tham số -cp chỉ rõ đường dẫn classpath của ddxp.jar. Nó sẽ hoạt động tốt nếu Java.exe được đặt trong đường dẫn của hệ thống.

Nếu bạn muốn thực hiện nó trong IDE, bạn hãy tham khảo chỉ dẫn của IDE mà bạn dùng.

Khi thực hiện thành công, kết quả trả về như trong ví dụ 9. Nó đã được viết gọn lại cho bài viết này.

Ví dụ 9. Kết quả (được viết gọn)
<div class="filterResults">
 <div class="brand">
  Brand: Yohuri
 </div>
 <div class="size">
  Size: 3 inches
 </div>
 <div class="singleFilterResult">
  <div class="color">
   Color: midnight blue
  </div>
  <div class="shippingInfo">
   <div class="receivedIn">
    Received in 3 day(s)
   </div>
  </div>
 </div>
 <div class="singleFilterResult">
  <div class="color">
   Color: clown
  </div>
  <div class="shippingInfo">
   <div class="receivedIn">
    Received in 4 day(s)
   </div>
  </div>
 </div>
</div>

Nếu bạn so sánh tài liệu XML với kết quả trong ví dụ 9, bạn sẽ thấy nó là chính xác. Sản phẩm midnight blue fishing lure có thời gian giao hàng là 0 ngày. Nó được cộng với giá trị của biến $shippingDelay (có giá trị là 3 nếu bạn nhìn lại mã Java ở trên) và bằng 3. Sản phẩm clown colored có thời gian giao hàng là 1 ngày. Nên kết quả là Received in 4 day(s) (nhận được trong 4 ngày) cho thời gian giao hàng. Nói cách khác, lệnh tìm kiếm đã thành công!


Còn giá cả của sản phẩm thì sao?

Bạn có thể sử dụng các hàm XQuery ở đâu nữa không? Hãy xem kết quả trong ví dụ 9. Lưu ý rằng, các sản phẩm chưa có giá. Giả sử rằng người dùng đã hài lòng với giá của sản phẩm ở trang eCommerce, nên bạn tạo một hàm để hiển thị giá.

Bạn có thể thắc mắc, "Tại sao tôi lại phải tạo một hàm cho giá? Tại sao không dùng luôn giá có trong tệp XML?". Nhớ rằng fishinhole.com bán hàng trên toàn thế giới. Giá cả sẽ được hiển thị theo nước mà người dùng đang sống. Nếu bạn xem lại tệp XML, bạn sẽ thấy điều này đã được giải thích với thành phần <international-prices>.

Do đó, bước tiếp theo là, xác định giá của sản phẩm dựa trên thông tin về nước mà người dùng sống. Lớp Java và tệp XQ phải được thay đổi, nhưng tệp XML thì không, như trong ví dụ 10.

Ví dụ 10. Viết lại lớp Java
private static final String COUNTRY	= "Mexico";
private static final String COUNTRY_FIELD	= "country";
...
private String getAllMinnowsWithMaximumDelay(int maximumDelay) throws Exception {
	XQExpression expression = getGenericExpression(); 
	
	expression.bindInt(new QName(SHIPPING_DELAY_FIELD), SHIPPING_DELAY_IN_DAYS,
		conn.createAtomicType(XQItemType.XQBASETYPE_INTEGER));
	
	expression.bindInt(new QName(MAXIMUM_DELAY_FIELD), maximumDelay,
		conn.createAtomicType(XQItemType.XQBASETYPE_INTEGER));
	
	expression.bindString(new QName(COUNTRY_FIELD), COUNTRY,
		conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
	
	FileReader fileReader = new FileReader(GET_ALL_MINNOWS_WITH_MAX_DELAY_FILE);
	XQSequence results = expression.executeQuery(fileReader);
	
	return results.getSequenceAsString(new Properties());
}

Ví dụ 10 khác với lớp Java bạn tạo ở trên bởi vì nó chứa hai trường tĩnh (static fields) mới và một biểu thức thêm vào. Hai trường mới là trường tên quốc gia và kiểu quốc gia. Trong ví dụ này, người sử dụng cư ngụ ở Mexico, do đó giá cả được hiển thị bằng đơn vị tiền pesos. Biểu thức mới gắn tên kiểu quốc gia là country với tên quốc gia của người sử dụng (Mexico).

Tệp XQ mới trong ví dụ 11 không khác mấy so với tệp XQ trong ví dụ 8, nhưng những thay đổi rất đáng lưu ý.

Ví dụ 11. Tệp XQ mới
declare variable $docName as xs:string external;
declare variable $maximumDelay as xs:integer external;
declare variable $shippingDelay as xs:integer external;
declare variable $country as xs:string external;

declare function local:calculateReceivedIn($delay as xs:integer) 
{
	let $receivedIn := ($delay + $shippingDelay)
	return ($receivedIn)
}; 

declare function local:getPrice($minnowElement as element(minnow)) as xs:string
{
if ($country = 'Mexico') then
(concat((data($minnowElement/international-prices/price[@denomination="peso"]))
  ," Pesos"))

else if ($country = 'Canada') then 
(concat(
 (data($minnowElement/international-prices/price[@denomination="canadian-dollar"]))
  ," Canadian Dollars"))

else if ($country = 'United States') then
(concat((data($minnowElement/international-prices/price[@denomination="dollar"]))
 ," Dollars"))

else 
(concat((data($minnowElement/international-prices/price[@denomination="euro"]))
 ," Euros"))
};

for $minnows in doc($docName)//minnows
return
<div class="filterResults">
 <div class="brand">
	Brand: {data($minnows/@brand)}	
 </div>
 <div class="size">
	Size: {data($minnows/@size)} inches
 </div>
 {
 for $minnow in $minnows/minnow
 where $minnow/availability/shipping/delay<=$maximumDelay
 return
  <div class="singleFilterResult">
   <div class="color">
	Color: {data($minnow/@color)}
   </div>
   <div class="shippingInfo">
    <div class="receivedIn">
     Received in {local:calculateReceivedIn($minnow/availability/shipping/delay)} day(s)
    </div>
    <div class="price">
     {local:getPrice($minnow)}
    </div>
   </div>
  </div>
 }
</div>

Đầu tiên, bạn mô tả một biến mới, $country, sẽ được gán với giá trị Mexico trong ví dụ 10 trước.

Tiếp theo, bạn khai báo hàm mới, tên là getPrice. Ở đây, bạn cần để ý đôi điều. Đầu tiên, nó lấy kiểu dữ liệu là element, không phải là kiểu integer như trong hàm trước. Qua đây, bạn có thể thấy rằng các thành phần cũng có thể được sử dụng như là các tham số của hàm.

Điều đáng quan tâm tiếp theo trong hàm này là bạn chỉ rõ kiểu dữ liệu trả về. Trong trường hợp này, bạn trả lại một chuỗi ký tự. Do đó, bạn không cần sử dụng từ khóa return trong thân hàm. Nó ngầm hiểu rằng đó là kết quả trả về.

Thân hàm sử dụng cấu trúc điều khiển if/then và so sánh giá trị của biến $country với một loạt các nước mà ứng dụng hỗ trợ. Nếu điều kiện so sánh trả lại giá trị đúng, giá của sản phẩm được trả về với ký hiệu tiền tệ của nước đó. Mặc định là Euros.

Cuối cùng bạn có một thẻ div mới trong biểu thức XQuery chính mà nó nhận giá của sản phẩm hiện tại trong vòng lặp. Đây là nơi mà hàm getPrice được gọi. Nó truyền chính nó (toàn bộ thành phần <minnow>) như là tham số cho hàm. Đây là thành phần mà biểu thức XQuery nằm trong hàm sẽ xử lý.

Đoạn mã ví dụ của bài viết này bao gồm các tệp riêng biệt cho ví dụ này và các ví dụ đã hoàn thành ở trên. Hàm này là XQueryFunctionTesterIncludesPrice.java và tệp XQ tương ứng là getAllMinnowsWithMaxDelayIncludesPrice.xq.

Biên dịch và thực thi XQueryFunctionTesterIncludesPrice như ví dụ trước. Kết quả sẽ giống như trong ví dụ 12.

Ví dụ 12. Kết quả từ tệp XQ mới
<div class="filterResults">
 <div class="brand">
  Brand: Yohuri
 </div>
 <div class="size">
  Size: 3 inches
 </div>
 <div class="singleFilterResult">
  <div class="color">
   Color: midnight blue
  </div>
  <div class="shippingInfo">
   <div class="receivedIn">
	Received in 3 day(s)
   </div>
  </div>
  <div class="price">
    100 Pesos
  </div>
 </div>
</div>

Một lần nữa, thử nghiệm đã thành công. Kết quả xuất ra là ký hiệu tiền tệ pesos bởi vì nước mà người sử dụng đang cư ngụ là Mexico. Bạn có thể thấy rõ thẻ div với lớp price có giá là 100 Pesos. Và, nhìn vào tài liệu XML, giá trị 100 là chính xác.


Hàm trong hàm

Chúng ta luôn có cách tốt hơn để giải quyết một vấn đề. Nhớ lại rằng, một trong các thuận lợi của việc sử dụng hàm XQuery là tính tái sử dụng. Thuận lợi khác là tính độc lập. Bây giờ hãy xem hàm mới trong ví dụ 13.

Ví dụ 13. Hàm mới với sự cải tiến
declare function local:getPrice($minnowElement as element(minnow)) as xs:string
{
if ($country = 'Mexico') then
(concat((data($minnowElement/international-prices/price[@denomination="peso"]))
  ," Pesos"))

else if ($country = 'Canada') then 
(concat(
 (data($minnowElement/international-prices/price[@denomination="canadian-dollar"]))
  ," Canadian Dollars"))

else if ($country = 'United States') then
(concat((data($minnowElement/international-prices/price[@denomination="dollar"]))
 ," Dollars"))

else 
(concat((data($minnowElement/international-prices/price[@denomination="euro"]))
 ," Euros"))};

Bên trong hàm này, các biểu thức được lặp đi lặp lại. Sự thay đổi duy nhất là giá trị của thuộc tính denomination. Đó là điểm mà chúng ta có thể cải tiến. Và bạn dễ dàng nhận thấy rằng, để cải tiến ta có thể sử dụng một hàm khác bên trong hàm đó. Tại sao không nhỉ?

Với một hàm khác để xử lý việc tìm kiếm, khi ta cần thay đổi điều kiện, bạn không cần phải thực hiện bốn lần nếu bạn sử dụng hàm trong ví dụ 13. Hơn nưa, nếu bạn tăng số lượng quốc gia được hỗ trợ (như thêm Ấn độ hoặc Trung Quốc) với đơn vị tiền tệ mới, bạn làm vấn đề trở nên tồi tệ hơn rất nhiều.

Bằng cách sử dụng hàm khác, bạn nhận được hai lợi ích cùng lúc là tính tái sử dụng và sự gọn nhẹ. Hàm mới sẽ xử lý các biểu thức so sánh cần thiết để lấy được giá đúng từ tài liệu XML.

Hãy tạo hàm có tên là getCountrySpecificPrice. Hàm này chấp nhận hai tham số: thành phần <minnow> và tên của thuộc tính mà biểu thị tên của đơn vị tiền tệ. Hàm trả lại giá của sản phẩm lấy được từ thành phần như giá trị của thuộc tính denomination mà khớp với tham số của hàm.

Ví dụ 14 không chỉ đưa ra hàm mới, mà còn chỉ ra cách viết lại hàm getPrice để có thể sử dụng hàm mới.

Ví dụ 14. Hàm cải tiến mới
declare function local:getCountrySpecificPrice($minnowElement as element(minnow),
$denomination as xs:string) {
let $price := 
   (data($minnowElement/international-prices/price[@denomination=$denomination]))
 
return ($price)
};

declare function local:getPrice($minnowElement as element(minnow)) as xs:string
{
if ($country = 'Mexico') then
(concat(local:getCountrySpecificPrice($minnowElement,"peso")," Pesos"))

else if ($country = 'Canada') then 
(concat(local:getCountrySpecificPrice($minnowElement,"canadian-dollar")
 ," Canadian Dollars"))

else if ($country = 'United States') then 
(concat(local:getCountrySpecificPrice($minnowElement,"dollar")," Dollars"))

else (concat(local:getCountrySpecificPrice($minnowElement,"euro")," Euros"))};

Như bạn có thể thấy, mã chương trình bây giờ trông gọn gàng hơn. Đây là phần quan trọng của bài học này bởi vì cho bạn thấy cách sử dụng hàm trong hàm để cải tiến mã chương trình của bạn.

Đó là những gì bài viết này muốn đề cập tới. Tuy nhiên, có thể bạn đã nhận ra rằng bạn có thể làm được nhiều hơn thế với các hàm XQuery đối với tài liệu XML và tệp XQ đi kèm tài liệu này. Hãy thử tự thử nghiệm và khám phá bằng cách viết các hàm của chính bạn.


Tổng kết

Mục đích của bài viết này là thảo luận chi tiết về các hàm XQuery. Tôi đã giải thích các khái niệm và các lợi ích khi sử dụng, khai báo và cài đặt chúng.

Tôi đã trình diễn một số cài đặt của một số hàm XQuery với các trường hợp trong môi trường mô phỏng thương mại điện tử. Trong trường hợp này, môi trường mô phỏng thương mại điện tử là một trang web bán các sản phẩm cá mồi quốc tế. Các hàm được thiết kế để tách lấy thông tin cụ thể đối với từng sản phẩm mà công ty bán.

Bạn đã thử nghiệm các hàm XQuery với chương trình viết bằng ngôn ngữ Java. Các chương trình này là các lớp Java chuẩn và thực thi được trên JRE và sử dụng thư viện XQJ của DataDirect để xử lý các truy vấn XQuery. Để kiểm tra kết quả chương trình, bạn xem xét kết quả ở của sổ lệnh.

Bạn cũng đã thực hiện một số thủ thuật cải tiến bên trong hàm XQuery. Tôi đã trình diễn cách mà bạn có thể ứng dụng khái niệm hướng đối tượng của sự kiện, như là sự đóng gói, với các hàm XQuery.

Tóm lược

Hàm XQuery cung cấp cho các nhà phát triển khả năng viết mã với sự độc lập, tính tái sử dụng, và tính dễ đọc và duy trì. Mã XQuery thường làm cho các chương trình trở lên phức tạp và khó duy trì. Tuy nhiên, các hàm XQuery, khi đã được cài đặt đúng đắn, có thể làm cho sự gian khổ của nhiệm vụ bắt lỗi và duy trì mã XQuery trở thành dễ thở hơn.


Tải về

Mô tảTênKích thước
Mã nguồn dùng trong bài nàyxqueryfunctions.zip9KB

Tài nguyên

Học tập

Lấy sản phẩm và công nghệ

Thảo luận

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Nguồn mở
ArticleID=391020
ArticleTitle=XQuery nâng cao: Tự xây dựng các hàm chức năng
publish-date=05212009