Struts+Hibernate+Javascript 实现无限级树形菜单
一、说明:
1、开发环境:
Eclipse3.2.1+MyEclipse5.1+Tomcat5.5+Microsoft SQL Server 2000
2、主要实现技术:Struts1.2+Hibernate3.0+JavaScript+JSTL1.1+自定义标签
3、页面的树形菜单的节点用 JavaScript进行控制
4、数据库中的商品类别表productCategory包含一个引用自身主键的外键,从而形成自身一对多关系
5、自定义标签实现类Recursion中主要用了递归实现节点的展开
补充:测试数据通过TestMain.java插入数据库的(连接数据库用的驱动程序是jtds-1.2.jar,自己加载),这个类用的JUnit单元测试,所以也要把JUnit的包加入构建路径中:-) 完整代码打包下载在后面的链接中
二、完成后运行效果如下图:
三、具体实现步骤:
1、新建一个Web 工程(随便加载JSTL1.1的支持),创建如下图所示的结构目录
2、创建数据库
数据库中就只有两张表,其中商品类别à商品为一对多关系,商品类别自身形成一对多关系(引用主键:类别编号做外键:父类编号),下面是由PowerDesigner 12 画的概念图
生成的数据库脚本tree.sql如下:
/*==============================================================*/
/* DBMS name: Microsoft SQL Server 2000 */
/* Created on: 2007-07-14 09:10:40 */
/*==============================================================*/
/*==============================================================*/
/* Table: product */
/*==============================================================*/
create database tree
go
use tree
go
create table product (
productId int identity not null,
CategoryId int not null,
productName varchar(100) not null,
productPrice money not null,
constraint PK_PRODUCT primary key nonclustered (productId)
)
go
/*==============================================================*/
/* Index: ProductCategoryToProduct_FK */
/*==============================================================*/
create index ProductCategoryToProduct_FK on product (
CategoryId ASC
)
go
/*==============================================================*/
/* Table: productCategory */
/*==============================================================*/
create table productCategory (
CategoryId int identity not null,
ParentCategoryId int null,
CategoryName varchar(50) not null,
constraint PK_PRODUCTCATEGORY primary key nonclustered (CategoryId)
)
go
/*==============================================================*/
/* Index: 商品类别拥有商品类别_FK */
/*==============================================================*/
create index 商品类别拥有商品类别_FK on productCategory (
ParentCategoryId ASC
)
go
alter table product
add constraint FK_PRODUCT_PRODUCTCA_PRODUCTC foreign key (CategoryId)
references productCategory (CategoryId)
go
alter table productCategory
add constraint FK_PRODUCTC_商品类别拥有商品类_PRODUCTC foreign key (ParentCategoryId)
references productCategory (CategoryId)
用查询分析器运行上述脚本,在数据库中建
表product结构如下图:
表
productCategory结构如下图:
3、数据库建好后,通过MyEclipse加载Struts 1.2、Hibernate 3.0的支持
4、切换到MyEclipse Database 视图,通过Hibernate Reverse Engineering生成两张表的Pojo类和*.hbm.xml映射文件
5、对自动生成的Pojo类和*.hbm.xml映射文件进行适当的修改,修改后的文件如下:
1)Product.java
package
com.lideedu.yame.tree.pojos;



public
class
Product
implements
java.io.Serializable
...
{

private Integer productId;

private ProductCategory productCategory;

private String productName;

private Double productPrice;



public Product() ...{

}


public Integer getProductId() ...{

return this.productId;

}


public void setProductId(Integer productId) ...{

this.productId = productId;

}


public ProductCategory getProductCategory() ...{

return this.productCategory;

}


public void setProductCategory(ProductCategory productCategory) ...{

this.productCategory = productCategory;

}


public String getProductName() ...{

return this.productName;

}


public void setProductName(String productName) ...{

this.productName = productName;

}


public Double getProductPrice() ...{

return this.productPrice;

}


public void setProductPrice(Double productPrice) ...{

this.productPrice = productPrice;

}

}

2)Product.hbm.xml
<?
xml version="1.0" encoding="utf-8"
?>

<!
DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
>

<!--

Mapping file autogenerated by MyEclipse - Hibernate Tools

-->

<
hibernate-mapping
>

<
class
name
="com.lideedu.yame.tree.pojos.Product"
table
="product"
>

<
id
name
="productId"
type
="java.lang.Integer"
>

<
column
name
="productId"
/>

<
generator
class
="native"
/>

</
id
>

<
many-to-one
name
="productCategory"
outer-join
="true"

class
="com.lideedu.yame.tree.pojos.ProductCategory"
>

<
column
name
="CategoryId"
not-null
="true"
/>

</
many-to-one
>

<
property
name
="productName"
type
="java.lang.String"
>

<
column
name
="productName"
length
="100"
not-null
="true"
/>

</
property
>

<
property
name
="productPrice"
type
="java.lang.Double"
>

<
column
name
="productPrice"
scale
="4"
not-null
="true"
/>

</
property
>

</
class
>

</
hibernate-mapping
>

3)ProductCategory.java
package
com.lideedu.yame.tree.pojos;


import
java.util.HashSet;

import
java.util.Set;


@SuppressWarnings(
"
serial
"
)


public
class
ProductCategory
implements
java.io.Serializable
...
{

private Integer categoryId;

private String categoryName;

private ProductCategory parentCategory;

private Set subCategories = new HashSet(0);

private Set products = new HashSet(0);


public Set getProducts() ...{

return products;

}


public void setProducts(Set products) ...{

this.products = products;

}


public ProductCategory() ...{

}


public Integer getCategoryId() ...{

return this.categoryId;

}


public void setCategoryId(Integer categoryId) ...{

this.categoryId = categoryId;

}


public String getCategoryName() ...{

return this.categoryName;

}


public void setCategoryName(String categoryName) ...{

this.categoryName = categoryName;

}


public ProductCategory getParentCategory() ...{

return parentCategory;

}


public void setParentCategory(ProductCategory parentCategory) ...{

this.parentCategory = parentCategory;

}


public Set getSubCategories() ...{

return subCategories;

}


public void setSubCategories(Set subCategories) ...{

this.subCategories = subCategories;

}

}

4)ProductCategory.hbm.xml
<?
xml version="1.0" encoding="utf-8"
?>

<!
DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"
>

<!--

Mapping file autogenerated by MyEclipse - Hibernate Tools

-->

<
hibernate-mapping
>

<
class
name
="com.lideedu.yame.tree.pojos.ProductCategory"
table
="productCategory"
>

<
id
name
="categoryId"
type
="java.lang.Integer"
>

<
column
name
="CategoryId"
/>

<
generator
class
="native"
/>

</
id
>

<
property
name
="categoryName"
type
="java.lang.String"
>

<
column
name
="CategoryName"
length
="50"
not-null
="true"
/>

</
property
>

<
set
name
="products"
inverse
="true"
cascade
="all"
lazy
="false"
>

<
key
>

<
column
name
="CategoryId"
not-null
="true"
/>

</
key
>

<
one-to-many
class
="com.lideedu.yame.tree.pojos.Product"
/>

</
set
>

<
many-to-one
name
="parentCategory"
class
="com.lideedu.yame.tree.pojos.ProductCategory"

cascade
="save-update"
>

<
column
name
="ParentCategoryId"
/>

</
many-to-one
>

<
set
name
="subCategories"
inverse
="true"
lazy
="false"
>

<
key
>

<
column
name
="ParentCategoryId"
/>

</
key
>

<
one-to-many
class
="com.lideedu.yame.tree.pojos.ProductCategory"
/>

</
set
>

</
class
>

</
hibernate-mapping
>

6、编写DAO
1) ProductDAO
package
com.lideedu.yame.tree.dao;


import
java.util.List;

import
org.hibernate.Session;

import
org.hibernate.Transaction;

import
com.lideedu.yame.tree.db.HibernateSessionFactory;

import
com.lideedu.yame.tree.pojos.Product;



public
class
ProductDAO
...
{

private Session session= null;

private Transaction tx = null;


public void save(Product product)...{


try ...{

session = HibernateSessionFactory.getSession();

tx = session.beginTransaction();

session.save(product);

tx.commit();


} catch (Exception e) ...{

e.printStackTrace();

if(tx != null)

tx.rollback();


}finally...{

HibernateSessionFactory.closeSession();

tx = null;

}

}


public List queryAll()...{

List list = null;


try ...{

session = HibernateSessionFactory.getSession();

list = session.createQuery("from Product p left join fetch p.productCategory").list();


} catch (Exception e) ...{

e.printStackTrace();


}finally...{

HibernateSessionFactory.closeSession();

}

return list;

}


public List queryByProductId(int productId)...{

List list = null;


try ...{

session = HibernateSessionFactory.getSession();

list = session.createQuery("from Product p left join fetch p.productCategory where p.productId="+productId).list();


} catch (Exception e) ...{

e.printStackTrace();


}finally...{

HibernateSessionFactory.closeSession();

}

return list;

}


public List queryByCategoryId(int categoryId)...{

List list = null;


try ...{

session = HibernateSessionFactory.getSession();

list = session.createQuery("from Product p left join fetch p.productCategory where p.productCategory.categoryId="+categoryId).list();


} catch (Exception e) ...{

e.printStackTrace();


}finally...{

HibernateSessionFactory.closeSession();

}

return list;

}

}

2) ProductCategoryDAO
package
com.lideedu.yame.tree.dao;


import
java.util.List;

import
org.hibernate.Session;

import
org.hibernate.Transaction;

import
com.lideedu.yame.tree.db.HibernateSessionFactory;

import
com.lideedu.yame.tree.pojos.ProductCategory;



public
class
ProductCategoryDAO
...
{

private Session session= null;

private Transaction tx = null;


public void save(ProductCategory pc)...{


try ...{

session = HibernateSessionFactory.getSession();

tx = session.beginTransaction();

session.save(pc);

tx.commit();


} catch (Exception e) ...{

e.printStackTrace();

if(tx != null)

tx.rollback();


}finally...{

HibernateSessionFactory.closeSession();

tx = null;

}

}


public List queryAll()...{

List list = null;


try ...{

session = HibernateSessionFactory.getSession();

list = session.createQuery("from ProductCategory p left join fetch p.parentCategory").list();


} catch (Exception e) ...{

e.printStackTrace();


}finally...{

HibernateSessionFactory.closeSession();

}

return list;

}

}

7、编写Struts 控制类
1)ListCategoryAction.java
这个类的作用是从数据库中查出根类,存放到List中,然后把List保存到request范围,在前台通过自定义标签取出后,在自定义标签的实现类RecursionTag中通过递归做成一棵目录树,在页面显示出来,页面的节点的展开和折叠是通过JavaScript控制的。
package
com.lideedu.yame.tree.struts.action;


import
java.util.ArrayList;

import
java.util.Iterator;

import
java.util.List;

import
javax.servlet.http.HttpServletRequest;

import
javax.servlet.http.HttpServletResponse;

import
org.apache.struts.action.Action;

import
org.apache.struts.action.ActionForm;

import
org.apache.struts.action.ActionForward;

import
org.apache.struts.action.ActionMapping;

import
com.lideedu.yame.tree.dao.ProductCategoryDAO;

import
com.lideedu.yame.tree.pojos.ProductCategory;



public
class
ListCategoryAction
extends
Action
...
{

@SuppressWarnings("unchecked")

public ActionForward execute(ActionMapping mapping, ActionForm form,


HttpServletRequest request, HttpServletResponse response) ...{

ProductCategoryDAO pcDAO = new ProductCategoryDAO();

List list = new ArrayList();

Iterator it = pcDAO.queryAll().iterator();


for (Iterator iter = it; iter.hasNext();) ...{

ProductCategory element = (ProductCategory) iter.next();

if(element.getParentCategory() == null)

list.add(element);

}

request.setAttribute("all", list);

return mapping.findForward("list");

}

}
2)QueryProductAction.java
此类用于显示具体商品的名称时调用,当页面目录树展开到没有子类的节点时,就调用此类从数据库中查询出该类别下所有商品的信息
package
com.lideedu.yame.tree.struts.action;


import
java.util.List;

import
javax.servlet.http.HttpServletRequest;

import
javax.servlet.http.HttpServletResponse;

import
org.apache.struts.action.Action;

import
org.apache.struts.action.ActionForm;

import
org.apache.struts.action.ActionForward;

import
org.apache.struts.action.ActionMapping;

import
com.lideedu.yame.tree.dao.ProductDAO;


public
class
QueryProductAction
extends
Action
...
{

private ProductDAO productDAO = new ProductDAO();

public ActionForward execute(ActionMapping mapping, ActionForm form,


HttpServletRequest request, HttpServletResponse response) ...{

int productId = new Integer(request.getParameter("productId"));

List list = productDAO.queryByProductId(productId);

request.setAttribute("products", list);

return mapping.findForward("listProducts");

}

}
8、Struts配置文件àstruts-config.xml
<?
xml version="1.0" encoding="UTF-8"
?>

<!
DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"
>


<
struts-config
>

<
data-sources
/>

<
form-beans
/>

<
global-exceptions
/>

<
global-forwards
/>

<
action-mappings
>

<
action
path
="/listCategory"
type
="com.lideedu.yame.tree.struts.action.ListCategoryAction"
>

<
forward
name
="list"
path
="/ListCategory.jsp"
/>

</
action
>

<
action
path
="/queryProduct"
type
="com.lideedu.yame.tree.struts.action.QueryProductAction"
>

<
forward
name
="listProducts"
path
="/ListProducts.jsp"
/>

</
action
>


</
action-mappings
>


<
message-resources
parameter
="com.lideedu.yame.tree.struts.ApplicationResources"
/>

</
struts-config
>

9、几个JSP页面文件
1) main.jsp 此页面非常简单,只是提供个调用ListCategoryAction的入口
<%
...
@ page language="java" import="java.util.*" pageEncoding="gbk"
%>


<!
DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
>

<
html
>

<
head
>

<
title
>
Main
</
title
>


<
meta
http-equiv
="pragma"
content
="no-cache"
>

<
meta
http-equiv
="cache-control"
content
="no-cache"
>

<
meta
http-equiv
="expires"
content
="0"
>

<
meta
http-equiv
="keywords"
content
="keyword1,keyword2,keyword3"
>

<
meta
http-equiv
="description"
content
="This is my page"
>

<!--

<link rel="stylesheet" type="text/css" href="styles.css">

-->


</
head
>


<
body
>

<
h3
><
a
href
="listCategory.do"
>
显示所有商品类别目录
</
a
></
h3
>

</
body
>

</
html
>
2) ListProducts.jsp
此页用来显示某个具体商品的详细信息
<%
...
@ page language="java" import="java.util.*" pageEncoding="GBK"
%>


<%
...
@ taglib uri="http://jakarta.apache.org/struts/tags-bean"

prefix="bean"
%>


<%
...
@ taglib uri="http://jakarta.apache.org/struts/tags-html"

prefix="html"
%>


<%
...
@ taglib uri="http://jakarta.apache.org/struts/tags-logic"

prefix="logic"
%>


<%
...
@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"
%>


<!
DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
>

<
html
>

<
head
>

<
title
>
My JSP 'ListProducts.jsp' starting page
</
title
>


<
meta
http-equiv
="pragma"
content
="no-cache"
>

<
meta
http-equiv
="cache-control"
content
="no-cache"
>

<
meta
http-equiv
="expires"
content
="0"
>

<
meta
http-equiv
="keywords"
content
="keyword1,keyword2,keyword3"
>

<
meta
http-equiv
="description"
content
="This is my page"
>

<!--

<link rel="stylesheet" type="text/css" href="styles.css">

-->


</
head
>


<
body
>

<
logic:iterate
id
="product"
name
="products"
scope
="request"
>

<
center
><
h3
>
商品详细信息
</
h3
></
center
>

商品所属类别:${product.productCategory.categoryName}
<
br
/>

商品编号:${product.productId}
<
br
/>

商品名称:${product.productName}
<
br
/>

价格:${product.productPrice}元
<
br
/>

</
logic:iterate
>

</
body
>

</
html
>
*** 3) ListCategory.jsp ***
核心页面 显示商品类别目录树和具体商品信息,
<%
...
@ page language="java" import="java.util.*" pageEncoding="gbk"
%>


<%
...
@ taglib uri="http://jakarta.apache.org/struts/tags-bean"

prefix="bean"
%>


<%
...
@ taglib uri="http://jakarta.apache.org/struts/tags-html"

prefix="html"
%>


<%
...
@ taglib uri="http://jakarta.apache.org/struts/tags-logic"

prefix="logic"
%>


<%
...
@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"
%>


<%
...
@ taglib uri="/WEB-INF/recursion.tld" prefix="r"
%>

<!
DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
>

<
html
>

<
head
>

<
title
>
无限级树型菜单
</
title
>


<
meta
http-equiv
="pragma"
content
="no-cache"
>

<
meta
http-equiv
="cache-control"
content
="no-cache"
>

<
meta
http-equiv
="expires"
content
="0"
>

<
script
type
="text/javascript"
src
="js/myjs.js"
></
script
>

<
link
href
="css/mycss.css"
rel
="stylesheet"
type
="text/css"
/>

</
head
>


<
body
background
="images/bg.jpg"
>

<
table
width
="100%"
height
="100%"
border
="0"
cellSpacing
="0"

cellPadding
="0"
>

<
tr
>

<
td
width
="45%"
height
="100%"
valign
="top"
nowrap
="nowrap"
>

<
ul
id
="containerul"
>

<
c:choose
>

<
c:when
test
="${not empty all}"
>

<
li
>

所有商品

<
ul
>

<!--
自定义标签
-->

<
r:yame
list
="${all}"
/>

</
ul
>

</
li
>

</
c:when
>

<
c:otherwise
>

<
center
>

<
h1
>

<
font
color
="red"
>
没有任何商品!!!
</
font
>

</
h1
>

</
center
>

</
c:otherwise
>

</
c:choose
>

</
ul
>

<
p
>


<
script
type
="text/javascript"
>
...
initiate();
</
script
>

</
p
>

</
td
>

<
td
valign
="top"
>

<
iframe
id
=mainFrame
name
=mainFrame
src
="about:blank"
frameborder
=0

width
="92%"
height
=280

onload
="iframeResize(); document.body.scrollTop=0;"
>

您的浏览器不支持此功能,请您使用最新的版本。

</
iframe
>

</
td
>

</
tr
>

</
table
>


<
script
type
=text/javascript
>
...

function iframeResize()


...{

var dyniframe = null;

var indexwin = null;


if (document.getElementById)


...{

dyniframe = document.getElementById("mainFrame");

indexwin = window;


if (dyniframe)


...{

if (dyniframe.contentDocument)


...{

dyniframe.height = dyniframe.contentDocument.body.scrollHeight + 10;

}

else if (dyniframe.document && dyniframe.document.body.scrollHeight)


...{

iframeheight= mainFrame.document.body.scrollHeight + 10;

dyniframe.height = iframeheight;

}

}

}

}

</
script
>

</
body
>

</
html
>
10、用到的JavaScript脚本和样式表(控制树节点的关键)
1) myjs.js
var
temp, temp2, cookieArray, cookieArray2, cookieCount;

function
initiate()
...
{
cookieCount=0;

if(document.cookie)...{
cookieArray=document.cookie.split(";");
cookieArray2=new Array();

for(i in cookieArray)...{
cookieArray2[cookieArray[i].split("=")[0].replace(/ /g,"")]=cookieArray[i].split("=")[1].replace(/ /g,"");
}
}
cookieArray=(document.cookie.indexOf("state=")>=0)?cookieArray2["state"].split(","):new Array();
temp=document.getElementById("containerul");

for(var o=0;o<temp.getElementsByTagName("li").length;o++)...{

if(temp.getElementsByTagName("li")[o].getElementsByTagName("ul").length>0)...{
temp2 = document.createElement("span");
temp2.className = "symbols";
temp2.style.backgroundImage = (cookieArray.length>0)?((cookieArray[cookieCount]=="true")?"url(images/minus.png)":"url(images/plus.png)"):"url(images/plus.png)";

temp2.onclick=function()...{
showhide(this.parentNode);
writeCookie();
}
temp.getElementsByTagName("li")[o].insertBefore(temp2,temp.getElementsByTagName("li")[o].firstChild)
temp.getElementsByTagName("li")[o].getElementsByTagName("ul")[0].style.display = "none";

if(cookieArray[cookieCount]=="true")...{
showhide(temp.getElementsByTagName("li")[o]);
}
cookieCount++;
}

else...{
temp2 = document.createElement("span");
temp2.className = "symbols";
temp2.style.backgroundImage = "url(images/page.png)";
temp.getElementsByTagName("li")[o].insertBefore(temp2,temp.getElementsByTagName("li")[o].firstChild);
}
}
}

function
showhide(el)
...
{
el.getElementsByTagName("ul")[0].style.display=(el.getElementsByTagName("ul")[0].style.display=="block")?"none":"block";
el.getElementsByTagName("span")[0].style.backgroundImage=(el.getElementsByTagName("ul")[0].style.display=="block")?"url(images/minus.png)":"url(images/plus.png)";
}

function
writeCookie()
...
{ // Runs through the menu and puts the "states" of each nested list into an array, the array is then joined together and assigned to a cookie.
cookieArray=new Array()

for(var q=0;q<temp.getElementsByTagName("li").length;q++)...{

if(temp.getElementsByTagName("li")[q].childNodes.length>0)...{

if(temp.getElementsByTagName("li")[q].childNodes[0].nodeName=="SPAN" && temp.getElementsByTagName("li")[q].getElementsByTagName("ul").length>0)...{
cookieArray[cookieArray.length]=(temp.getElementsByTagName("li")[q].getElementsByTagName("ul")[0].style.display=="block");
}
}
}
document.cookie="state="+cookieArray.join(",")+";expires="+new Date(new Date().getTime() + 365*24*60*60*1000).toGMTString();
}
2) mycss.css
#containerul, #containerul ul
{...}
{
text-align:left;

margin:0; /**//* Removes browser default margins applied to the lists. */

padding:0; /**//* Removes browser default padding applied to the lists. */
}

#containerul li
{...}
{

margin:0 0 0 30px; /**//* A left margin to indent the list items and give the menu a sense of structure. */

padding:0; /**//* Removes browser default padding applied to the list items. */

list-style-type:none; /**//* Removes the bullet point that usually goes next to each item in a list. */
}

#containerul .symbols
{...}
{ /**//* Various styles to position the symbols next to the items in the menu. */
float:left;
width:12px;
height:1em;
background-position:0 50%;
background-repeat:no-repeat;
}
11、自定义标签的编写
1) 创建标签处理类(Tag Handler ClassàRecursionTag.java)
package
com.lideedu.yame.utils;


import
java.io.IOException;

import
java.util.Iterator;

import
java.util.List;

import
java.util.Set;

import
javax.servlet.jsp.JspException;

import
javax.servlet.jsp.JspWriter;

import
javax.servlet.jsp.tagext.TagSupport;

import
com.lideedu.yame.tree.dao.ProductDAO;

import
com.lideedu.yame.tree.pojos.Product;

import
com.lideedu.yame.tree.pojos.ProductCategory;


@SuppressWarnings(
"
serial
"
)


public
class
RecursionTag
extends
TagSupport
...
{

private List list = null;



public List getList() ...{

return list;

}



public void setList(List list) ...{

this.list = list;

}



public int doStartTag() throws JspException ...{

JspWriter out = pageContext.getOut();


for (Iterator iter = list.iterator(); iter.hasNext();) ...{

ProductCategory element = (ProductCategory) iter.next();

printTreeNode(out, element);

}

return TagSupport.SKIP_BODY;

}



/** *//**

* 由MyEclipse重构的方法

* @param out

* @param element

*/


private void printTreeNode(JspWriter out, ProductCategory element) ...{


try ...{

out.println("<li>"+element.getCategoryName()+"<ul>");


} catch (IOException e) ...{

e.printStackTrace();

}

printAllCategory(element);


try ...{

out.println("</ul></li>");


} catch (IOException e) ...{

e.printStackTrace();

}

}



private void printAllCategory(ProductCategory pc)...{

JspWriter out = pageContext.getOut();

ProductDAO productDAO = new ProductDAO();

// 如果该类还拥有子类,则进行递归


if(pc.getSubCategories().size()>0)...{

Set set = pc.getSubCategories();


for (Iterator iter = set.iterator(); iter.hasNext();) ...{

ProductCategory element = (ProductCategory) iter.next();

printTreeNode(out, element);

}

// 打印出无子类的商品类别下的所有商品信息


}else if(productDAO.queryByCategoryId(pc.getCategoryId()) != null)...{

List list = productDAO.queryByCategoryId(pc.getCategoryId());


for (Iterator iter = list.iterator(); iter.hasNext();) ...{

Product element = (Product) iter.next();


try ...{

// 为具体商品添加超链接

out.println("<li><a href=queryProduct.do?productId=" + element.getProductId()+

" title=单击查看商品详细信息 target=mainFrame>"+element.getProductName()+"</a></li>");


} catch (IOException e) ...{

e.printStackTrace();

}

}

}

else


try ...{

out.println("<li><ul>"+pc.getCategoryName()+"</ul></li>");


} catch (IOException e) ...{

e.printStackTrace();

}

}

}
2) 创建标签库描述文件(Tag Library Descriptor Fileàrecursion.tld)
放在路径 /WEB-INF/recursion.tld
<?
xml version="1.0" encoding="UTF-8"
?>

<!
DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "web-jsptaglibrary_1_2.dtd"
>

<
taglib
>

<
tlib-version
>
1.0
</
tlib-version
>

<
jsp-version
>
2.0
</
jsp-version
>

<
short-name
>
recursion
</
short-name
>

<
tag
>

<
name
>
yame
</
name
>

<
tag-class
>
com.lideedu.yame.utils.RecursionTag
</
tag-class
>

<
body-content
>
empty
</
body-content
>

<
attribute
>

<
name
>
list
</
name
>

<
required
>
true
</
required
>

<
rtexprvalue
>
true
</
rtexprvalue
>

</
attribute
>

</
tag
>

</
taglib
>
四、总结说明
1、 该项目只是简单的实现树形菜单,有一种业务逻辑没有考虑,就是当类别目录下同时拥有子目录和具体商品的时候只会显示子目录,而具体商品没显示,只需在标签处理类RecursionTag.java中的printAllCategory方法中添加相应的业务逻辑即可实现,有兴趣自行加工 J
2、 整个工程的完整源码打包上传,解压后直接用Eclipse导入就可运行,为方便上传下载去除了/WEB-INF/lib目录下的jar文件,自已添加便可
3、由于时间仓促、水平有限,可能存在不足之处,欢迎指教!!!有问题的也可联系
完整代码打包下载:
http://dl2.youkuaiyun.com/down4/20070717/17210233237.rar
E-Mail:zym83215@163.com
QQ:10389811
郑炎铭
2007-07-17 20:20