Võ Văn Hải's blog

Chỉ có một điều tuyệt đối đó là mọi thứ đều tương đối…

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:

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.

https://vovanhai.files.wordpress.com/2008/12/richpanel_01.png

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.
https://vovanhai.files.wordpress.com/2008/12/richpanel_02.png

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:
https://vovanhai.files.wordpress.com/2008/12/richpanel_03.png

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:


package vovanhai.wordpress.jsf;

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:


package vovanhai.wordpress.jsf;

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”%&gt;
<%@ taglib prefix=”h” uri=”http://java.sun.com/jsf/html”%&gt;
<%@ taglib prefix=”rich” uri=”http://richfaces.org/rich”%&gt;
<%@ taglib prefix=”a4j” uri=”http://richfaces.org/a4j”%&gt;

<!DOCTYPE html PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”&gt;
<html>
<head>
<meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″>
<title>Left-Side Category menu</title>

<style type=”text/css”>
.rich-pmenu-hovered-element {
background-color: #ff7800;
}

.rich-pmenu-item-selected {
background-color: lightcyan;
}

</style>
</head>
<body>
<f:view>
<h:form >
<h:panelGrid columns=”3″ width=”100%”>
<rich:panelMenu selectedChild=”thisChild”
binding=”#{categoryBean.myPanelMenu}” label=”Categories”
expandSingle=”false” iconCollapsedGroup=”chevronDown”
iconExpandedGroup=”chevronUp” iconGroupTopPosition=”right”
iconItem=”chevron” style=”width: 230px” >
</rich:panelMenu>
</h:panelGrid>
</h:form>
</f:view>
</body>
</html>

Thực thi ứng dụng trên JBOSS, kết quả như đã đưa ra ở trên.

Chúc thành công!

7 Responses to “Dùng rich:panelMenu hiển thị dữ liệu của 2 bảng csdl quan hệ 1-n”

  1. SeBO said

    Hi,
    I didn’t understand anything from Your language🙂 but your code was very helpful !! THX😉

  2. 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 .

  3. 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!

  4. 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…

  5. 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

  6. 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…

  7. 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!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: