j2ee核心模式筆記二——迭代標籤的使用及測試

迭代標籤的使用及測試
 
(本文適合具有一定jsp/servlet開發基礎的人閱讀)
 
  初級的JSP開發人員往往在JSP中混雜諸多的邏輯內容,比如我們爲了顯示數據庫的某個查詢結果,經常會在頁面中出現下列代碼,而這些代碼可能在許多地方重複出現
    <%
    while (rs.next()){
    {
        out.println("<td>"+rs.getName()+"</td>");
        out.println("<td>"+rs.getAuthor()+"</td>");
        .......
    }
    %>
 
這些在j2ee核心模式一書中可以被視作表現層的不佳實踐,是因爲多個視圖都包含了同樣的控制代碼,而且看來極不雅觀。比較好的一種方法就是使用標籤對其進行重構,雖然你可以使用JSTL的標準標籤庫,但是爲了揭示一些更本質的東西,我們這裏採用了自定義標籤來完成上述重構,在使用自定義標籤之前,我們先構建完成上述實例的相關環境配置。
 
(1)oracle數據庫
 
我們用在數據庫裏先建一個BOOKS的實例,然後啓動該實例後用SYSTEM登錄(可以使用sql plus),執行下列腳本:
 
----book.sql begin------
 
--以sys權限暫時連接,可以執行以下的GRANT權限
connect
sys/books@books as sysdba;
--刪除用戶BOOKS
drop user BOOKS cascade;
--創建用戶BOOKS
CREATE USER "BOOKS" IDENTIFIED BY "BOOKS" PROFILE DEFAULT default tablespace users temporary tablespace temp ACCOUNT UNLOCK;
--賦予用戶BOOKS系統中的默認角色CONNECT和RESOURCE
GRANT "CONNECT" TO "BOOKS";
GRANT "RESOURCE" TO "BOOKS";
--讓BOOKS可以無限制地使用表空間,表空間的數據文件大小以及是否自動增長可以在企業版管理器中設置
GRANT UNLIMITED TABLESPACE TO "BOOKS";
--賦予用戶創建表、執行表、修改表(包括其它模式)的權限
GRANT CREATE TABLE TO "BOOKS";
GRANT EXECUTE ANY PROCEDURE TO "BOOKS";
GRANT UPDATE ANY TABLE TO "BOOKS";
GRANT CREATE ANY INDEX TO "BOOKS";
--賦予用戶可以運行SYS下四個包中相應系統函數的功能
GRANT EXECUTE ON "SYS"."UTL_FILE" TO "BOOKS";
GRANT EXECUTE ON "SYS"."DBMS_SQL" TO "BOOKS";
GRANT EXECUTE ON "SYS"."DBMS_JOB" TO "BOOKS";
GRANT EXECUTE ON "SYS"."DBMS_OUTPUT" TO "BOOKS";
GRANT EXECUTE ON "SYS"."DBMS_LOCK" TO "BOOKS";
--賦予用戶的默認角色爲所有角色(用戶所具有的角色不一定完全開啓,可以禁用,此處即開啓所有角色)
ALTER USER "BOOKS" DEFAULT ROLE ALL;
--賦予BOOKS執行ALTER USER命令
GRANT ALTER USER TO "BOOKS";
COMMIT;
 
--創建序列爲遞增列做準備
create sequence BOOKS_BOOKID_SEQ
minvalue 1
maxvalue 999999999999999999999999999
start with 21
increment by 1
cache 20;
 
--創建觸發器以實現數據遞增
create or replace trigger books_i
  before insert on books
  for each row
declare
  -- local variables here
  next_book_id number;
begin
  SELECT BOOKS_BOOKID_SEQ.nextval into next_book_id FROM dual;
  :new.bookid :=next_book_id;
end books_i;
 
--創建表結構
 
create table BOOKS
(
  NAME      VARCHAR2(20),
  PRICE     VARCHAR2(20),
  AUTHOR    VARCHAR2(20),
  PUBLISHER VARCHAR2(20),
  BOOKID    NUMBER not null
)
tablespace USERS
  pctfree 10
  initrans 1
  maxtrans 255
  storage
  (
    initial 64
    minextents 1
    maxextents unlimited
  );
-- Create/Recreate primary, unique and foreign key constraints
alter table BOOKS
  add constraint BOOKID primary key (BOOKID)
  using index
  tablespace USERS
  pctfree 10
  initrans 2
  maxtrans 255
  storage
  (
    initial 64K
    minextents 1
    maxextents unlimited
  );
 
--插入數據
insert into books (NAME, PRICE, AUTHOR, PUBLISHER, BOOKID)
values ('j2ee核心模式', '45', 'yfhuang', '機械', 2);
 
insert into books (NAME, PRICE, AUTHOR, PUBLISHER, BOOKID)
values ('java程序設計', '45', 'fying', '機械', 3);
 
insert into books (NAME, PRICE, AUTHOR, PUBLISHER, BOOKID)
values ('agilejava', '30', 'jeff', '電子', 4);
 
COMMIT;
EXIT;
 
----books.sql end-------
 
到此,我們有了一個BOOKS數據庫,裏面有張books表
 
(2)Tomcat (此處爲Tomcat 5.0.28爲例,修改系統的server.xml文件)
 
添加JNDI全局資源,建立數據庫連接池
 
<Resource name="jdbc/books" auth="Container" type="javax.sql.DataSource"/>
 <ResourceParams name="jdbc/books">
 <parameter>
  <name>factory</name>
  <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
 </parameter>
 <parameter>
  <name>username</name>
  <value>books</value>
 </parameter>
 <parameter>
  <name>password</name>
  <value>books</value>
 </parameter>
 <parameter>
  <name>driverClassName</name>
  <value>oracle.jdbc.driver.OracleDriver</value>
 </parameter>
 <parameter>
  <name>url</name>
  <value>jdbc:oracle:thin:@127.0.0.1:1521:books</value>
 </parameter>
 <parameter>
 <name>maxActive</name>
  <value>20</value>
 </parameter>
 <parameter>
  <name>maxIdle</name>
  <value>10</value>
 </parameter>
 <parameter>
  <name>maxWait</name>
  <value>10000</value>
 </parameter>
 </ResourceParams>
 
在j2ee web應用程序中添加到資源的引用,在server.xml中添加
 
<Context path="/j2ee" docBase="j2ee"
debug="0" privileged="true" reloadable="true" >
  <ResourceLink name="jdbc/books" global="jdbc/books"
                type="javax.sql.DataSource"/>
</Context>
 
通過以上兩個步驟,你就可以在j2ee這個web應用程序中使用books數據庫了
 
(3) 建立用來演示的jsp頁面bookdetail.jsp頁面
 
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="
http://yfhuang.chap1lib" prefix="tag"%>
<html>
 <body bgcolor="white">
  <tag:generatebean jndisource="jdbc/books" beanname="booklist">
   select * from books
  </tag:generatebean>
  <table>
   <tr>
    <td>
     <b>
      自定義標籤:使用TagSupport標籤(靠的是IterationTag接口機制)進行迭代的示例,顯示oracle數據庫中的books表信息
     </b>
    </td>
   </tr>
  </table>
  <table border=2 cellspacing=3 cellpadding=3>
   
   <tag:recordheader/>
   <tag:recordsite bean="${booklist}">
    <tr>
     <tag:recordcontent />
    </tr>
   </tag:recordsite>
  </table>
 </body>
</html>
 
在該頁面中,使用了(4)中用到的標籤庫,該標籤庫分成兩部分,generatebean標籤主要用來生成booklist,而recordsite主要用來展示booklist。這些自定義標籤定義如(4).
 
(4) 建立自定義標籤chap1lib.tld
 
<?xml version="1.0" encoding="UTF-8"?>
<taglib>
 <tlib-version>1.0</tlib-version>
 <jsp-version>1.2</jsp-version>
 <short-name>chap1lib</short-name>
 <uri>http://yfhuang.chap1lib</uri>
<!-- 自定義標籤:一個使用自定義標籤查詢oracle數據庫的綜合示例-->
 <tag>
  <name>generatebean</name>
  <tag-class>chapter1.GenerateBean</tag-class>
  <body-content>jsp</body-content>
  <attribute>
   <name>jndisource</name>
   <required>true</required>
   <rtexprvalue>true</rtexprvalue>
  </attribute>
  <attribute>
   <name>beanname</name>
   <required>true</required>
   <rtexprvalue>true</rtexprvalue>
  </attribute>
 </tag>
 <tag>
  <name>recordsite</name>
  <tag-class>chapter1.RecordsIte</tag-class>
  <body-content>jsp</body-content>
  <attribute>
   <name>bean</name>
   <required>true</required>
   <rtexprvalue>true</rtexprvalue>
  </attribute>
 </tag>
 <tag>
  <name>recordheader</name>
  <tag-class>chapter1.RecordHeader</tag-class>
  <body-content>empty</body-content>
 </tag>
 <tag>
  <name>recordcontent</name>
  <tag-class>chapter1.RecordContent</tag-class>
  <body-content>empty</body-content>
 </tag>
</taglib>
 
(5) 標籤處理核心類
 
----GenerateBean.java------
-----GenerateBean.java主要負責數據庫連接參數的裝配-------
 
package chapter1;
 
import java.util.ArrayList;
import java.util.Collection;
 
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.BodyTagSupport;
 
public class GenerateBean extends BodyTagSupport implements DataEngine,DataBaseBean {
 private String jndisource;
 
 private String beanname;
 
 private String sql;
 
 private DataEngine dg;
 
 private Collection recordcollect;
 
 
 public GenerateBean(){
  dg = this;
  recordcollect=new ArrayList();
 }
 public int doStartTag() throws JspTagException {
  return EVAL_BODY_BUFFERED;
 }
 
 public int doEndTag() throws JspTagException {
  System.out.println("set dataengine");
  pageContext.setAttribute(beanname, this);
  String body = this.getBodyContent().getString();
  this.sql = body;
  return SKIP_BODY;
 }
 
 public String getJndisource() {
  return jndisource;
 }
 
 public void setJndisource(String jndisource) {
  System.out.println("set jndisource:"+jndisource);
  this.jndisource = jndisource;
 }
 
 public String getBeanname() {
  return beanname;
 }
 
 public void setBeanname(String beanname) {
  this.beanname = beanname;
 }
 
 public DataEngine getDg() {
  return dg;
 }
 
 public String getSql() {
  return sql;
 }
 
 public Collection getRecordcollect() {
  return recordcollect;
 }
 
 public void setSql(String sql) {
  this.sql = sql;
 }
 
}
 
------DataBaseBean.java和DataEngine.java是GenerateBean.java的兩個輔助接口
----DataBaseBean.java-----
 
package chapter1;
 
import java.util.Collection;
 
public interface DataBaseBean {
 public DataEngine getDg();
 public String getSql();
 public Collection getRecordcollect();
}
 
----DataEngine.java----
 
package chapter1;
 
public interface DataEngine {
 public String getJndisource();
}
 
-----RecordsIte.java-------
-----RecordsIte主要根據Genarate的裝配參數查詢數據庫,並通過標籤生命週期方法doStartTag方
-----法doAfterBody方法對標籤本體進行迭代,標籤本體是RecordContent,顯示單本書信息
 
package chapter1;
 
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
import javax.servlet.jsp.tagext.TagSupport;
import javax.sql.DataSource;
 
public class RecordsIte extends TagSupport {
 
 private DataBaseBean bean;
 private Iterator iterator;
 private BookBean onebook;
 
 public void QueryDataBase() {
  try {
   System.out.println("Query DataBase");
   Context initCtx = new InitialContext();
   Context envCtx = (Context) initCtx.lookup("java:comp/env");
   DataSource ds = null;
   ds = (DataSource) envCtx.lookup(bean.getDg().getJndisource());
   if (ds != null) {
    Connection cn = ds.getConnection();
    if (cn != null) {
     Statement stmt = cn.createStatement();
     ResultSet rst = stmt.executeQuery(bean.getSql());
     bean.getRecordcollect().clear();
     while (rst.next()){
      BookBean abook=new BookBean();
      abook.setName(rst.getString("name"));
      abook.setAuthor(rst.getString("author"));
      abook.setPrice(rst.getString("price"));
      abook.setPublisher(rst.getString("publisher"));
      bean.getRecordcollect().add(abook);
     }
     rst.close();
     cn.close();
    }
   }else{
    System.out.println("不能找到jndi 數據源");
   }
  } catch (NamingException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (SQLException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }
 
 public int doStartTag() throws JspTagException {
  QueryDataBase();
  iterator=bean.getRecordcollect().iterator();
  if (iterator.hasNext()) {
   System.out.println("RecordsIte dostarttag iterator");
   this.onebook = (BookBean) iterator.next();
   System.out.println(onebook.getAuthor());
   return EVAL_BODY_INCLUDE;
  } else {
   return SKIP_BODY;
  }
 }
 
 public int doAfterBody() throws JspException {
  System.out.println("do after body begin");
  if (iterator.hasNext()) {
   System.out.println("do after body iterator");
   this.onebook=(BookBean)iterator.next();
   return EVAL_BODY_AGAIN;
  } else {
   return SKIP_BODY;
  }
 }
 
 public DataBaseBean getBean() {
  return bean;
 }
 
 public void setBean(DataBaseBean bean) {
  this.bean = bean;
 }
 
 public BookBean getOnebook() {
  return onebook;
 }
 
}
 
------RecordContent.java------
package chapter1;
 
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.SimpleTagSupport;
 
public class RecordContent extends SimpleTagSupport{
 
 private BookBean onebook;
 
 public void doTag() throws JspException, IOException {
  System.out.println("RecordsContent dotag begin");
  JspWriter out = getJspContext().getOut();
  RecordsIte ancestorTag=(RecordsIte)this.getParent();
  if (ancestorTag == null) {
   System.out.println("沒有父類");
  } else {
   //System.out.println(ancestorTag.test);
   BookBean onebook=ancestorTag.getOnebook();
   out.println(format(onebook.getName()));
   out.println(format(onebook.getPrice()));
   out.println(format(onebook.getAuthor()));
   out.println(format(onebook.getPublisher()));
  }
 }
 
 public static String format(String m) {
  return "<td>" + m + "</td>";
 }
 
 public static String format(int m) {
  return "<td>" + m + "</td>";
 }
}
 
(6) 標籤處理的測試
 
在最初寫這個頁面迭代程序的時候,我在標籤處理核心類RecordsIte的數據庫查詢部分是這樣寫的,
 
public void QueryDataBase() {
  try {
   System.out.println("Query DataBase");
   Context initCtx = new InitialContext();
   Context envCtx = (Context) initCtx.lookup("java:comp/env");
   DataSource ds = null;
   ds = (DataSource) envCtx.lookup(bean.getDg().getJndisource());
   if (ds != null) {
    Connection cn = ds.getConnection();
    if (cn != null) {
     Statement stmt = cn.createStatement();
     ResultSet rst = stmt.executeQuery(bean.getSql());
     bean.getRecordcollect().clear();
     BookBean abook=new BookBean();
     while (rst.next()){
      abook.setName(rst.getString("name"));
      abook.setAuthor(rst.getString("author"));
      abook.setPrice(rst.getString("price"));
      abook.setPublisher(rst.getString("publisher"));
      bean.getRecordcollect().add(abook);
     }
     rst.close();
     cn.close();
    }
   }else{
    System.out.println("不能找到jndi 數據源");
   }
  } catch (NamingException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (SQLException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }
 
abook的生成放到了循環外,錯誤在哪裏?昏了頭了,找了半天還是沒發現,不要緊的,逼我寫了個測試,使用的是cactus的框架,測試如下:
 
//TestRecordIte.java
 
package test;
 
import java.util.Iterator;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
import org.apache.cactus.JspTestCase;
import org.apache.cactus.WebResponse;
import chapter1.BookBean;
import chapter1.GenerateBean;
import chapter1.RecordsIte;
 
public class TestRecordIte extends JspTestCase {
 
 private RecordsIte tag;
 
 
 public void setUp() { 
  this.tag = new RecordsIte();
  GenerateBean genb = new GenerateBean();
  this.tag.setPageContext(this.pageContext);
  genb.setBeanname("booklist");
  genb.setJndisource("jdbc/books");
  genb.setSql("select * from books");
  this.tag.setBean(genb);
 }
 
 
 public void testQueryDatabase() throws Exception{
  
  this.tag.QueryDataBase();
  int number = 1;
  Iterator ite = this.tag.getBean().getRecordcollect().iterator();
  while (ite.hasNext()) {
   //System.out.print("the " + number + "is:");
   BookBean onebook = (BookBean) ite.next();
   System.out.println(onebook.getName());
   number++;
  }
 }
  
 public void testDoStartTag() throws Exception {
  int result = this.tag.doStartTag();
  assertEquals(TagSupport.EVAL_BODY_INCLUDE, result);
 }
 
 public void testDoAfterBody() throws JspException {
  int result = this.tag.doStartTag();
  assertEquals(TagSupport.EVAL_BODY_INCLUDE, result);

  boolean firsttime = true;
  int number=1;
  if (firsttime || result == TagSupport.EVAL_BODY_AGAIN) {
   result = this.tag.doAfterBody();
   firsttime = false;
   number++;
  }
  assertEquals(true,number==2);
 }
 }
 
該測試爲taglib測試,採用cactus框架,可以測試taglib的行爲,要使用該測試,必須導入cactus的相關lib,具體可見
 
要拷貝cactus中的jspRedirector.jsp(可在cactus下載包中找)至webapps目錄下
並在j2ee web應用程序的webapps目錄下添加這樣的配置
 
<servlet>
  <servlet-name>JspRedirector</servlet-name>
  <jsp-file>/chap1/jspRedirector.jsp</jsp-file>
  </servlet>
 <servlet-mapping>
  <servlet-name>JspRedirector</servlet-name>
  <url-pattern>/JspRedirector</url-pattern>
 </servlet-mapping>
 
 
把該測試當作junit運行(測試有些在客戶端運行,有些在服務器運行,具體參見cactus
對於客戶端的Junit測試部分,需要一個配置文件cactus.properties(放在classpath中)
 
cactus.contextURL = http://127.0.0.1/j2ee
cactus.servletRedirectorName = JspRedirector
cactus.disableLogging=enable
 
測試雖然通過了(這邊只是偷懶的測試,沒有對記錄的內容判斷,只是打印出來),testQueryDatabase方法在Tomcat的控制檯上把最後一條數據庫記錄(agilejava 30 jeff....)打印了三遍。
 
原來生成集合時就不對,肯定在bean.getRecordcollect().add(abook)發生了問題,
恍然大悟,終於發現BookBean abook=new BookBean()在while外面,每循環一次,abook還是那個abook,不過
內容變了,雖然裏面加了三次abook,內容卻是同一個abook,它對應於數據庫的最後1條記錄。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章