阅读文章

2006/02 归档

[ 来源:| 作者: | 时间:2010-10-12 | 浏览: 次 ]

关键字:java、PHP技术研究者

The main theme for the Java Platform, Enterprise Edition (Java EE) 5 is ease of development. The platform's web tier contributes significantly to ease of development in two ways. First, the platform now includes the Java Standard Tag Library (JSTL) and JavaServer Faces technology. Second, all the web-tier technologies offer a set of features that make development of web applications on Java EE much easier.  Some of these features are the following:

  • A new expression language (EL) syntax that allows deferred evaluation of expressions, enables using expressions to both get and set data and to invoke methods, and facilitates customizing the resolution of a variable or property referenced by an expression
  • Support for resource injection through annotations to simplify configuring access to resources and environment data
  • Complete alignment of JavaServer Faces technology tags and JavaServer Pages (JSP) software code
  • In this series of articles, web-tier proponents at Sun introduce the new concepts that every web-application developer should be familiar with to get the most out of the Java EE 5 web-tier technologies. This first article in the series gives an overview of the new features introduced in version 2.1 of JSP technology.

    Under the auspices of JSR 245 at the Java Community Process (JCP), the main focus of JSP 2.1 technology has been to provide a better alignment with the next release of JavaServer Faces technology, version 1.2.

    The misalignment between the two technologies originated with the fact that version 1.0 of JavaServer Faces technology depended on JSP 1.2 technology. The reason is that the JSP 1.2 software was already widely available at the time, and the intention was to make the JavaServer Faces 1.0 interface more accessible to a broader audience. A consequence of this requirement was that JavaServer Faces technology could not take advantage of the EL introduced in the subsequent version of JSP technology, version 2.0. In addition, JSP 2.0 technology could not be modified to accommodate the needs of JavaServer Faces technology. And JSP 1.2 technology does not support an EL. Therefore, JavaServer Faces technology introduced an EL that was suited to its needs as a user interface (UI) component framework. As a result, page authors using JavaServer Faces technology tags with JSP technology code encountered some incompatibilities between the two technologies.

    The expert groups have worked together on the upcoming releases of JSP 2.1 and JavaServer Faces 1.2 technologies in Java EE 5 to fix these integration issues and make sure that the two technologies work together seamlessly. One result is that all of the web-tier technologies now share a unified EL, allowing you to mix code from all of these technologies freely and without worry. This article provides an overview of the work that was done to improve the alignment of these technologies.  It also explains the other minor improvements that made it into JSP 2.1 technology.

    The simple EL included in JSP 2.0 technology offers many advantages to the page author. Using simple expressions, page authors can easily access external data objects from their pages. The JSP technology container evaluates and resolves these expressions as it encounters them. It then immediately returns a response because the JSP request-processing model has only one phase, the render phase. However, because the request-processing model does not support a postback, all JSP expressions are read-only.

    Unlike JSP technology, JavaServer Faces technology supports a multiphase life cycle. When a user enters values into the JavaServer Faces UI components and submits the page, those values are converted, validated, and propagated to server-side data objects, after which component events are processed. In order to perform all these tasks in an orderly fashion, the JavaServer Faces life cycle is split into separate phases. Therefore, JavaServer Faces technology evaluates expressions at different phases of the life cycle rather than immediately, as JSP technology would do. Additionally, the expressions can be used to set as well as get data, so that the values a user enters into the UI components are propagated to server-side objects during a postback. Finally, some JavaServer Faces technology expressions can invoke methods on server-side objects during various stages of the life cycle in order to validate data and handle component events.

    Each EL suited the needs of its respective technology very well. When using both JSP technology tags or template text and JavaServer Faces technology tags, however, page authors would expose incompatibilities between the ELs. One example involves using JavaServer Faces components inside the c:forEach tag, as in the following example:

    <c:forEach var="location" items="${handler.locations}">
    <h:inputText value="#{location}"/>
    </c:forEach>
     

    The problem with this code stems from the fact that the iteration variable location is visible only within the boundaries of its iteration tag. This means that on a postback, the JavaServer Faces request life cycle has no way to access the value associated with the variable location.

    To solve problems such as these, the expert groups for JavaServer Faces and JSP technologies decided to unify the two ELs. They created a more powerful EL, the unified EL, that supports the following features:

  • Deferred expressions, which can be evaluated at different stages of the page life cycle
  • Expressions that can set data of external objects as well as get that data
  • Method expressions, which can invoke methods that perform event handling, validation, and other functions for the JavaServer Faces technology UI components
  • A flexible mechanism to enable customization of variable and property resolution for EL expression evaluation
  • With the addition of these features into a unified EL, the incompatibility problems such as that illustrated by the preceding c:forEach code example can be solved. Now, the iteration variable can be a deferred expression that refers to the proper object within the collection being iterated over -- for example, locations[0]. This way, the deferred expression can be evaluated later on during the JavaServer Faces request life cycle, as shown in the following code:

    <c:forEach var="location" items="#{locations}">
    <h:inputText value="#{location}"/>
    </c:forEach>
     

    For more details on changes made to the c:forEach tag and other JSTL tags, please see the upcoming article of this series, which will describe new features in JSTL. If you are a page author, make sure to read another upcoming article in the series, which describes the differences between the two EL syntaxes now supported in JSP technology. The article "Unified Expression Language (EL)" provides a much more detailed description. It also explains how to create a custom resolver, which is one of the new features of the unified EL.

    It is also important to note that the EL is useful beyond all of the web technology specifications. This is why the EL is agnostic of the technology hosting it and is currently defined through its own independent document within the JSP specification. This makes it clear that the EL is not dependent on the JSP specification and might therefore have its own JSR in the future.

    Prior to the Java EE 5 platform, accessing data sources, web services, environment entries, and Enterprise JavaBeans (EJB) bean references required the use of the Java Naming and Directory Interface (JNDI) API along with the declaration of entries into a deployment descriptor. Thanks to injection annotations, it is now possible to inject container-managed objects without having to write the traditional boilerplate code and deal with the deployment descriptor.

    For example, a tag handler could access a database through the following code:

    @Resource(name="jdbc/myDB")
    javax.sql.DataSource myDB;
    public getProductsByCategory() {
    // Get a connection and execute the query.
    Connection conn = mydb.getConnection();
    ...
    }
     

    The field myDB of type javax.sql.DataSource is annotated with @Resource and is injected by the container prior to the tag handler being made available to the application. The data source JNDI mapping is inferred from the field name catalogDS and type javax.sql.DataSource. Moreover, the myDB resource no longer needs to be defined in the deployment descriptor. An upcoming article in this series will provide more detail on how to perform injection with web-tier applications.

    A frequent complaint in user forums concerns the large number of blank lines, or white spaces, that often clutter the output of a JSP technology page. For example, consider this page:

    <%@page contentType="text/html"%>
    <%@page trimDirectiveWhitespaces="false"%>
    <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
    <jsp:useBean id="conference" class="conf.Conference" scope="page" />

    <%--
    Test JSP page to showcase the trimWhiteSpace directive.
    A few more lines here to create
    more blank lines in the output.
    --%>
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Test trimWhiteSpace directive</title>
    </head>
    <body>
    <h1>Test trimWhiteSpace directive</h1>

    Hello Pedro!<br>For the conference, you'll need to bring:<br>
    <c:forEach items="${conference.items}"
    var="conf" varStatus="stat">
    ${conf} <br>
    </c:forEach>
    </body>
    </html>
     

    Prior to JSP 2.1 technology, this page would produce the following output in the response (where represents the end of a blank line):





    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Test trimWhiteSpace directive</title>
    </head>
    <body>
    <h1>Test trimWhiteSpace directive</h1>

    Hello Pedro!<br>For the conference, you'll need to bring:<br>

    laptop <br>

    white papers <br>

    candy bars <br>

    </body>
    </html>
     

    Although these extra blank lines do not change the way a browser displays the output page, they do add extra payload to the response and make the HTML source code less legible. To solve this issue, JSP 2.1 technology now offers the configuration parameter trimDirectiveWhitespaces, which can be specified as a page directive or as a property-group configuration parameter that applies to a group of pages.

    With trimDirectiveWhitespaces enabled, template text containing only blank lines, or white space, is removed from the response output. In the code example above, the lines containing only blank lines would be removed, yielding the following output:

    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Test trimWhiteSpace directive</title>
    </head>
    <body>
    <h1>Test trimWhiteSpace directive</h1>

    Hello Pedro!<br>For the conference, you'll need to bring:<br>
    laptop<br>
    white papers<br>
    candy bars<br>
    </body>
    </html>
     

    Maintaining compatibility with earlier versions is critical for any Java EE platform release. For JSP 2.1 technology, the only backward compatibility issue involves the EL's new support of the #{ syntax that refers to deferred expressions. The #{ character sequence can create backward compatibility problems in two situations: when the sequence is included in template text or in attribute values.

    In JSP 2.1 technology, a deferred expression does not make sense in the context of template text because evaluation of an expression is always done immediately by the JSP container. Because no entity other than the JSP container processes template text, a deferred expression would always be left unevaluated if it were included in template text. Therefore, any page authors who have included the #{ character sequence in template text probably meant to use the ${} syntax instead. Acknowledging this fact, the JSP specification authors have mandated that the inclusion of #{ in template text triggers a translation error in JSP 2.1 technology.

    With respect to tag attribute values, it is important to note that tag libraries based on JSP versions prior to 2.1 (such as the 1.1 version of the JavaServer Faces component tag library) made extensive use of the #{ syntax to specify deferred expressions. Because these tag libraries must be responsible for the parsing of these deferred expressions, they must execute in an environment in which the #{ character sequence is processed as a string literal.

    To determine whether or not to treat the #{ character sequence as a string literal, the web container relies on the JSP version indicated by the tag library descriptor (TLD) associated with the tag library. The JSP version specified in the TLD tells the web container which version of the JSP specification the tag library is written for. JSP versions previous to 2.1 processed #{ as a string literal, as did applications based on those earlier versions. With version 2.1 or later, the character sequence #{ represents a deferred expression, assuming the attribute has been declared to support deferred expressions in the TLD. If the attribute does not support deferred expressions, then the presence of the #{ character sequence results in a translation error.

    Web-application developers who have developed applications for a version prior to JSP 2.1 technology and want to deploy and run them on a JSP 2.1 container must take the following actions to ensure that the container handles any instances of the #{ character sequence correctly:

  • Escape each instance of the #{ characters using a backslash: \#{.
  • Globally allow usage of the #{ character sequence as a string literal by setting the JSP property groups element deferred-syntax-allowed-as-literal to true or by setting the page/tag-file directive attribute deferredSyntaxAllowedAsStringLiteral to true.
  • Aside from the incompatibilities relating to the #{ character sequence, all JSP 2.0 technology applications will run as is on JSP 2.1 containers. However, if a web application uses third-party tag libraries that are based on JavaServer Faces 1.1 technology or earlier, some new features provided by JSP 2.1 and JavaServer Faces 1.2 technologies will not be available with these libraries. For example, although EL functions may now be used in the JavaServer Faces 1.2 core and HTML tag libraries, they cannot be used with third-party tag libraries that are based on JavaServer Faces 1.1 technology and earlier, because these libraries run on a version of the JavaServer Faces EL that did not support EL functions.

    Now that JavaServer Faces technology and JSTL are part of the Java EE 5 platform, they take precedence over any other version of these libraries that could be bundled with a web application. Some people may ask: "But what if I want to replace the container's implementation of these technologies with another implementation?" This is no longer possible. This would be like replacing the implementation of the java.util package in the Java Platform, Standard Edition (Java SE, formerly referred to as J2SE), a fruitless task. The same applies from now on with platform tag libraries.

    Essentially, applications should not replace the APIs that the Java EE 5 platform requires. If the platform architects really wanted to make this possible, they would have designed the APIs with service-provider interfaces to allow applications to easily swap in their own implementations. However, this was not their intention, and applications will always have to rely on the platform tag libraries bundled with a container.

    Let's look at a few other minor additions and clarifications to the JSP 2.1 specification. For a detailed list of all changes between JSP 2.0 and JSP 2.1, please consult Appendix E of the specification. Most notable are the following:

  • The JSP spec now supports Byte Order Marks (BOM) as authoritative page-encoding declarations for JSP technology pages and tag files in standard syntax.
  • Because JSP 2.1 technology requires J2SE 5.0, some APIs that can take advantage of generics have been modified.
  • And many more, as Appendix E points out.
  •  前言
      Regular Expressions(正则表达式,以下用RE称呼)对小弟来说一直都是神密的地带,看到一些网络上的大大,简单用RE就决解了某些文字的问题,小弟便兴起了学一学RE的想法,但小弟天生就比较懒一些,总希望看有没有些快速学习的方式,于是小弟又请出Google大神,藉由祂的神力,小弟在网络上找到了Jim Hollenhorst先生的文章,经过了阅读,小弟觉得真是不错,所以就做个小心得报告,跟Move-to.Net的朋友分享,希望能为各位大大带来一丁点在学习RE时的帮助。Jim Hollenhorst大大文章之网址如下,有需要的大大可直接连结。

      The 30 Minute Regex Tutorial By Jim Hollenhorst

      http://www.codeproject.com...

      什么是RE?
      想必各位大大在做文件查找的时侯都有使用过万用字符”*”,比如说想查找在Windows目录下所有的Word文件时,你可能就会用”*.doc”这样的方式来做查找,因为”*”所代表的是任意的字符。RE所做的就是类似这样的功能,但其功能更为强大。

      写程序时,常需要比对字符串是否符合特定样式,RE最主要的功能就是来描述这特定的样式,因此可以将RE视为特定样式的描述式,举个例子来说,”\w+”所代表的就是任何字母与数字所组成的非空字符串(non-null string)。在.NET framework中提供了非常强大的类别库,藉此可以很轻易的使用RE来做文字的查找与取代、对复杂标头的译码及验证文字等工作。

      学习RE最好的方式就是藉由例子亲自来做做看。Jim Hollenhorst大大也提供了一个工具程序Expresso(来杯咖啡吧),来帮助我们学习RE,下载的网址是http://www.codeproject.com...

      接下来,就让我们来体验一些例子吧。

      一些简单的例子
      假设要查找文章中Elvis后接有alive的文字符串的话,使用RE可能会经过下列的过程,括号是所下RE的意思:

      1. elvis (查找elvis)

      上述代表所要查找的字符顺序为elvis。在.NET中可以设定乎略字符的大小写,所以”Elvis”、”ELVIS”或者是”eLvIs”都是符合1所下的RE。但因为这只管字符出现的顺序为elvis,所以pelvis也是符合1所下的RE。可以用2的RE来改进。

      2. \belvis\b (将elvis视为一整体的字查找,如elvis、Elvis乎略字符大小写时)
    “\b”在RE中有特别的意思,在上述的例子中所指的就是字的边界,所以\belvis\b用\b把elvis的前后边界界定出来,也就是要elvis这个字。

      假设要将同一行里elvis后接有alive的文字符串找出来,此时就会用到另外二个特别意义的字符”.”及”*”。”.”所代表就是除了换行字符的任意字符,而”*”所代表的是重复*之前项目直到找到符合RE的字符串。所以”.*”所指的就是除了换行字符外的任意数目的字符数。所以查找同一行里elvis后接有alive的文字符串找出来,则可下如3之RE。

      3. \belvis\b.*\balive\b (查找elvis后面接有alive的文字符串,如elvis is alive)

      用简单之特别字符就可以组成功能强大的RE,但也发现当使用越来越多的特别字符时,RE就会越来越难看得懂了。


    再看看另外的例子
      组成有效的电话号码

      假使要从网页上收集顾客格式为xxx-xxxx的7位数字的电话号码,其中x是数字,RE可能会这样写。

      4. \b\d\d\d-\d\d\d\d (查找七位数字之电话号码,如123-1234)
      每一个\d代表一个数字。”-”则是一般的连字符号,为避免太多重复的\d,RE可以改写成如5的方式。

      5. \b\d{3}-\d{4} (查找七位数字电话号码较好的方法,如123-1234)
      在\d后的{3},代表重复前一个项目三次,也就是相等于\d\d\d。

      RE的学习及测试工具 Expresso

      因为RE不易阅读及使用者容易会下错RE的特性,Jim大大开发了一个工具软件Expresso,用来帮助使用者学习及测试RE,除了上面所述的网址之外,也可以上Ultrapico网站(http://www.Ultrapico.com)。安装完Expresso后,在Expression Library中,Jim大大把文章的例子都建立在其中,可以边看文章边测试,也可以试着修改范例所下的RE,马上可以看到结果,小弟觉得非常好用。各位大大可以试试。

      .NET中RE的基础概念
      特殊字符

      有些字符有特别的意义,比如之前所看到的”\b”、”.”、”*”、”\d”等。”\s”所代表的是任意空格符,比如说spaces、tabs、newlines等.。”\w”代表是任意字母或数字字符。

      再看一些例子吧
      6. \ba\w*\b (查找a开头的字,如able)
      这RE描述要查找一个字的开始边界(\b),再来是字母”a”,再加任意数目的字母数字(\w*),再接结束这个字的结束边界(\b)。

      7. \d+ (查找数字字符串)
      “+”和”*”非常相似,除了+至少要重复前面的项目一次。也就是说至少有一个数字。

      8. \b\w{6}\b (查找六个字母数字的字,如ab123c)

      下表为RE常用的特殊字符

      . 除了换行字符的任意字符
      \w 任意字母数字字符
      \s 任意空格符
      \d 任意数字字符
      \b 界定字的边界
      ^ 文章的开头,如”^The'' 用以表示出现于文章开头的字符串为”The”
      $ 文章的结尾,如”End$”用以表示出现在文章的结尾为”End”
      特殊字符”^”及”$”是用来查找某些字必需是文章的开头或结尾,这在验证输入是否符合某一样式时特别用有,比如说要验证七位数字的电话号码,可能会输入如下9的RE。

      9. ^\d{3}-\d{4}$ (验证七位数字之电话号码)

      这和第5个RE相同,但其前后都无其它的字符,也就是整串字符串只有这七个数字的电话号码。在.NET中如果设定Multiline这个选项,则”^”和”$”会每行进行比较,只要某行的开头结尾符合RE即可,而不是整个文章字符串做一次比较。

      转意字符(Escaped characters)

      有时可能会需要”^”、”$”单纯的字面意义(literal meaning)而不要将它们当成特殊字符,此时”\”字符就是用来移除特殊字符特别意义的字符,因此”\^”、”\.”、”\\”所代表的就是”^”、”.”、”\”的字面意义。

      重复前述项目

      在前面看过”{3}”及”*”可以用来重复前述字符,之后我们会看到如何用同样的语法重复整个次描述(subexpressions)。下表是使用重复前述项目的一些方式。

      * 重复任意次数
      + 重复至少一次
      ? 重复零次或一次
      {n} 重复n次
      {n,m} 重复至少n次,但不超过m次
      {n,} 重复至少n次

      再来试一些例子吧

      10. \b\w{5,6}\b (查找五个或六个字母数字字符的字,如as25d、d58sdf等)
      11. \b\d{3}\s\d{3}-\d{4} (查找十个数字的电话号码,如800 123-1234)
      12. \d{3}-\d{2}-\d{4} (查找社会保险号码,如 123-45-6789)
      13. ^\w* (每行或整篇文章的第一个字)
      在Espresso可试试有Multiline和没Multiline的不同。

      匹配某范围的字符

      有时需要查找某些特定的字符时怎么辨?这时中括号”[]”就派上了用场。因此[aeiou]所要查找的是”a”、”e”、”i”、”o”、”u”这些元音,[.?!]所要查找的是”.”、”?”、”!”这些符号,在中括号中的特殊字符的特别意义都会被移除,也就是解译成单纯的字面意义。也可以指定某些范围的字符,如”[a-z0-9]”,所指的就是任意小写字母或任意数字。

      接下来再看一个比较初复杂查找电话号码的RE例子

      14. \(?\d{3}[( ] \s?\d{3}[- ]\d{4} (查找十位数字之电话号码,如(080) 333-1234 )

      这样的RE可查找出较多种格式的电话号码,如(080) 123-4567、511 254 6654等。”\(?”代表一个或零个左小括号”(“,而”[( ]”代表查找一个右小括号”)”或空格符,”\s?”指一个或零个空格符组。但这样的RE会将类似”800) 45-3321”这样的电话找出来,也就是括号没有对称平衡的问题,之后会学到择一(alternatives)来决解这样的问题。

      不包含在某特定字符组里(Negation)

      有时需要查找在包含在某特定字符组里的字符,下表说明如何做类似这样的描述。

      \W 不是字母数字的任意字符
      \S 不是空格符的任意字符
      \D 不是数字字符的任意字符
      \B 不在字边界的位置
      [^x] 不是x的任意字符
      [^aeiou] 不是a、e、i、o、u的任意字符

      15. \S+ (不包含空格符的字符串)

      择一(Alternatives)

      有时会需要查找几个特定的选择,此时”|”这个特殊字符就派上用场了,举例来说,要查找五个数字及九个数字(有”-”号)的邮政编码。

      16. \b\d{5}-\d{4}\b|\b\d{5}\b (查找五个数字及九个数字(有”-”号)的邮政编码)

      在使用Alternatives时需要注意的是前后的次序,因为RE在Alternatives中会优先选择符合最左边的项目,16中,如果把查找五个数字的项目放在前面,则这RE只会找到五个数字的邮政编码。了解了择一,可将14做更好的修正。

      17. (\(\d{3}\)|\d{3})\s?\d{3}[- ]\d{4} (十个数字的电话号码)

      群组(Grouping)

      括号可以用来介定一个次描述,经由次描述的介定,可以针对次描述做重复或及他的处理。

      18. (\d{1,3}\.){3}\d{1,3} (寻找网络地址的简单RE)

      此RE的意思第一个部分(\d{1,3}\.){3},所指的是,数字最小一位最多三位,并且后面接有”.”符号,此类型的共有三个,之后再接一到三位的数字,也就是如192.72.28.1这样的数字。

      但这样会有个缺点,因为网络地址数字最多只到255,但上述的RE只要是一到三位的数字都是符合的,所以这需要让比较的数字小于256才行,但只单独使用RE并无法做这样的比较。在19中使用择一来将地址的限制在所需要的范围内,也就是0到255。

      19. ((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?) (寻找网络地址)

      有没有发觉RE越来越像外星人说的话了?就以简单的寻找网络地址,直接看RE都满难理解的哩。

      Expresso Analyzer View

      Expresso提供了一个功能,它可以将所下的RE变成树状的说明,一组组的分开说明,提供了一个好的除错环境。其它的功能,如部分符合(Partial Match只查找反白RE的部分)及除外符合(Exclude Match只不查找反白RE的部分)就留给各位大大试试啰。

      当次描述用括号群组起来时,符合次描述的文字可用在之后的程序处理或RE本身。在预设的情型下,所符合的群组是由数字命名,由1开始,由顺序是由左至右,这自动群组命名,可在Expresso中的skeleton view或result view中看到。

      Backreference是用来查找群组中抓取的符合文字所相同的文字。举例来说”\1”所指符合群组1所抓取的文字。

      20. \b(\w+)\b\s*\1\b (寻找重复字,此处说的重复是指同样的字,中间有空白隔开如dog dog这样的字)
    (\w+)会抓取至少一个字符的字母或数字的字,并将它命名为群组1,之后是查找任意空格符,再接和群组1相同的文字。

      如果不喜欢群组自动命名的1,也可以自行命名,以上述例子为例,(\w+)改写为(?<Word>\w+),这就是将所抓取的群组命名为Word,Backreference就要改写成为\k<Word>
    21. \b(?<Word>\w+)\b\s*\k<Word>\b (使用自行命名群组抓取重复字)

      使用括号还有许多特别的语法元素,比较通用的列表如下:

      抓取(Captures) 
      (exp) 符合exp并抓取它进自动命名的群组
      (?<name>exp) 符合exp并抓取它进命名的群组name
      (?:exp) 符合exp,不抓取它
      Lookarounds 
      (?=exp) 符合字尾为exp的文字
      (?<=exp) 符合前缀为exp的文字
      (?!exp) 符合后面没接exp字尾的文字
      (?<!exp) 符合前面没接exp前缀的文字
      批注Comment 
      (?#comment) 批注

      Positive Lookaround

      接下来要谈的是lookahead及lookbehind assertions。它们所查找的是目前符合之前或之后的文字,并不包含目前符合本身。这些就如同”^”及”\b”特殊字符,本身并不会对应任何文字(用来界定位置),也因此称做是zero-width assertions,看些例子也许会清楚些。

      (?=exp)是一个”zero-width positive lookahead assertion”。它指的就是符合字尾为exp的文字,但不包含exp本身。

      22. \b\w+(?=ing\b) (字尾为ing的字,比如说filling所符合的就是fill)
    (?<=exp)是一个”zero-width positive lookbehind assertion”。它指的就是符合前缀为exp的文字,但不包含exp本身。

      23. (?<=\bre)\w+\b (前缀为re的字,比如说repeated所符合的就是peated)
      24. (?<=\d)\d{3}\b (在字尾的三位数字,且之前接一位数字)
      25. (?<=\s)\w+(?=\s) (由空格符分隔开的字母数字字符串)

      Negative Lookaround

      之前有提到,如何查找一个非特定或非在特定群组的字符。但如果只是要验证某字符不存在而不要对应这些字符进来呢?举个例子来说,假设要查找一个字,它的字母里有q但接下来的字母不是u,可以用下列的RE来做。

      26. \b\w*q[^u]\w*\b (一个字,其字母里有q但接下来的字母不是u)

      这样的RE会有一个问题,因为[^u]要对应一个字符,所以若q是字的最后一个字母,[^u]这样的下法就会将空格符对应下去,结果就有可能会符合二个字,比如说”Iraq haha”这样的文字。使用Negative Lookaround就能解决这样的问题。

      27. \b\w*q(?!u)\w*\b (一个字,其字母里有q但接下来的字母不是u)
      这是”zero-width negative lookahead assertion”。

      28. \d{3}(?!\d) (三个位的数字,其后不接一个位数字)

      同样的,可以使用(?<!exp),”zero-width negative lookbehind assertion”,来符合前面没接exp前缀的文字符串。

      29. (?<![a-z ])\w{7} (七个字母数字的字符串,其前面没接字母或空格)

      30. (?<=<(\w+)>).*(?=<\/\1>) (HTML卷标间的文字)
      这使用lookahead及lookbehind assertion来取出HTML间的文字,不包括HTML卷标。

      请批注(Comments Please)
      括号还有个特殊的用途就是用来包住批注,语法为”(?#comment)”,若设定”Ignore Pattern Whitespace”选项,则RE中的空格符当RE使用时会乎略。此选项设定时,”#”之后的文字会乎略。

      31. HTML卷标间的文字,加上批注

      (?<=   #查找前缀,但不包含它
      <(\w+)> #HTML标签
      )       #结束查找前缀
      .*      #符合任何文字
      (?=     #查找字尾,但不包含它
      <\/\1>  #符合所抓取群组1之字符串,也就是前面小括号的HTML标签
      )       #结束查找字尾

      寻找最多字符的字及最少字符的字(Greedy and Lazy)
      当RE下要查找一个范围的重复时(如”.*”),它通常会寻找最多字符的符合字,也就是Greedy matching。举例来说。

      32. a.*b  (开始为a结束为b的最多字符的符合字)

      若有一字符串是”aabab”,使用上述RE所得到的符合字符串就是”aabab”,因为这是寻找最多字符的字。有时希望是符合最少字符的字也就是lazy matching。只要将重复前述项目的表加上问号(?)就可以把它们全部变成lazy matching。因此”*?”代表的就是重复任意次数,但是使用最少重复的次数来符合。举个例子来说:

      33. a.*?b (开始为a结束为b的最少字符的符合字)

      若有一字符串是”aabab”,使用上述RE第一个所得到的符合字符串就是”aab”再来是”ab”,因为这是寻找最少字符的字。

      *? 重复任意次数,最少重复次数为原则
      +? 重复至少一次,最少重复次数为原则
      ?? 重复零次或一次,最少重复次数为原则
      {n,m}? 重复至少n次,但不超过m次,最少重复次数为原则
      {n,}? 重复至少n次,最少重复次数为原则

    还有什么没提到呢?

      到目前为止,已经提到了许多建立RE的元素,当然还有许多元素没有提到,下表整理了一些没提到的元素,在最左边的字段的数字是说明在Expresso中的例子。

      # 语法 说明

      \a Bell 字符
      \b 通常是指字的边界,在字符组里所代表的就是backspace
      \t Tab

      34 \r Carriage return

      \v Vertical Tab
      \f From feed

      35 \n New line
       \e Escape

      36 \nnn ASCII八位码为nnn的字符

      37 \xnn 十六位码为nn的字符

      38 \unnnn Unicode为nnnn的字符

      39 \cN Control N字符,举例来说Ctrl-M是\cM

      40 \A 字符串的开始(和^相似,但不需籍由multiline选项)

      41 \Z 字符串的结尾
      \z 字符串的结尾

      42 \G 目前查找的开始

      43 \p{name} Unicode 字符组名称为name的字符,比如说\p{Lowercase_Letter} 所指的就是小写字
      (?>exp) Greedy次描述,又称之为non-backtracking次描述。这只符合一次且不采backtracking。

      44 (?<x>-<y>exp)

      or (?-<y>exp) 平衡群组。虽复杂但好用。它让已命名的抓取群组可以在堆栈中操作使用。(小弟对这个也是不太懂哩)

      45 (?im-nsx:exp) 为次描述exp更改RE选项,比如(?-i:Elvis)就是把Elvis大乎略大小写的选项关掉

      46 (?im-nsx) 为之后的群组更改RE选项。
      (?(exp)yes|no) 次描述exp视为zero-width positive lookahead。若此时有符合,则yes次描述为下一个符合标的,若否,则no 次描述为下一个符合标的。
      (?(exp)yes) 和上述相同但无no次描述
      (?(name)yes|no) 若name群组为有效群组名称,则yes次描述为下一个符合标的,若否,则no 次描述为下一个符合标的。

      47 (?(name)yes) 和上述相同但无no次描述

      结论
      经过了一连串的例子,及Expresso的帮忙,相信各位大大对RE有个基本的了解,网络上当然有许多有关于RE的文章,如果各位大大有兴趣http://www.codeproject.com...还有许多关于RE的相关文章。若大大对书有兴趣的话,Jeffrey Friedl的Mastering Regular Expressions很多大大都有推(小弟还没拜读)。希望籍由这样的心得报告,能让对RE有兴趣的大大能缩短学习曲线,当然这是小弟第一次接触RE,若文章中有什么错误或说明的不好的地方,可要请各位大大体谅,并请各位大大将需要修正的地方mail给小弟,小弟会非常感谢各位大大。

    症状:
    上传文件到 Windows 2003 server + IIS 6.0 服务器的时候遇到下列错误:
    请求对象错误 'ASP 0104 : 80004005'
    操作被禁止
    /Upload.asp, line 40

    原因:
    IIS6.0 禁止上传超过 200kB 的文件. 因此你需要修改 IIS 的默认设置.

    技术背景
    在 IIS 6.0 中, AspMaxRequestEntityAllowed 属性指定了一个 ASP 请求(Request)可以使用的最大字节数. 如果 Content-Length 头信息中包含的请求长度超过了 AspMaxRequestEntityAllowed 的值, IIS 将返回一个 403 错误信息.
    这个属性值与 MaxRequestEntityAllowed 相似, 但是是针对 ASP 请求的. 假如你知道自己的 ASP 应用只需要处理很少的请求数据, 你可以在 World Wide Web Publishing Service (WWW 发布服务)层级设定全局的 MaxRequestEntityAllowed 属性为 1MB, 并单独设定 AspMaxRequestEntityAllowed 为一个较小的值.

    解决方法
    打开位于 C:\Windows\System32\Inetsrv 中的 metabase.XML, 并修改 AspMaxRequestEntityAllowed 为你需要的值(例如 1073741824, 1GB).

    ASP与存储过程(Stored Procedures)的文章不少,但是我怀疑作者们是否真正实践过。我在初学时查阅过大量相关资料,发现其中提供的很多方法实际操作起来并不是那么回事。对于简单的应用,这些资料也许是有帮助的,但仅限于此,因为它们根本就是千篇一律,互相抄袭,稍微复杂点的应用,就全都语焉不详了。
        现在,我基本上通过调用存储过程访问SQL Server,以下的文字虽不敢保证绝对正确,但都是实践的总结,希望对大家能有帮助。

        存储过程就是作为可执行对象存放在数据库中的一个或多个SQL命令。
        定义总是很抽象。存储过程其实就是能完成一定操作的一组SQL语句,只不过这组语句是放在数据库中的(这里我们只谈SQL Server)。如果我们通过创建存储过程以及在ASP中调用存储过程,就可以避免将SQL语句同ASP代码混杂在一起。这样做的好处至少有三个:
        第一、大大提高效率。存储过程本身的执行速度非常快,而且,调用存储过程可以大大减少同数据库的交互次数。
        第二、提高安全性。假如将SQL语句混合在ASP代码中,一旦代码失密,同时也就意味着库结构失密。
        第三、有利于SQL语句的重用。
       
        在ASP中,一般通过command对象调用存储过程,根据不同情况,本文也介绍其它调用方法。为了方便说明,根据存储过程的输入输出,作以下简单分类:
        1. 只返回单一记录集的存储过程
        假设有以下存储过程(本文的目的不在于讲述T-SQL语法,所以存储过程只给出代码,不作说明):

        /*SP1*/
        CREATE PROCEDURE dbo.getUserList
        as
        set nocount on
        begin
           select * from dbo.[userinfo]
        end
        go

        以上存储过程取得userinfo表中的所有记录,返回一个记录集。通过command对象调用该存储过程的ASP代码如下:
       
        '**通过Command对象调用存储过程**
        DIM MyComm,MyRst
        Set MyComm = Server.CreateObject("ADODB.Command")
        MyComm.ActiveConnection = MyConStr          'MyConStr是数据库连接字串
        MyComm.CommandText      = "getUserList"     '指定存储过程名
        MyComm.CommandType      = 4                 '表明这是一个存储过程
        MyComm.Prepared         = true              '要求将SQL命令先行编译
        Set MyRst = MyComm.Execute
        Set MyComm = Nothing

        存储过程取得的记录集赋给MyRst,接下来,可以对MyRst进行操作。
        在以上代码中,CommandType属性表明请求的类型,取值及说明如下:
          -1   表明CommandText参数的类型无法确定
          1    表明CommandText是一般的命令类型
          2    表明CommandText参数是一个存在的表名称
          4    表明CommandText参数是一个存储过程的名称
       
        还可以通过Connection对象或Recordset对象调用存储过程,方法分别如下:

        '**通过Connection对象调用存储过程**
        DIM MyConn,MyRst
        Set MyConn = Server.CreateObject("ADODB.Connection")
        MyConn.open MyConStr                            'MyConStr是数据库连接字串
        Set MyRst  = MyConn.Execute("getUserList",0,4)  '最后一个参断含义同CommandType
        Set MyConn = Nothing

        '**通过Recordset对象调用存储过程**
        DIM MyRst
        Set MyRst = Server.CreateObject("ADODB.Recordset")
        MyRst.open "getUserList",MyConStr,0,1,4
        'MyConStr是数据库连接字串,最后一个参断含义与CommandType相同

       
        2. 没有输入输出的存储过程
        请看以下存储过程:

        /*SP2*/
        CREATE PROCEDURE dbo.delUserAll
        as
        set nocount on
        begin
           delete from dbo.[userinfo]
        end
        go

        该存储过程删去userinfo表中的所有记录,没有任何输入及输出,调用方法与上面讲过的基本相同,只是不用取得记录集:

        '**通过Command对象调用存储过程**
        DIM MyComm
        Set MyComm = Server.CreateObject("ADODB.Command")
        MyComm.ActiveConnection = MyConStr          'MyConStr是数据库连接字串
        MyComm.CommandText      = "delUserAll"      '指定存储过程名
        MyComm.CommandType      = 4                 '表明这是一个存储过程
        MyComm.Prepared         = true              '要求将SQL命令先行编译
        MyComm.Execute                              '此处不必再取得记录集
        Set MyComm = Nothing 

        当然也可通过Connection对象或Recordset对象调用此类存储过程,不过建立Recordset对象是为了取得记录集,在没有返回记录集的情况下,还是利用Command对象吧。


        3. 有返回值的存储过程
        在进行类似SP2的操作时,应充分利用SQL Server强大的事务处理功能,以维护数据的一致性。并且,我们可能需要存储过程返回执行情况,为此,将SP2修改如下:

        /*SP3*/
        CREATE PROCEDURE dbo.delUserAll
        as
        set nocount on
        begin
           BEGIN TRANSACTION
           delete from dbo.[userinfo]
           IF @@error=0
              begin
                 COMMIT TRANSACTION
                 return 1
              end
           ELSE
              begin
                 ROLLBACK TRANSACTION
                 return 0
              end        
           return
        end
        go

        以上存储过程,在delete顺利执行时,返回1,否则返回0,并进行回滚操作。为了在ASP中取得返回值,需要利用Parameters集合来声明参数:

        '**调用带有返回值的存储过程并取得返回值**
        DIM MyComm,MyPara
        Set MyComm = Server.CreateObject("ADODB.Command")
        MyComm.ActiveConnection = MyConStr          'MyConStr是数据库连接字串
        MyComm.CommandText      = "delUserAll"      '指定存储过程名
        MyComm.CommandType      = 4                 '表明这是一个存储过程
        MyComm.Prepared         = true              '要求将SQL命令先行编译
        '声明返回值
        Set Mypara = MyComm.CreateParameter("RETURN",2,4)
        MyComm.Parameters.Append MyPara
        MyComm.Execute
        '取得返回值
        DIM retValue
        retValue = MyComm(0)    '或retValue = MyComm.Parameters(0)
        Set MyComm = Nothing
       
        在MyComm.CreateParameter("RETURN",2,4)中,各参数的含义如下:
        第一个参数("RETURE")为参数名。参数名可以任意设定,但一般应与存储过程中声明的参数名相同。此处是返回值,我习惯上设为"RETURE";
        第二个参数(2),表明该参数的数据类型,具体的类型代码请参阅ADO参考,以下给出常用的类型代码:
        adBigInt: 20 ;
        adBinary : 128 ;
        adBoolean: 11 ;
        adChar: 129 ;
        adDBTimeStamp: 135 ;
        adEmpty: 0 ;
        adInteger: 3 ;
        adSmallInt: 2 ;
        adTinyInt: 16 ;
        adVarChar: 200 ;
        对于返回值,只能取整形,且-1到-99为保留值;
        第三个参数(4),表明参数的性质,此处4表明这是一个返回值。此参数取值的说明如下:
        0 : 类型无法确定; 1: 输入参数;2: 输入参数;3:输入或输出参数;4: 返回值
       
        以上给出的ASP代码,应该说是完整的代码,也即最复杂的代码,其实

        Set Mypara = MyComm.CreateParameter("RETURN",2,4)
        MyComm.Parameters.Append MyPara
           
        可以简化为

        MyComm.Parameters.Append MyComm.CreateParameter("RETURN",2,4)

        甚至还可以继续简化,稍后会做说明。
        对于带参数的存储过程,只能使用Command对象调用(也有资料说可通过Connection对象或Recordset对象调用,但我没有试成过)。


        4. 有输入参数和输出参数的存储过程
        返回值其实是一种特殊的输出参数。在大多数情况下,我们用到的是同时有输入及输出参数的存储过程,比如我们想取得用户信息表中,某ID用户的用户名,这时候,有一个输入参数----用户ID,和一个输出参数----用户名。实现这一功能的存储过程如下:

        /*SP4*/
        CREATE PROCEDURE dbo.getUserName
           @UserID int,
           @UserName varchar(40) output
        as
        set nocount on
        begin
           if @UserID is null return
           select @UserName=username
               from dbo.[userinfo]
               where userid=@UserID
           return
        end
        go

        调用该存储过程的ASP代码如下:

        '**调用带有输入输出参数的存储过程**
        DIM MyComm,UserID,UserName
        UserID = 1
        Set MyComm = Server.CreateObject("ADODB.Command")
        MyComm.ActiveConnection = MyConStr          'MyConStr是数据库连接字串
        MyComm.CommandText      = "getUserName"     '指定存储过程名
        MyComm.CommandType      = 4                 '表明这是一个存储过程
        MyComm.Prepared         = true              '要求将SQL命令先行编译
        '声明参数
        MyComm.Parameters.append MyComm.CreateParameter("@UserID",3,1,4,UserID)
        MyComm.Parameters.append MyComm.CreateParameter("@UserName",200,2,40)
        MyComm.Execute
        '取得出参
        UserName = MyComm(1)
        Set MyComm = Nothing

        在以上代码中,可以看到,与声明返回值不同,声明输入参数时需要5个参数,声明输出参数时需要4个参数。声明输入参数时5个参数分别为:参数名、参数数据类型、参数类型、数据长度、参数值。声明输出参数时,没有最后一个参数:参数值。
        需要特别注意的是:在声明参数时,顺序一定要与存储过程中定义的顺序相同,而且各参数的数据类型、长度也要与存储过程中定义的相同。
        如果存储过程有多个参数,ASP代码会显得繁琐,可以使用with命令简化代码:

        '**调用带有输入输出参数的存储过程(简化代码)**
        DIM MyComm,UserID,UserName
        UserID = 1
        Set MyComm = Server.CreateObject("ADODB.Command")
        with MyComm
           .ActiveConnection = MyConStr          'MyConStr是数据库连接字串
           .CommandText      = "getUserName"     '指定存储过程名
           .CommandType      = 4                 '表明这是一个存储过程
           .Prepared         = true              '要求将SQL命令先行编译
           .Parameters.append .CreateParameter("@UserID",3,1,4,UserID)
           .Parameters.append .CreateParameter("@UserName",200,2,40)
           .Execute
        end with
        UserName = MyComm(1)
        Set MyComm = Nothing
       
        假如我们要取得ID为1到10,10位用户的用户名,是不是要创建10次Command对象呢?不是的。如果需要多次调用同一存储过程,只需改变输入参数,就会得到不同的输出:

        '**多次调用同一存储过程**
        DIM MyComm,UserID,UserName
        UserName = ""
        Set MyComm = Server.CreateObject("ADODB.Command")
        for UserID = 1 to 10
           with MyComm
              .ActiveConnection = MyConStr          'MyConStr是数据库连接字串
              .CommandText      = "getUserName"     '指定存储过程名
              .CommandType      = 4                 '表明这是一个存储过程
              .Prepared         = true              '要求将SQL命令先行编译
              if UserID = 1 then
                 .Parameters.append .CreateParameter("@UserID",3,1,4,UserID)
                 .Parameters.append .CreateParameter("@UserName",200,2,40)
                 .Execute
              else
                 '重新给入参赋值(此时参数值不发生变化的入参以及出参不必重新声明)
                 .Parameters("@UserID") = UserID
                 .Execute
              end if
           end with
           UserName = UserName + MyComm(1) + ","    '也许你喜欢用数组存储
        next
        Set MyComm = Nothing

        通过以上代码可以看出:重复调用同一存储过程时,只需为值发生改变的输入参数重新赋值即可,这一方法在有多个输入输出参数,且每次调用时只有一个输入参数的值发生变化时,可以大大减少代码量。


        5. 同时具有返回值、输入参数、输出参数的存储过程
        前面说过,在调用存储过程时,声明参数的顺序要与存储过程中定义的顺序相同。还有一点要特别注意:如果存储过程同时具有返回值以及输入、输出参数,返回值要最先声明。
        为了演示这种情况下的调用方法,我们改善一下上面的例子。还是取得ID为1的用户的用户名,但是有可能该用户不存在(该用户已删除,而userid是自增长的字段)。存储过程根据用户存在与否,返回不同的值。此时,存储过程和ASP代码如下:

        /*SP5*/
        CREATE PROCEDURE dbo.getUserName
           --为了加深对"顺序"的印象,将以下两参数的定义顺序颠倒一下
           @UserName varchar(40) output,
           @UserID int
        as
        set nocount on
        begin
           if @UserID is null return
           select @UserName=username
               from dbo.[userinfo]
               where userid=@UserID
           if @@rowcount>0
              return 1
           else
              return 0
           return
        end
        go

        '**调用同时具有返回值、输入参数、输出参数的存储过程**
        DIM MyComm,UserID,UserName
        UserID = 1
        Set MyComm = Server.CreateObject("ADODB.Command")
        with MyComm
           .ActiveConnection = MyConStr          'MyConStr是数据库连接字串
           .CommandText      = "getUserName"     '指定存储过程名
           .CommandType      = 4                 '表明这是一个存储过程
           .Prepared         = true              '要求将SQL命令先行编译
           '返回值要最先被声明
           .Parameters.Append .CreateParameter("RETURN",2,4)
           '以下两参数的声明顺序也做相应颠倒
           .Parameters.append .CreateParameter("@UserName",200,2,40)
           .Parameters.append .CreateParameter("@UserID",3,1,4,UserID)
           .Execute
        end with
        if MyComm(0) = 1 then
           UserName = MyComm(1)
        else
           UserName = "该用户不存在"
        end if
        Set MyComm = Nothing


        6. 同时返回参数和记录集的存储过程
        有时候,我们需要存储过程同时返回参数和记录集,比如在利用存储过程分页时,要同时返回记录集以及数据总量等参数。以下给出一个进行分页处理的存储过程:

        /*SP6*/
        CREATE PROCEDURE dbo.getUserList
           @iPageCount int OUTPUT,   --总页数
           @iPage int,               --当前页号
           @iPageSize int            --每页记录数
        as
        set nocount on
        begin
           --创建临时表
           create table #t (ID int IDENTITY,   --自增字段
                            userid int,
                            username varchar(40))
           --向临时表中写入数据
           insert into #t
              select userid,username from dbo.[UserInfo]
                 order by userid
          
           --取得记录总数
           declare @iRecordCount int
           set @iRecordCount = @@rowcount

           --确定总页数
           IF @iRecordCount%@iPageSize=0
              SET @iPageCount=CEILING(@iRecordCount/@iPageSize)
           ELSE
              SET @iPageCount=CEILING(@iRecordCount/@iPageSize)+1
     
           --若请求的页号大于总页数,则显示最后一页
           IF @iPage > @iPageCount
              SELECT @iPage = @iPageCount

           --确定当前页的始末记录
           DECLARE @iStart int    --start record
           DECLARE @iEnd int      --end record
           SELECT @iStart = (@iPage - 1) * @iPageSize
           SELECT @iEnd = @iStart + @iPageSize + 1

           --取当前页记录   
           select * from #t where ID>@iStart and ID<@iEnd

           --删除临时表
           DROP TABLE #t

           --返回记录总数
           return @iRecordCount
        end
        go

        在上面的存储过程中,输入当前页号及每页记录数,返回当前页的记录集,总页数及记录总数。为了更具典型性,将记录总数以返回值的形式返回。以下是调用该存储过程的ASP代码(具体的分页操作略去):

        '**调用分页存储过程**
        DIM pagenow,pagesize,pagecount,recordcount
        DIM MyComm,MyRst
        pagenow = Request("pn")
        '自定义函数用于验证自然数
        if CheckNar(pagenow) = false then pagenow = 1
        pagesize = 20
        Set MyComm = Server.CreateObject("ADODB.Command")
        with MyComm
           .ActiveConnection = MyConStr          'MyConStr是数据库连接字串
           .CommandText      = "getUserList"     '指定存储过程名
           .CommandType      = 4                 '表明这是一个存储过程
           .Prepared         = true              '要求将SQL命令先行编译
           '返回值(记录总量)
           .Parameters.Append .CreateParameter("RETURN",2,4)
           '出参(总页数)
           .Parameters.Append .CreateParameter("@iPageCount",3,2)
           '入参(当前页号)
           .Parameters.append .CreateParameter("@iPage",3,1,4,pagenow)
           '入参(每页记录数)
           .Parameters.append .CreateParameter("@iPageSize",3,1,4,pagesize)
           Set MyRst = .Execute
        end with
        if MyRst.state = 0 then        '未取到数据,MyRst关闭
           recordcount = -1
        else
           MyRst.close    '注意:若要取得参数值,需先关闭记录集对象
           recordcount = MyComm(0)
           pagecount   = MyComm(1)
           if cint(pagenow)>=cint(pagecount) then pagenow=pagecount
        end if
        Set MyComm = Nothing

        '以下显示记录
        if recordcount = 0 then
           Response.Write "无记录"
        elseif recordcount > 0 then
           MyRst.open
           do until MyRst.EOF
           ......
           loop
           '以下显示分页信息
           ......
        else  'recordcount=-1
           Response.Write "参数错误"
        end if

        对于以上代码,只有一点需要说明:同时返回记录集和参数时,若要取得参数,需先将记录集关闭,使用记录集时再将其打开。


        7. 返回多个记录集的存储过程
        本文最先介绍的是返回记录集的存储过程。有时候,需要一个存储过程返回多个记录集,在ASP中,如何同时取得这些记录集呢?为了说明这一问题,在userinfo表中增加两个字段:usertel及usermail,并设定只有登录用户可以查看这两项内容。

        /*SP7*/
        CREATE PROCEDURE dbo.getUserInfo
           @userid int,
           @checklogin bit
        as
        set nocount on
        begin
           if @userid is null or @checklogin is null return
           select username
              from dbo.[usrinfo]
              where userid=@userid
           --若为登录用户,取usertel及usermail
           if @checklogin=1
              select usertel,usermail
                 from dbo.[userinfo]
                 where userid=@userid
           return
        end
        go

        以下是ASP代码:

        '**调用返回多个记录集的存储过程**
        DIM checklg,UserID,UserName,UserTel,UserMail
        DIM MyComm,MyRst
        UserID = 1
        'checklogin()为自定义函数,判断访问者是否登录
        checklg = checklogin()
        Set MyComm = Server.CreateObject("ADODB.Command")
        with MyComm
           .ActiveConnection = MyConStr          'MyConStr是数据库连接字串
           .CommandText      = "getUserInfo"     '指定存储过程名
           .CommandType      = 4                 '表明这是一个存储过程
           .Prepared         = true              '要求将SQL命令先行编译
           .Parameters.append .CreateParameter("@userid",3,1,4,UserID)
           .Parameters.append .CreateParameter("@checklogin",11,1,1,checklg)
           Set MyRst = .Execute
        end with
        Set MyComm = Nothing

        '从第一个记录集中取值
        UserName = MyRst(0)
        '从第二个记录集中取值
        if not MyRst is Nothing then
           Set MyRst = MyRst.NextRecordset()
           UserTel  = MyRst(0)
           UserMail = MyRst(1)
        end if
        Set MyRst = Nothing

        以上代码中,利用Recordset对象的NextRecordset方法,取得了存储过程返回的多个记录集。


        至此,针对ASP调用存储过程的各种情况,本文已做了较为全面的说明。最后说一下在一个ASP程序中,调用多个存储过程的不同方法。
        在一个ASP程序中,调用多个存储过程至少有以下三种方法是可行的:
        1. 创建多个Command对象
        
        DIM MyComm
        Set MyComm = Server.CreateObject("ADODB.Command")
        '调用存储过程一
        ......
        Set MyComm = Nothing
        Set MyComm = Server.CreateObject("ADODB.Command")
        '调用存储过程二
        ......
        Set MyComm = Nothing
        ......

        2. 只创建一个Command对象,结束一次调用时,清除其参数

        DIM MyComm
        Set MyComm = Server.CreateObject("ADODB.Command")
        '调用存储过程一
        .....
        '清除参数(假设有三个参数)
        MyComm.Parameters.delete 2
        MyComm.Parameters.delete 1
        MyComm.Parameters.delete 0
        '调用存储过程二并清除参数
        ......
        Set MyComm = Nothing

        此时要注意:清除参数的顺序与参数声明的顺序相反,原因嘛,我也不知道。

        3. 利用Parameters数据集合的Refresh方法重置Parameter对象

        DIM MyComm
        Set MyComm = Server.CreateObject("ADODB.Command")
        '调用存储过程一
        .....
        '重置Parameters数据集合中包含的所有Parameter对象
        MyComm.Parameters.Refresh
        '调用存储过程二
        .....
        Set MyComm = Nothing
     
        一般认为,重复创建对象是效率较低的一种方法,但是经测试(测试工具为Microsoft Application Center Test),结果出人意料:
        方法2 >= 方法1 >> 方法3
        方法2的运行速度大于等于方法1(最多可高4%左右),这两种方法的运行速度远大于方法3(最多竟高达130%),所以建议在参数多时,采用方法1,在参数较少时,采用方法2。

        花了一天的时间,终于把我对于在ASP中调用存储过程的一些粗浅的经验形成了文字。这其中,有些是我只知其果而不明其因的,有些可能是错误的,但是,这些都是经过我亲身实践的。各位看官批判地接受吧。有不同意见,希望一定向我指明,先谢了。

    大家应该都知道 Recordset 有个 GetRows 属性,但是真正使用的不多,我也是最近才用的!汗……

    其实这个属性很简单,就是把数据集输出到一个数组中。但是实用性可不小,在这里我举一个例子说明一下GetRows的使用方法,大家举一反三能想到更多的用法!

    比如一个分类的表 T_Cate,结构和数据如下:

    ID   |   Title   |  Intro
    -----------------------------------------
    1    |   新闻   |  这里是新闻
    2    |   教程   |  这里是教程
    3    |   下载   |  这里是下载

    好了,表建立好了,数据也有了,下面我们就要用到GetRows咯!

    Dim Rs_Cate
    Dim Arr_Cate
    Set Rs_Cate=Conn.ExeCute("SELECT ID,Title,Intro FROM T_Cate ORDER BY ID ASC")
    Arr_Cate=Rs_Cate.GetRows
    Set Rs_Cate=Nothing

    好了表数据已经导出到数组了!下面我们将遍历这个数组

    Dim Arr_CateNumS,Arr_CateNumI
    Arr_CateNumS=Ubound(Arr_Cate,2)  '得到数组中数据的下标
    For Arr_CateNumI=0 To Arr_CateNumS
        Response.Write("ID:"&Arr_Cate(0,Arr_CateNumI)&" | 标题:"&Arr_Cate(1,Arr_CateNumI)&" | 介绍:"&Arr_Cate(2,Arr_CateNumI)&"<br>")
    Next

    呵呵,好了,输出的数据为:
    ID:1 | 标题:新闻 | 介绍:这里是新闻
    ID:2 | 标题:教程 | 介绍:这里是教程
    ID:3 | 标题:下载 | 介绍:这里是下载

    好了,具体就写这么多吧!文采不行,如果大家有什么不明白的,多用用就可以了,呵呵

    本文对JBuilder和MsSQL的安装不在此累诉,重点从MsSQL 的JDBC driver谈起。

    首先,安装mssql的JDBC driver的压缩包,其实在这个包中真正有用的就是mssqlserver.jar,msutil.jar,msbase.jar,所以我直接把它放到c:\system\mssql-jdbc(这个目录可以自己定义)下

    JBuilder中,做如下的操作:

    一、选择Tool -> Configure Libraries,在Configure Libraries窗口中选择左边列表框下的New,弹出New Library Wizard 窗口,在Name中输入mssql(可以自己定义),Location中选择User Home,点击Add,选择mysql jdbc的路径:c:\system\mssql-jdbc\mssqlserver.jar,c:\system\mssql-jdbc\msutil.jar,c:\system\mssql-jdbc\msbase.jar,点击ok(两次);

     

    二、选择Project -> Project Properties -> Paths -> Required Libraries -> 点击Add,选择第一步骤中添加的User Home下的mssql,点击ok(两次);

     

    三、选择Enterprise -> Enterprise Setup -> Database Drivers -> 点击Add,选择刚添加的User Home下的mysql,点击ok,在列表框中出现mssql.config,点击ok;

     

    四、提示重新启动JBuilder,设置才能生效,确定后,重启JBuilder;

     

    五、选择Tool -> Database Pilot ,在Database Pilot窗口中选择View -> Options -> Drivers -> 点击Add

    Driver class中输入:com.microsoft.jdbc.sqlserver.SQLServerDriver

    Sample URL中输入:microsoft:sqlserver://localhost:1433;DatabaseName=点击ok;

     

    六、在Database Pilot窗口中选择File -> New,在New URL 窗口中,

    Driver:从列表中选择第五步骤建立的com.microsoft.jdbc.sqlserver.SQLServerDriverURL:将DataBaseName改为存在的数据库,本例中的URL如下:microsoft:sqlserver://localhost:1433;DatabaseName=test,点击ok;

     

     

    七、在Database Pilot窗口中Database URLs列表中双击刚建立的url,输入连接数据库的用户名、密码,连接成功。

       经过两天的尝试,今天终于将整合了IIS6.0和Resin_3.0.8。下面我把这两天的心得写出来与大家交流一下。
        在此之前,我想从网上找些资料。不过,关于IIS与Resin整合的文章很少,而有关IIS6.0整合的文章就更少了。到Resin的官方网站上,虽然是有关IIS与Resin整合的文章。不过是英文,而且版本很低,是Resin_2.1的。最后只有一点一点的摸索。后来又看到了Resin_3.0.8里面自带的文档中有一部分是关于IIS6.0与Resin_3.0.8整合的内容。按照上面说的,终于将IIS6.0与Resin_3.0.8整合好了。Resin的官方文挡写的不是很清楚。特别有些地方很中文版的IIS6.0有些出入。
        1、安装好J2SDK和Resin.(我J2SDK是安装在D:\J2SDK下的,Resin是解压缩在D:\Resin_3.0.8文件夹下的)。
        2、环境变量。在我的电脑上单击鼠标右键→属性→高级→环境变量。
             1)新建两个系统变量(单击下面的新建):
             ①变量名:JAVA_HOME
               变量值:D:\J2SDK(J2SDK安装的目录)
             ②变量名:classpath
               变量值:.;%JAVA_HOME%\lib\tools.jar(注:前面是一个小数点加个分
                       号,直接复制就可以)
             ③变量名:RESIN_HOME
               变量值:D:\Resin_3.0.8(Resin安装的目录)
             2)编辑系统变量Path
               在变量值后面追加;%JAVA_HOME%\bin\
        3、复制文件。在windows2003下面是不能使用Rensin_3.0.8\bin\setup.exe。运行setup回报错。这就需要我们自己来复制文件。我的IIS是安装在C:\Inetpub下的。
             1)C:\Inetpub下面新建一个文件夹,重命名为scripts
             2)把Rensin_3.0.8\libexec下的isapi_srun.dll复制到
                C:\Inetpub\scripts下
             3)在C:\Inetpub\scripts新建一个resin.ini文件。
                文件内容下面两行数据:
                ResinConfigServer localhost 6802
                IISPriority high
                (官方的文档上说只有需要建立多个站点的时候才必须使用
                 resin.ini。如果只有一个站点就没必要新建resin.ini。推荐使用)
        4、设置IIS。
             1)在默认网站上单击右键→属性→ISAPI筛选器→添加
                筛选器名称:Resin_3.0.8(可随便填)
                可执行文件:C:\Inetpub\scripts\isapi_srun.dll
             2)在默认网站上单击右键→新建→虚拟目录
                别名:scripts
                本地路径:C:\Inetpub\scripts
             3)单击WEB服务扩展→添加一个新的WEB服务扩展
                扩展名:Resin(可随便添)
                要求文件:C:\Inetpub\scripts\isapi_srun.dll
                选中设置扩展状态为允许
        5、设置Resin_3.0.8\conf\resin.conf。
           在文件中找到(大概是在最后)
           <host id=''>
             <document-directory>doc</document-directory>
           改成
           <host id=''>
             <document-directory>c:/inetpub/wwwroot</document-directory>
           (c:/inetpub/wwwroot,为你的默认网站的目录)
        好,到这里,我们就完全设置完了。
        然后先运行net stop w3svc关闭IIS服务。
        再运行net start w3svc重新启动IIS。
        打开Resin_3.0.8\bin\httpd.exe。
        现在IIS6.0与Resin_3.0.8已经成功整合了。我们可以将httpd.exe作为服务启动。那么,计算机启动的时候就会启动httpd.exe。
        安装服务
        d:\resin_3.0.8\bin\httpd.exe -install
        取消服务
        d:\resin_3.0.8\bin\httpd.exe -remove

        IIS6.0与Resin_3.0.8的整合困扰了我好几天。现在写出来跟大家交流一下。有什么错误请大家指出来。

    开发一个注重性能的JDBC应用程序不是一件容易的事. 当你的代码运行很慢的时候JDBC驱动程序并不会抛出异常告诉你。

      本系列的性能提示将为改善JDBC应用程序的性能介绍一些基本的指导原则,这其中的原则已经被许多现有的JDBC应用程序编译运行并验证过。 这些指导原则包括:
     正确的使用数据库MetaData方法
       只获取需要的数据
       选用最佳性能的功能
       管理连接和更新

      以下这些一般性原则可以帮助你解决一些公共的JDBC系统的性能问题.

      使用数据库Metadata方法

      因为通过ResultSet对象生成的Metadata方法与其它的JDBCB方法相比是较慢的, 经常的使用它们将会削弱系统的的性能. 本节的指导原则将帮助你选择和使用meatdata时优化系统性能.

      少用Metadata方法

      与其它的JDBC方法相比, 由ResultSet对象生成的metadata对象的相对来说是很慢的. 应用程序应该缓存从ResultSet返回的metadata信息,避免多次不必要的执行这个操作.

      几乎没有哪一个JDBC应用程序不用到metadata,虽然如此,你仍可以通过少用它们来改善系统性能. 要返回JDBC规范规定的结果集的所有列信息, 一个简单的metadata的方法调用可能会使JDBC驱动程序去执行很复杂的查询甚至多次查询去取得这些数据. 这些细节上的SQL语言的操作是非常消耗性能的.

      应用程序应该缓存这些metadata信息. 例如, 程序调用一次getTypeInfo方法后就将这些程序所依赖的结果信息缓存. 而任何程序都不大可能用到这些结果信息中的所有内容,所以这些缓存信息应该是不难维护的.

      避免null参数

      在metadata的方法中使用null参数或search patterns是很耗时的. 另外, 额外的查询会导致潜在的网络交通的增加. 应尽可能的提供一些non-null的参数给metadata方法.

      因为metadata的方法很慢, 应用程序要尽可能有效的调用它们. 许多应用程序只传递少量的non-null参数给这些方法.

      例如:

    ResultSet WSrs = WSc.getTables (null, null, "WSTable", null);

      应该这样:

    ResultSet WSrs = WSc.getTables ("cat1", "johng", "WSTable", "TABLE");

      在第一个getTables()的调用中, 程序可能想知道表'WSTable'是否存在. 当然, JDBC驱动程序会逐个调用它们并且会解译不同的请求. JDBC驱动程序会解译请求为: 返回所有的表, 视图, 系统表, synonyms, 临时表, 或存在于任何数据库类别任何Schema中的任何别名为'WSTable'的对象.

      第二个getTables()的调用会得到更正确的程序想知道的内容. JDBC驱动程序会解译这个请求为: 返回当前数据库类别中所有存在于'johng'这个schema中的所有表.

      很显然, JDBC驱动程序处理第二个请求比处理第一个请求更有效率一些.

      有时, 你所请求信息中的对象有些信息是已知的. 当调用metadata方法时, 程序能传送到驱动程序的的任何有用信息都可以导致性能和可靠性的改善.

      使用'哑元'(dummy)查询确定表的特性

      要避免使用getColumns()去确定一个表的特性. 而应该使用一个‘哑元’查询来使用getMetadata()方法.

      请考虑这样一个程序, 程序中要允许用户选取一些列. 我们是否应该使用getColumns()去返回列信息给用户还是以一个'哑元'查询来调用getMetadata()方法呢?

      案例 1: GetColumns 方法

    ResultSet WSrc = WSc.getColumns (... "UnknownTable" ...);
    // getColumns()会发出一个查询给数据库系统
    . . .
    WSrc.next();
    string Cname = getString(4);
    . . .
    // 用户必须从反复从服务器获取N行数据
    // N = UnknownTable的列数
      案例 2: GetMetadata 方法

    // 准备'哑元'查询
    PreparedStatement WSps = WSc.prepareStatement
    ("SELECT * from UnknownTable WHERE 1 = 0");
    // 查询从来没有被执行,只是被预储
    ResultSetMetaData WSsmd=WSps.getMetaData();
    int numcols = WSrsmd.getColumnCount();
    ...
    int ctype = WSrsmd.getColumnType(n)
    ...
    // 获得了列的完整信息
      在这两个案例中, 一个查询被传送到服务器. 但在案例1中, 查询必须被预储和执行, 结果的描述信息必须确定(以传给getColumns()方法), 并且客户端必须接收一个包含列信息的结果集. 在案例2中, 只要准备一个简单的查询并且只用确定结果描述信息. 很显然, 案例2执行方式更好一些.

      这个讨论有点复杂, 让我们考虑一个没有本地化支持prepared statement的DBMS服务器. 案例1的性能没有改变, 但案例2中, 因为'哑元'查询必须被执行而不是被预储使得它的性能增强了一些. 因为查询中的WHERE子句总是为FALSE, 查询在不用存取表的数据情况的下会生成没有数据的结果集. 在这种情况下,第二种方式当然比第一种方式好一些.

      总而言之,总是使用ResultSet的metadata方法去获取列信息,像列名,列的数据类型,列的数据精度和长度等. 当要求的信息无法从ResultSet的metadata中获取时才去用getColumns()方法(像列的缺省值这些信息等)。
    设为首页 - 加入收藏 - 网站地图 - - 站长QQ:271457770
    PHP教程 - PHP精品资源站 | 我们为PHP初学者提供动力!