Dùng rich:panelMenu hiển thị dữ liệu của 2 bảng csdl quan hệ 1-n
Trong bài này tôi muốn đề cập đến vấn đề sinh ra menu 1 cách động (dynamic) theo dữ liệu hiện có trong các bảng csdl. Ví dụ này được xây dụng làm 3 phần, phần 1 là tạo databse, phần 3 tạo ra 1 EJB làm business logic, phần 3 tôi sẽ nói đến việc phát sinh giao diện.
Yêu cầu về phần mêm:
- Eclipse ganymede (3.4), download tại http://www.eclipse.org/downloads/
- RichFaces 3.2.2, download tại http://www.jboss.org/jbossrichfaces/downloads/
- JBoss Application Server 4.2.3, download tại http://www.jboss.org/download/
1. Tạo database
Hệ quản trị Cơ sở dữ liệu ở đây được sử dụng là MS SQL Server, bạn có thể dùng bất cứ hệ quản trị csdl nào miễn tạo ra cấu trúc các bảng và mối quan hệ giữa chúng như sau là được.

Ngoài ra bạn còn cần phải có 1 username tên teo, mật khẩu cho username này là 12345678 và phải cấp quần truy xuất đến db này. Bạn cũng có thể dùng username sa với password của bạn. Script để tạo csdl trên bao gồm cả tạo username ở đây:
| IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = N’teo’) CREATE USER [teo] FOR LOGIN [teo] WITH DEFAULT_SCHEMA=[dbo] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N’[dbo].[Category]‘) AND type in (N’U')) BEGIN CREATE TABLE [dbo].[Category]( [categoryID] [int] NOT NULL, [categoryName] [nvarchar](50) NOT NULL, CONSTRAINT [PK_Category] PRIMARY KEY CLUSTERED ( [categoryID] ASC )WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY] ) ON [PRIMARY] END GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N’[dbo].[Product]‘) AND type in (N’U')) BEGIN CREATE TABLE [dbo].[Product]( [productID] [int] NOT NULL, [productName] [nvarchar](50) NOT NULL, [description] [nvarchar](250) NULL, [categoryID] [int] NOT NULL, CONSTRAINT [PK_Product] PRIMARY KEY CLUSTERED ( [productID] ASC )WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY] ) ON [PRIMARY] END GO IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N’[dbo].[FK_Product_Category]‘) AND parent_object_id = OBJECT_ID(N’[dbo].[Product]‘)) ALTER TABLE [dbo].[Product] WITH CHECK ADD CONSTRAINT [FK_Product_Category] FOREIGN KEY([categoryID]) REFERENCES [dbo].[Category] ([categoryID]) |
Bạn cần phải thêm 1 số mẫu tin cho db để thử nghiệm.
Đây là link download script sql và 2 file chứa dữ liệu thử nghiệm, bạn có thể import chúng vào database.
2. Tạo EJB
Tạo 1 project dạng EJB Project, đặt tên là RichFaceLeftPanelMenuJPA
Đây là cấu trúc file sau khi hoàn tất phần tạo EJB này.

Cách tạo EJB project với Hibarnate bạn có thể tham khảo ở bài JPA EJB3 – ví dụ mối quan hệ giữa các bảng trong CSDL. Bạn có thể download full Eclipse SourceCode ở đây sau đó import vào eclipse của bạn.
Trong project này có lớp Client.java để bạn có thể test thử ejb của bạn có thành công không sau khi deploy. Kết xuất của Client.java như sau:
| Bikes —————– Bikes Components Clothing Accessories —————– Adjustable Race Bearing Ball BB Ball Bearing Headset Ball Bearings Blade LL Crankarm ML Crankarm HL Crankarm Chainring Bolts Chainring Nut Crown Race Chain Stays Decal 1 Mountain End Caps Road End Caps Touring End Caps Freewheel —————– Chain Stays Mountain End Caps Freewheel Road End Caps Decal 1 Adjustable Race Touring End Caps |
chúc mừng! bạn đã có tầng business logic rồi. Bây giờ có thể là lúc phát triển jsf.
3. Thiết kế giao diện JSF
Tạo 1 project dạng Dynamic Web Project với tên RichFaceLeftPanelMenuJSF. Tham chiếu đến project ejb ở trên bằng cách nhấn phải chuột lên project vừa tạo, chọn References sau đó tìm đến mục Java EE Module Dependencies, check vào project RichFaceLeftPanelMenuJPA.
Kết qủa đạt được sau khi xong ứng dụng như hình sau:

Công việc của chúng ta là viết các managed bean. Ở đây chúng ta nói về cách viết bean để tạo ra giao diện chứ không phải thiết kế bằng cách viết các tag của jsf.
Bạn tạo gói vovanhai.wordpress.jsf. Các managed bean của chúng ta sẽ được lưu ở đây
Tạo lớp CategoryBean.java với nội dung sau:
import java.util.Collection; import java.util.Set; import javax.naming.InitialContext; import org.richfaces.component.html.HtmlPanelMenu; import org.richfaces.component.html.HtmlPanelMenuGroup; import org.richfaces.component.html.HtmlPanelMenuItem; import vovanhai.wordpress.jpa.Category; import vovanhai.wordpress.jpa.CategoryDAO; import vovanhai.wordpress.jpa.Product; public class CategoryBean { private int categoryid; private String categoryname;
private HtmlPanelMenu myPanelMenu=null;
public CategoryBean(int categoryid, String categoryname) { this.categoryid = categoryid; this.categoryname = categoryname; } public CategoryBean() { this(0,“”); } public int getCategoryid() { return categoryid; } public void setCategoryid(int categoryid) { this.categoryid = categoryid; } public String getCategoryname() { return categoryname; } public void setCategoryname(String categoryname) { this.categoryname = categoryname; } //================================================ private CategoryDAO getCategoryDAO(){ CategoryDAO dao=null; try { System.setProperty(“java.naming.factory.initial”,“org.jnp.interfaces.NamingContextFactory”); System.setProperty( “java.naming.provider.url”, “localhost:1099″); System.setProperty( “java.naming.factory.url.pkgs”, “org.jboss.naming” ); InitialContext context=new InitialContext(); dao=(CategoryDAO)context.lookup(“CategoryDAOBean/remote”); } catch (Exception e) { e.printStackTrace(); } return dao; } /** * lấy tất cả các cetagory * @return */ public Collection<Category> getAllCategory(){ CategoryDAO dao=getCategoryDAO(); if(dao==null) return null; return dao.getAllCategory(); } //============================================================= public HtmlPanelMenu getMyPanelMenu() { if(myPanelMenu==null){ myPanelMenu = new HtmlPanelMenu(); Collection<Category>catlist=getAllCategory(); Set<Product>productList=null; for(Category cat:catlist){ HtmlPanelMenuGroup group=new HtmlPanelMenuGroup(); group.setId(“cat”+cat.getCategoryid()); group.setLabel(cat.getCategoryname()); group.setIconStyle(“align=’right’”); productList=cat.getProductCollection(); for(Product pro:productList){ final HtmlPanelMenuItem item=new HtmlPanelMenuItem(); item.setId(“item”+pro.getProductid()); item.setData(pro);//gán data là 1 product để sau này chọn item.setLabel(pro.getProductname()); group.getChildren().add(item); item.setRendered(true); item.addActionListener(new MyActionListener());//trang bị sự kiện cho item } myPanelMenu.getChildren().add(group); } } return myPanelMenu; } public void setMyPanelMenu(HtmlPanelMenu myPanelMenu) { this.myPanelMenu = myPanelMenu; } } |
Ở đây chúng ta lấy dữ liệu ở các bảng nhờ vào EJB project ở trên.
Điểm quan trọng cần quan tâm của chúng ta là phương thức
public HtmlPanelMenu getMyPanelMenu() {…}
Trong đó chúng ta dùng 1 đối tượng có tên myPanelMenu là 1 thể hiện của HtmlPanelMenu. Đây chính là đối tượng đặc tả cho thẻ <rich:panelMenu …/> trên phần thiết kế bằng tag.
Đầu tiên chúng ta duyệt qua danh sách các Catagories, ứng với mỗi category ta tạo 1 HtmlPanelMenuGroup. Tiếp tục, ứng với mỗi category ta duyệt qua danh sách các product thuộc về category này, mỗi product ta tạo 1 HtmlPanelMenuItem và ta trang bị cho các item này sự kiện khi ta click chuột.
Sự kiện cho các HtmlPanelMenuItemđược viết thành 1 lớp có tên MyActionListener.java. Code như sau:
import javax.faces.event.AbortProcessingException; import javax.faces.event.ActionEvent; import javax.faces.event.ActionListener; import org.richfaces.component.html.HtmlPanelMenuItem; import vovanhai.wordpress.jpa.Product; public class MyActionListener implements ActionListener{ private Product selProduct=new Product(); public Product getSelProduct() { return selProduct; } public void setSelProduct(Product selProduct) { this.selProduct = selProduct; }
public void processAction(ActionEvent e) throws AbortProcessingException { try { HtmlPanelMenuItem sel=(HtmlPanelMenuItem)e.getSource(); selProduct=(Product)sel.getData(); System.out.println(selProduct); } catch (Exception e1) { System.out.println(“==========================================”); e1.printStackTrace(); System.out.println(“==========================================”); } } } |
Ở đây việc xử lý đơn giản là nếu người dùng chọn 1 Menu là in nó ra màn hình console của Jboss.
Ta cấu hình faces-config.xml, thêm vào 1 bean như sau:
| <managed-bean> <managed-bean-name>categoryBean</managed-bean-name> <managed-bean-class>vovanhai.wordpress.jsf.CategoryBean</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> |
Bây giờ có thể viết trang JSP được rồi, tạo trang jsp có tên LeftSidePanel.jsp với nội dung sau:
| <%@ page language=”java” contentType=”text/html; charset=UTF-8″ pageEncoding=”UTF-8″%> <%@ taglib prefix=”f” uri=”http://java.sun.com/jsf/core”%> <%@ taglib prefix=”h” uri=”http://java.sun.com/jsf/html”%> <%@ taglib prefix=”rich” uri=”http://richfaces.org/rich”%> <%@ taglib prefix=”a4j” uri=”http://richfaces.org/a4j”%> <!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”> <style type=”text/css”> .rich-pmenu-item-selected { |
Thực thi ứng dụng trên JBOSS, kết quả như đã đưa ra ở trên.
Chúc thành công!
SeBO said
Hi,
but your code was very helpful !! THX
I didn’t understand anything from Your language
V Dxung said
Xin chào bạn, mình đang tìm kiếm giải pháp cho việc hiển thị dynamic panelMenu .
Mình chưa thử các đoạn code của bạn, và mới chỉ xem qua, nhưng có lẽ đoạn code vẫn chưa xử lý được panelGroup lồng nhau đúng không ?
Ví dụ panelMenu của mình có dạng ntn :
–Root
|
—Cat 1
| |
| |-Item 1
| |-Item 2
| –Item 3
—Cat 2
|
–Cat 2.1
| |
| –Cat 2.1
–Item 2.1
Thì không hiểu bạn có hướng giải quyết gì không ?
Cái panel kiểu này cũng tương tự như tree, nhưng tree thì build in hỗ trợ sẵn rất nhiều , còn panelMenu này ít quá thì phải.
Rất vui nếu được bạn trả lời .
vovanhai said
Chuyện này bình thường thôi mà. Trong PanelMenu có 1 PanelMenu khác, lồng bao nhiêu mà chả được.
Bạn nên tham khảo tạo đây để xem cách sinh component bằng java code.
Nhớ đăng ký làm thành viên của RichFaces trước nhé!
Chúc thành công!
phamvuongkha said
thay oi! em muon hoi thay la muon lam ajax tren jsp thi lam nhu the nao.vi em dang muon tim hieu ve ajax.nhung em doc tai lieu thi kho hieu qua.thay co the cho em mot vi du ve ajax duoc khong a.
em rat cam on thay.
Sinh Vien Aptech…
Trầm Minh Trí said
Em chào thầy Hải.Thầy ơi!cho em hỏi làm cách nào mình có thể chuyển trang khác khi click vào các item trên PanelMenu vậy Thầy, Thầy có thể chỉ cho em được hông?Em đang cần gấp để làm ôồ án cuối kỳ,mong Thầy hồi âm giúp em.Em cảm ơn Thầy
Trầm Minh Trí said
Chào thầy Hải và mọi người..em làm theo hướng dẫn của thầy Hải và làm được cái panelMenu..nhưng e muốn nhúng nó trong trang web thì bằng cách nào e có thể chuyển trang khi click vào item..xin thầy Hải và bạn nào biết thì cho mình xin cách giải quyết…cảm ơn mọi ý kiến đóng góp…
Huỳnh Văn Lộc said
Thầy ơi cho em hỏi nếu em tạo EJB Project(EJB 2) chỉ cho truy xuất Local (cũng không dùng sesion để facade) thì khi mình gọi hàm getProductCollection() trong CategoryBean:
productList=cat.getProductCollection();
sẽ phát sinh lỗi phải không thưa thầy?
em làm như vậy thì nhận được lỗi: A CMR collection may only be used within the transction in which it was created….
nhưng em không biết có phải nguyên nhân là như vậy không…
mong nhận ý kiến của thầy.
Em cảm ơn thầy!