Võ Văn Hải's blog

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

Bean-Managed Persistent Entity Beans – EJB2- Student Example

Bean-Managed Persistent Entity Beans

PDF version here

I. Khái niệm

1. Bean-Managed Persistence


– Với BMP, người viết bean phải viết tường minh các triệu gọi dùng để truy xuất cơ sở dữ liệu. Truy xuất dữ liệu có thể được mã hóa bên trong bean hoặc đóng gói trong đối tượng truy xuất dữ liệu như một phần của bean.

– BMP được chọn dùng khi:

  • Muốn điều khiển hoàn toàn việc truy xuất cơ sở dữ liệu, như viết các truy vấn tối ưu.
  • Muốn viết thao tác truy xuất cho các hệ quản trị cơ sở dữ liệu cũ, ERP…
  • Nơi lưu trữ không phải là một hệ quản trị cơ sở dữ liệu.

Tuy nhiên, mối liên hệ quá chặt chẽ giữa BMP và cơ sở dữ liệu nó thao tác trên đó là một yếu điểm của BMP.

– BMP cũng như các Entity Bean khác, có 3 trạng thái:

  • Does Not Exits: không tồn tại trong bộ nhớ.
  • Pooled: có mặt trong instance pool nhưng chưa sẳn sàng hoạt động.
  • Ready: sẵn sàng đáp ứng các yêu cầu từ client cho EJB Object ủy nhiệm đến.

2. Vòng đời của Entity Bean

– Vòng đời của bean bắt đầu khi container gọi Class.newInstance(), sau đó liên kết bean với đối tượng chứa thông tin môi trường bằng cách gọi phương thức setEntityContext(), bean hình thành và được đặt vào instance pool (trạng thái Pooled). Bean trong pool không liên kết với bất kỳ cơ sở dữ liệu và một EJB Object nào, container có thể dùng bean này để thực hiện bất kỳ một phương thức home hoặc finder nào.

– Khi container chọn bean để phục vụ một triệu gọi của client đến EJB Object, bean chuyển sang trạng thái Ready, liên kết với cơ sở dữ liệu và với EJB Object cụ thể. Có hai cách chuyển đến trạng thái Ready:

  • Client gọi phương thức create() của Home Object và như vậy container sẽ gọi lần lượt các phương thức ejbCreate()ejbPostCreate().
  • Container gọi phương thức ejbActivate() để kích hoạt lại các bean đang thụ động, cấp phát tài nguyên như sockect cho bean..

– Trong trạng thái Ready, bean liên kết với một EJB Object. Container gọi các business method trên bean, được EJB Object ủy nhiệm đến. Container cũng đồng bộ trạng thái của bean với cơ sở dữ liệu bằng cách dùng các phương thức ejbLoad()ejbStore().

– Container có thể chuyển bean trở lại trạng thái Pooled, điều này xảy ra khi:

  • Client gọi phương thức remove() và như vậy container sẽ gọi phương thức ejbRemove().
  • Container gọi phương thức ejbPassivate() để chuyển bean sang trạng thái thụ động.

– Cuối vòng đời của bean, container sẽ loại bean ra khỏi pool và gọi phương thức unsetEntityContext() tách bean ra khỏi đối tượng chứa thông tin môi trường liên quan đến nó.

3. Các phương thức callback và các Finder

– Phương thức ejbCreate() được gọi khi ghi trạng thái của bean vào cơ sở dữ liệu, bean liên kết với đại diện EJB Object của nó. Như vậy một hàng mới được tạo và được ghi vào cơ sở dữ liệu. Đặc tả EJB từ 2.0 trở đi cho phép nạp chồng phương thức này dưới dạng ejbCreate<SUFFIX>(), cần có phương thức create<SUFFIX>() tương ứng trong home interface. Lệnh SQL tương ứng là INSERT.

– Phương thức ejbPostCreate() được gọi sau khi gọi ejbCreate(). Phương thức này dùng cho một số tác vụ như: giải quyết các vấn đề phát sinh khi chèn hàng vào cơ sở dữ liệu, thiết lập mối quan hệ giữa các bean có liên quan với nhau sau khi chúng hình thành, chuyển tham chiếu của bean đến một bean khác, thay đổi các thông số transaction, …

– Phương thức ejbRemove() dùng để tách mối liên kết giữa bean với dữ liệu tương ứng trong cơ sở dữ liệu. Lệnh SQL tương ứng là DELETE.

– Phương thức ejbLoad() dùng để nạp dữ liệu từ cơ sở dữ liệu vào bean tương ứng (còn gọi là refresh dữ liệu), đồng bộ trạng thái của bean với hàng dữ liệu mà nó ánh xạ. Phương thức này được gọi trước khi các business method được thực hiện. Lệnh SQL tương ứng là SELECT.

– Phương thức ejbStore() dùng để ghi trạng thái (dữ liệu của lớp bean) vào cơ sở dữ liệu, đồng bộ trạng thái của bean với hàng dữ liệu mà nó ánh xạ. Phương thức này được gọi sau khi business method thực hiện xong. Lệnh SQL tương ứng là UPDATE.

– Phương thức setEntityContext() dùng để liên kết bean với đối tượng javax.ejb.EntityContext. Thông qua đối tượng này nhận những thông tin liên quan đến môi trường.

– Phương thức unsetEntityContext() tách bean khỏi môi trường, giải phóng tài nguyên do setEntityContext() cấp, thực thể bean sẵn sàng để hủy.

– Mỗi phương thức có tên find<SUFFIX>() trong home interface, phải có một phương thức ejbFind<SUFFIX>() tương ứng với cùng đối số trong lớp bean. Khi một phương thức find() được triệu gọi trên Home Object, container sẽ ủy nhiệm phương thức ejbFind() tương ứng trên bean thực hiện yêu cầu. Kết quả trả về của các phương thức find() là một đối tượng kiểu EJB Object hoặc một Collection các đối tượng kiểu EJB Object, trong lúc kết quả trả về của các phương thức ejbFind() là khóa chính hoặc một Collection các khóa chính.

4. Hoạt động của Entity Bean

Các phương thức callback

1. Container đăng ký tất cả các bean đã được triển khai với JNDI.

2. Client tìm thấy (lookup()) home interface của bean thông qua JNDI.

3. Client dùng home interface để tạo ra EJB Object. Khi client triệu gọi phương thức create() của home interface, container triệu gọi phương thức ejbCreate() tương ứng của bean.

4. Client triệu gọi các business method trên EJB Object, container ủy nhiệm các lời gọi này đến bean.

5. Client gọi phương thức remove() của EJB Object, container gọi ejbRemove() của bean tương ứng.

Container đồng bộ trạng thái của bean với cơ sở dữ liệu bằng cách dùng các phương thức ejbLoad()ejbStore().

5. Lớp Primary Key (khóa chính)

– Primary Key là một đối tượng dùng để chỉ định duy nhất một entity bean. Primary Key có thể là kiểu serializable bất kỳ nào, bao gồm các kiểu có sẵn (Integer, Double, String, …) hoặc các lớp được định nghĩa bởi người phát triển bean.

– Có hai kiểu Primary Key:

  • Single-Field: Primary Key ánh xạ với một field định nghĩa trong lớp bean.
  • Compound: lớp Primary Key (cài đặt giao diện java.io.Serializable) do người dùng định nghĩa ánh xạ đến nhiều field trong lớp bean.

– Ví dụ Coumpound Primary Key:

public class ShipPK implements java.io.Serializable {

public String name;

public String registration;

public ShipPK() { }

public ShipPK( String name, String registration ) {

this.name = name;

this.registration = registration;

}

public String getName() { return name; }

public String getRegistration( ) { return registration; }

public boolean equals( Object obj ) {

if ( obj == null || !( obj instanceof ShipPK ) ) return false;

ShipPK other = ( ShipPK )obj;

if ( this.name.equals(other.name) && this.registration.equals( other.registration ) )

return true;

else

return false;

}

public int hashCode() { return name.hashCode( )^registration.hashCode( ); }

public String toString( ) { return name+” “+registration; }

}

Chú ý khi tạo lớp Primary Key:

  • Phải cài đặt giao diện java.io.Serializable.
  • Các field phải public.
  • Cần định nghĩa các phương thức equals() hashCode() để chỉ định Primary Key trong Collection.

– Phương thức findByPrimaryKey() của home interface nhận đối số là một Primary Key và trả về một đối tượng kiểu remote interface:

public Ship findByPrimaryKey( ShipPK primarykey )throws FinderException, RemoteException;

– Lớp bean phải có những field tương ứng với dữ liệu riêng của lớp Primary Key. Phương thức ejbCreate() của lớp bean cũng trả về một đối tượng Primary Key:

public ShipPK ejbCreate( String name, String registration ) {

setName( name );

setRegistration(registration);

// database insert logic

return new ShipPK( name, registration );

}

public void ejbPostCreate( String name, String registration ) { }

– Deployment Descriptor cũng phải định nghĩa lớp Primary Key cần cho triển khai:

<prim-key-class>cShipPK</prim-key-class>

II. Thiết kế

A. Tạo BMP

1. Tạo remote interface

package student;

import java.rmi.RemoteException;

import javax.ejb.EJBObject;

public interface StudentRemote extends EJBObject{

public String getName() throws RemoteException;

public String getStudentId() throws RemoteException;

public void setName(String name) throws RemoteException;

}

2. Tạo home interface

package student;

import java.rmi.RemoteException;

import java.util.Enumeration;

import javax.ejb.CreateException;

import javax.ejb.EJBHome;

import javax.ejb.FinderException;

public interface StudentRemoteHome extends EJBHome{

public StudentRemote create(String StudentId,String name)throws CreateException, RemoteException;

public StudentRemote findByPrimaryKey(String StudentId)throws FinderException, RemoteException;

public Enumeration findByName(String name)throws FinderException, RemoteException;

}

3. Lớp Primary Key (khóa chính)

– Primary Key định nghĩa thuộc tính dùng để định vị một hàng dữ liệu (tức một bean) trong cơ sở dữ liệu. Thường chỉ cần một thuộc tính, nhưng đôi khi cần một bộ thuộc tính mới xác định được một hàng duy nhất trong bảng. Nếu Primary Key chỉ là kiểu serializable có sẵn thì không cần phải viết lớp Primary Key.

4. Tạo lớp EJB

– Lớp EJB cho BMP viết phức tạp vì người cung cấp bean phải chịu trách nhiệm truy xuất cơ sở dữ liệu. Bao gồm:

  • Dữ liệu riêng (các field) và các accesors/mutators (các business method)
  • Các phương thức hỗ trợ
  • Các phương thức callback và các Finder

a) Dữ liệu riêng của bean và các accesor

– Bean phải có dữ liệu riêng, chính là thể hiện các field của hàng dữ liệu mà bean ánh xạ trong nó. Dữ liệu riêng này được truy xuất (vào/ra) bởi các getter/setter (gọi chung là các accessor). Đây cũng là các business method được client triệu gọi. Tiếp đầu ngữ set/get là quy ước, không phải bắt buộc.

private String studentId;

private String name;

public String getName() { return name; }

public String getStudentId () { return studentId; }

public void setName( String name ) { this.name = name; }

b) Phương thức hỗ trợ

– Để dễ cài đặt, nên viết trước các phương thức hỗ trợ (helper method, còn gọi là utility method). Các phương thức này chỉ dùng trong lớp nên phạm vi truy xuất thường là private. Bao gồm:

  • Tạo kết nối cơ sở dữ liệu: kết nối này lưu trong đối tượng Connection, dữ liệu riêng của bean. Kết nối cơ sở dữ liệu được thực hiện thông qua JDBC-ODBC

private Connection makeConnection() throws Exception{

Connection con=null;

Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”);

String url=“jdbc:odbc:student”;

con=DriverManager.getConnection(url);

return con;

}

  • Các phương thức dùng để chèn hoặc xóa một hàng dữ liệu của bảng, sẽ được dùng cho các phương thức ejbCreate() (chèn, lệnh SQL insert) và ejbRemove() (xóa, lệnh SQL delete) của bean.

private void insertStudent(String studentId, String name) throws Exception{

Connection con=makeConnection();

String sql=“insert into Student values(?,?)”;

PreparedStatement pSt=con.prepareStatement(sql);

pSt.setString(1, studentId);

pSt.setString(2, name);

pSt.executeUpdate();

pSt.close();

con.close();

}

private void deleteStudent(String studentId) throws Exception{

Connection con=makeConnection();

String sql=“delete from Student where studentID='”+studentId+“‘”;

con.createStatement().executeUpdate(sql);

con.close();

}

  • Các phương thức dùng để nạp hoặc lưu một hàng dữ liệu của bảng, sẽ được dùng cho các phương thức ejbLoad() (lệnh SQL select) và ejbStore() (lệnh SQL update) của bean.

private void storeStudent() throws Exception{

Connection con=makeConnection();

String sql=“update Student set name=? where studentId=?”;

PreparedStatement pSt=con.prepareStatement(sql);

pSt.setString(1, studentId);

pSt.setString(2, name);

int effectedRows=pSt.executeUpdate();

pSt.close();

con.close();

if(effectedRows<1)

throw new Exception(“Storing row for studentId:”+studentId+” failed.”);

}

private void loadStudent() throws Exception{

Connection con=makeConnection();

String sql=“select * from Student where studentID='”+studentId+“‘”;

ResultSet rs=con.createStatement().executeQuery(sql);

if(rs.next()){

this.name=rs.getString(“name”);

}

else{

throw new Exception(“Row for student id:”+studentId+” not found in database”);

}

}

c) Các phương thức callback và các Finder

– Đã mô tả phần trên.

d) Code hoàn chỉnh

package student;

import java.rmi.RemoteException;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.util.Enumeration;

import java.util.Properties;

import java.util.Vector;

import javax.ejb.CreateException;

import javax.ejb.EJBException;

import javax.ejb.EntityBean;

import javax.ejb.EntityContext;

import javax.ejb.FinderException;

import javax.ejb.ObjectNotFoundException;

public class StudentBean implements EntityBean{

private static final long serialVersionUID = 1L;

protected EntityContext context;

//Environment properties

public Properties env;

//Bean-Managed states fields

private String studentId;

private String name;

//==================================================

//default constructor

public StudentBean(){

System.out.println (“EJB created…”);

}

//Setters/getters

public void setStudentId(String studentId){

this.studentId=studentId;

}

public String getStudentId(){

return this.studentId;

}

public void setName(String name){

this.name=name;

}

public String getName(){

return this.name;

}

//==================================================

//Tao Student trong database

public String ejbCreate(String studentId,String name) throws CreateException{

try {

insertStudent(studentId,name);

this.studentId=studentId;

this.name=name;

}

catch (Exception ex) {

throw new EJBException(“ejbCreate: “+ex.getMessage());

}

return studentId;

}

public void ejbPostCreate( String studentId, String name )

{ }

public String ejbFindByPrimaryKey(String studentId) throws FinderException{

boolean result=false;

System.out.println (“in ejbFindByPrimaryKey”);

try {

result=findByPrimaryKey(studentId);

}

catch (Exception ex) {

throw new EJBException(“ejbFindByPrimaryKey”+ex.getMessage());

}

if(!result)

throw new ObjectNotFoundException(“row for id “+studentId+” not found!”);

return studentId;

}

public Enumeration ejbFindByName(String name) throws FinderException{

Enumeration a=null;

try {

a= FindByName(name);

} catch (Exception e) {

e.printStackTrace();

}

return a;

}

public void ejbRemove(){

System.out.println (“in ejbRemove…”);

try {

deleteStudent(studentId);

}

catch (Exception ex) {

throw new EJBException(“ejbRemove: “+ex.getMessage());

}

System.out.println (“leaving ejbRemove”);

}

public void setEntityContext(EntityContext context){

this.context=context;

}

public void unsetEntityContext() throws EJBException, RemoteException {

this.context=null;

this.env=null;

}

public void ejbActivate() throws EJBException, RemoteException {

this.studentId=(String)context.getPrimaryKey();

}

public void ejbPassivate() throws EJBException, RemoteException {

this.studentId=null;

}

public void ejbLoad(){

try {

loadStudent();

}

catch (Exception ex) {

throw new EJBException(“ejbLoad: “+ex.getMessage());

}

}

public void ejbStore() throws EJBException, RemoteException {

try {

storeStudent();

} catch (Exception e) {

e.printStackTrace();

}//update

}

//==========================================================

private Connection makeConnection() throws Exception{

Connection con=null;

Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”);

String url=“jdbc:odbc:student”;

con=DriverManager.getConnection(url);

return con;

}

//——————————————

/**

* T?m theo khóa chính

* @param studentId: mssv cần t?m

* @return: true nếu t?m thấy

* @throws Exception

*/

private boolean findByPrimaryKey(String studentId) throws Exception{

Connection con=makeConnection();

String sql=“select * from Student where studentID='”+studentId+“‘”;

ResultSet rs=con.createStatement().executeQuery(sql);

boolean ret=rs.next();con.close();

return ret;

}

/**

* T?m theoi tên

* @param name: tên cần t?m

* @return danh sách các sinh viên tên $name

*/

private Enumeration FindByName(String name) throws Exception{

Vector vec=new Vector();

Connection con=makeConnection();

String sql=“select * from Student where name='”+name+“‘”;

ResultSet rs=con.createStatement().executeQuery(sql);

while(rs.next())

vec.add(rs.getString(“studentId”));

con.close();

return vec.elements();

}

/**

* Xóa 1 SV khỏi CSDL

* @param studentId l mssv c?n xa

* @throws Exception

*/

private void deleteStudent(String studentId) throws Exception{

Connection con=makeConnection();

String sql=“delete from Student where studentID='”+studentId+“‘”;

con.createStatement().executeUpdate(sql);

con.close();

}

/**

* Thêm 1 sinh viên vào csdl

* @param studentId: mssv cần thêm

* @param name: tên sv cần thêm

* @throws Exception

*/

private void insertStudent(String studentId, String name) throws Exception{

Connection con=makeConnection();

String sql=“insert into Student values(?,?)”;

PreparedStatement pSt=con.prepareStatement(sql);

pSt.setString(1, studentId);

pSt.setString(2, name);

pSt.executeUpdate();

pSt.close();

con.close();

}

/**

* Update thôngng tin sinh viên

*/

private void storeStudent() throws Exception{

Connection con=makeConnection();

String sql=“update Student set name=? where studentId=?”;

PreparedStatement pSt=con.prepareStatement(sql);

pSt.setString(1, studentId);

pSt.setString(2, name);

int effectedRows=pSt.executeUpdate();

pSt.close();

con.close();

if(effectedRows<1)

throw new Exception(“Storing row for studentId:”+studentId+” failed.”);

}

/**

* Load thông tin của 1 sinh viên

*/

private void loadStudent() throws Exception{

Connection con=makeConnection();

String sql=“select * from Student where studentID='”+studentId+“‘”;

ResultSet rs=con.createStatement().executeQuery(sql);

if(rs.next()){

this.name=rs.getString(“name”);

}

else{

throw new Exception(“Row for student id:”+studentId+” not found in database”);

}

}

}

– Trong BMP, khi container chuyển bean từ trạng thái Pooled sang trạng thái Ready sẽ không tự động thiết lập khóa chính. Vì vậy, các phương thức ejbCreate()ejbActivate() cần phải thiết lập khóa chính, đó là lý do cần lưu trữ đối tượng EntityContext.

B. Tạo client truy xuất Stateless Session Bean

– Tương tự như viết phần client cho Session Bean.

package client;

import java.awt.BorderLayout;

import java.awt.Color;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.util.Enumeration;

import javax.naming.Context;

import javax.naming.InitialContext;

import javax.rmi.PortableRemoteObject;

import javax.swing.Box;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JScrollPane;

import javax.swing.JTable;

import javax.swing.JTextField;

import javax.swing.table.DefaultTableModel;

import student.StudentRemote;

import student.StudentRemoteHome;

public class Client extends JFrame implements ActionListener{

private static final long serialVersionUID = 1L;

private JTextField tfID,tfName;

private JButton btAdd,btFindById,btFindByName;

private DefaultTableModel dtm;

private JTable table;

private StudentRemoteHome studentHome;

private JLabel lblStatus;

public Client(){

super(“BMP demontraction”);

setDefaultCloseOperation(EXIT_ON_CLOSE);

setSize(400,250);setResizable(false);

Box b=Box.createVerticalBox();

Box b1=Box.createHorizontalBox();

Box b2=Box.createHorizontalBox();

Box b3=Box.createHorizontalBox();b.add(Box.createVerticalStrut(8));

b.add(b1); b.add(Box.createVerticalStrut(8));

b.add(b2);b.add(Box.createVerticalStrut(8));

b.add(b3);b.add(Box.createVerticalStrut(8));

this.add(b,BorderLayout.NORTH);

JLabel l1,l2;

b1.add(l1=new JLabel(“Student ID:”,JLabel.RIGHT));

b1.add(tfID=new JTextField(20));

b2.add(l2=new JLabel(“Name:”,JLabel.RIGHT));

b2.add(tfName=new JTextField(20));

l2.setPreferredSize(l1.getPreferredSize());

b3.add(btAdd=new JButton(“Add”));b3.add(Box.createHorizontalStrut(5));

b3.add(btFindById=new JButton(“Find By ID”));b3.add(Box.createHorizontalStrut(5));

b3.add(btFindByName=new JButton(“Find By Name”));

btAdd.addActionListener(this);

btFindById.addActionListener(this);

btFindByName.addActionListener(this);

lblStatus=new JLabel(“infos…”);lblStatus.setForeground(Color.red);

this.add(lblStatus,BorderLayout.SOUTH);

createTable();

DoLook();

}

private void DoLook() {

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” );

Context ctx = new InitialContext();

Object ref = ctx.lookup( “Student” );

studentHome = ( StudentRemoteHome )PortableRemoteObject.narrow( ref, StudentRemoteHome.class );

} catch ( Exception e ) {

e.printStackTrace();

}

}

private void createTable() {

String []hd={“Student ID”,“Name”};

dtm=new DefaultTableModel(hd,0);

table=new JTable(dtm);

this.add(new JScrollPane(table),BorderLayout.CENTER);

}

public static void main(String[] args) {

new Client().setVisible(true);

}

@Override

public void actionPerformed(ActionEvent e) {

Object o=e.getSource();

if(o.equals(btAdd)){

String id=tfID.getText();

String name=tfName.getText();

if(id.trim().equals(“”)||name.trim().equals(“”))

lblStatus.setText( “Fields cannot be null!\nPlease enter it”);

else

{

try {

studentHome.create(id, name);

lblStatus.setText(“Create new item successfully!”);

} catch (Exception e1) {

lblStatus.setText(“Can not be create new Item”);

}

}

}

else if(o.equals(btFindById)){

StudentRemote student=null;

dtm.setRowCount(0);

try {

student=studentHome.findByPrimaryKey(tfID.getText());

if(student!=null){

String []tmp=new String[2];

tmp[0] = student.getStudentId();

tmp[1] = student.getName();

dtm.addRow( tmp );

}

} catch (Exception e1) {

lblStatus.setText(e1.getMessage());

}

}

else if(o.equals(btFindByName)){

String[] tmp = new String[2];

StudentRemote student = null;

dtm.setRowCount(0);

try {

Enumeration en = studentHome.findByName( tfName.getText() );

if ( e != null ) {

while ( en.hasMoreElements() ) {

student = (StudentRemote)en.nextElement();

tmp[0] = student.getStudentId();

tmp[1] = student.getName();

dtm.addRow( tmp );

}

} else {

lblStatus.setText( “Could not find” );

}

} catch ( Exception ex ){

lblStatus.setText(ex.getMessage());

}

}

}

}

III. Triển khai

1. Chuẩn bị các deployment descriptor

– Chuẩn bị ejb-jar.xml trong thư mục META-INF.

<?xml version=”1.0″ encoding=”UTF-8″?>

<!DOCTYPE ejb-jar PUBLIC “-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN” “http://java.sun.com/dtd/ejb-jar_2_0.dtd”&gt;

<ejb-jar>

<display-name>emp</display-name>

<enterprise-beans>

<entity>

<ejb-name>Student</ejb-name>

<home>student.StudentRemoteHome</home>

<remote>student.StudentRemote</remote>

<ejb-class>student.StudentBean</ejb-class>

<persistence-type>Bean</persistence-type>

<prim-key-class>java.lang.String</prim-key-class>

<reentrant>false</reentrant>

</entity>

</enterprise-beans>

<assembly-descriptor>

<container-transaction>

<method>

<ejb-name>Student</ejb-name>

<method-name>*</method-name>

</method>

<trans-attribute>Required</trans-attribute>

</container-transaction>

</assembly-descriptor>

</ejb-jar>

– Chuẩn bị jboss.xml trong thư mục META-INF.

<?xml version=”1.0″ encoding=”UTF-8″?>

<!DOCTYPE jboss PUBLIC “-//JBoss//DTD JBOSS 4.0//EN” “http://www.jboss.org/j2ee/dtd/jboss_4_0.dtd”&gt;

<jboss>

<enterprise-beans>

<entity>

<ejb-name>Student</ejb-name>

<jndi-name>Student</jndi-name>

</entity>

</enterprise-beans>

</jboss>

2. Đóng gói EJB

– Dùng công cụ jar để đóng gói EJB

C:\EJB\BMP> jar cvf Student.jar student/*.class META-INF/*.xml

added manifest

adding: student/StudentRemote.class(in = 302) (out= 204)(deflated 32%)

adding: student/StudentBean.class(in = 5908) (out= 2775)(deflated 53%)

adding: student/StudentRemoteHome.class(in = 485) (out= 271)(deflated 44%)

adding: META-INF/ejb-jar.xml(in = 535) (out= 224)(deflated 58%)

adding: META-INF/jboss.xml(in = 262) (out= 137)(deflated 47%)

– Kết quả gói student.jar có nội dung như sau:

student

StudentRemote.class

StudentBean.class

StudentRemoteHome.class

META-INF

ejb-jar.xml

jboss.xml

MANIFEST.MF

2. Tạo cơ sở dữ liệu

– Tạo cơ sở dữ liệu bất kỳ, tạo bảng student với design như sau:

Nhập trước một số record để thử

Sau đó Control Panel->Administrative Tools->Data Sources(ODBC) tạo 1 DSN có tên student.

3. Triển khai gói student.jar:

– Sao chép gói student.jar vào thư mục %JBOSS_HOME%\server\default\deploy

4. Sử dụng client để truy xuất EJB

– cần có các gói đã nói trong bài Session Bean

Tạo lớp Client.java trong thư mục có tên client.

Nội dung như sau


package client;
import java.awt.BorderLayout;

import java.awt.Color;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.util.Enumeration;

import javax.naming.Context;

import javax.naming.InitialContext;

import javax.rmi.PortableRemoteObject;

import javax.swing.Box;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JScrollPane;

import javax.swing.JTable;

import javax.swing.JTextField;

import javax.swing.table.DefaultTableModel;

import student.StudentRemote;

import student.StudentRemoteHome;

public class Client extends JFrame implements ActionListener{

private static final long serialVersionUID = 1L;

private JTextField tfID,tfName;

private JButton btAdd,btFindById,btFindByName;

private DefaultTableModel dtm;

private JTable table;

private StudentRemoteHome studentHome;

private JLabel lblStatus;

public Client(){

super(“BMP demontraction”);

setDefaultCloseOperation(EXIT_ON_CLOSE);

setSize(400,250);setResizable(false);

Box b=Box.createVerticalBox();

Box b1=Box.createHorizontalBox();

Box b2=Box.createHorizontalBox();

Box b3=Box.createHorizontalBox();b.add(Box.createVerticalStrut(8));

b.add(b1); b.add(Box.createVerticalStrut(8));

b.add(b2);b.add(Box.createVerticalStrut(8));

b.add(b3);b.add(Box.createVerticalStrut(8));

this.add(b,BorderLayout.NORTH);

JLabel l1,l2;

b1.add(l1=new JLabel(“Student ID:”,JLabel.RIGHT));

b1.add(tfID=new JTextField(20));

b2.add(l2=new JLabel(“Name:”,JLabel.RIGHT));

b2.add(tfName=new JTextField(20));

l2.setPreferredSize(l1.getPreferredSize());

b3.add(btAdd=new JButton(“Add”));b3.add(Box.createHorizontalStrut(5));

b3.add(btFindById=new JButton(“Find By ID”));b3.add(Box.createHorizontalStrut(5));

b3.add(btFindByName=new JButton(“Find By Name”));

btAdd.addActionListener(this);

btFindById.addActionListener(this);

btFindByName.addActionListener(this);

lblStatus=new JLabel(“infos…”);lblStatus.setForeground(Color.red);

this.add(lblStatus,BorderLayout.SOUTH);

createTable();

DoLook();//lookup

}

private void DoLook() {

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” );

Context ctx = new InitialContext();

Object ref = ctx.lookup( “Student” );

studentHome = ( StudentRemoteHome )PortableRemoteObject.narrow( ref, StudentRemoteHome.class );

} catch ( Exception e ) {

e.printStackTrace();

}

}

private void createTable() {

String []hd={“Student ID”,“Name”};

dtm=new DefaultTableModel(hd,0);

table=new JTable(dtm);

this.add(new JScrollPane(table),BorderLayout.CENTER);

}

public static void main(String[] args) {

new Client().setVisible(true);

}

@Override

public void actionPerformed(ActionEvent e) {

Object o=e.getSource();

if(o.equals(btAdd)){

String id=tfID.getText();

String name=tfName.getText();

if(id.trim().equals(“”)||name.trim().equals(“”))

lblStatus.setText( “Fields cannot be null!\nPlease enter it”);

else

{

try {

studentHome.create(id, name);

lblStatus.setText(“Create new item successfully!”);

} catch (Exception e1) {

lblStatus.setText(“Can not be create new Item”);

}

}

}

else if(o.equals(btFindById)){

StudentRemote student=null;

dtm.setRowCount(0);

try {

student=studentHome.findByPrimaryKey(tfID.getText());

if(student!=null){

String []tmp=new String[2];

tmp[0] = student.getStudentId();

tmp[1] = student.getName();

dtm.addRow( tmp );

}

} catch (Exception e1) {

lblStatus.setText(e1.getMessage());

}

}

else if(o.equals(btFindByName)){

String[] tmp = new String[2];

StudentRemote student = null;

dtm.setRowCount(0);

try {

Enumeration en = studentHome.findByName( tfName.getText() );

if ( e != null ) {

while ( en.hasMoreElements() ) {

student = (StudentRemote)en.nextElement();

tmp[0] = student.getStudentId();

tmp[1] = student.getName();

dtm.addRow( tmp );

}

} else {

lblStatus.setText( “Could not find” );

}

} catch ( Exception ex ){

lblStatus.setText(ex.getMessage());

}

}

}

}

Tạo file  build.bat với nội dung

javac -d . *.java
pause

Biên dịch chương trình bằng cách chạy file build.bat. Đảm bảo là không có lỗi nào!

Tạo file run.bat với nội dung

java client/Client
pause

Thực thi file này. Kết quả như sau:
bmp_02

CHÚC CÁC BẠN THÀNH CÔNG!

6 Responses to “Bean-Managed Persistent Entity Beans – EJB2- Student Example”

  1. Kat said

    link ảnh cuối cùng died thì phải ^^! … bạn rảnh repair lại nhé . Thanx cho bài viết rất nhiều.

  2. thanh long said

    cho mình hỏi trong lớp remote interface sao có phương thức setName() mà lại không có setStudentID(). hoạt động của lớp này là gì ?

  3. Fi NB said

    Thầy ơi coi giúp em:
    Em đã làm giống như thầy:
    – EJB đã insert vô database.
    – Nhưng lúc EJB gọi StoreStudent thì lại báo lỗi trên server Jboss

    12:24:20,931 INFO [EJBDeployer] Undeploying: file:/C:/jboss-4.2.3.GA/server/default/deploy/TestQLSV.jar
    12:24:20,947 INFO [ProxyFactory] Unbind EJB Home ‘Student’ from jndi ‘Student’
    12:24:20,962 INFO [EjbModule] Undeployed Student
    12:24:21,072 INFO [EjbModule] Deploying Student
    12:24:21,119 INFO [ProxyFactory] Bound EJB Home ‘Student’ to jndi ‘Student’
    12:24:21,119 INFO [EJBDeployer] Deployed: file:/C:/jboss-4.2.3.GA/server/default/deploy/TestQLSV.jar
    12:24:56,916 INFO [STDOUT] EJB created…
    12:24:56,994 ERROR [STDERR] java.lang.Exception: Storing row for studentId: hoa failed.
    12:24:56,994 ERROR [STDERR] at com.bmp.Student.storeStudent(Student.java:264)
    12:24:56,994 ERROR [STDERR] at com.bmp.Student.ejbStore(Student.java:158)
    12:24:56,994 ERROR [STDERR] at sun.reflect.GeneratedMethodAccessor114.invoke(Unknown Source)
    12:24:56,994 ERROR [STDERR] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    12:24:56,994 ERROR [STDERR] at java.lang.reflect.Method.invoke(Method.java:597)
    12:24:57,150 ERROR [STDERR] at org.jboss.ejb.plugins.BMPPersistenceManager.invokeEjbStore(BMPPersistenceManager.java:498)
    12:24:57,150 ERROR [STDERR] at org.jboss.resource.connectionmanager.CachedConnectionInterceptor.invokeEjbStore(CachedConnectionInterceptor.java:294)
    12:24:57,150 ERROR [STDERR] at org.jboss.ejb.EntityContainer.invokeEjbStore(EntityContainer.java:742)
    12:24:57,150 ERROR [STDERR] at org.jboss.ejb.GlobalTxEntityMap$2.invokeEjbStore(GlobalTxEntityMap.java:132)
    12:24:57,150 ERROR [STDERR] at org.jboss.ejb.GlobalTxEntityMap$GlobalTxSynchronization.synchronize(GlobalTxEntityMap.java:300)
    12:24:57,150 ERROR [STDERR] at org.jboss.ejb.GlobalTxEntityMap$GlobalTxSynchronization.beforeCompletion(GlobalTxEntityMap.java:364)
    12:24:57,150 ERROR [STDERR] at com.arjuna.ats.internal.jta.resources.arjunacore.SynchronizationImple.beforeCompletion(SynchronizationImple.java:114)
    12:24:57,150 ERROR [STDERR] at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.beforeCompletion(TwoPhaseCoordinator.java:247)
    12:24:57,150 ERROR [STDERR] at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.end(TwoPhaseCoordinator.java:86)
    12:24:57,150 ERROR [STDERR] at com.arjuna.ats.arjuna.AtomicAction.end(AtomicAction.java:216)
    12:24:57,150 ERROR [STDERR] at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commit(TransactionImple.java:240)
    12:24:57,150 ERROR [STDERR] at org.jboss.ejb.plugins.TxInterceptorCMT.endTransaction(TxInterceptorCMT.java:501)
    12:24:57,150 ERROR [STDERR] at org.jboss.ejb.plugins.TxInterceptorCMT.runWithTransactions(TxInterceptorCMT.java:361)
    12:24:57,150 ERROR [STDERR] at org.jboss.ejb.plugins.TxInterceptorCMT.invokeHome(TxInterceptorCMT.java:161)
    12:24:57,150 ERROR [STDERR] at org.jboss.ejb.plugins.SecurityInterceptor.invokeHome(SecurityInterceptor.java:145)
    12:24:57,150 ERROR [STDERR] at org.jboss.ejb.plugins.LogInterceptor.invokeHome(LogInterceptor.java:132)
    12:24:57,150 ERROR [STDERR] at org.jboss.ejb.plugins.ProxyFactoryFinderInterceptor.invokeHome(ProxyFactoryFinderInterceptor.java:107)
    12:24:57,150 ERROR [STDERR] at org.jboss.ejb.EntityContainer.internalInvokeHome(EntityContainer.java:521)
    12:24:57,150 ERROR [STDERR] at org.jboss.ejb.Container.invoke(Container.java:981)
    12:24:57,150 ERROR [STDERR] at sun.reflect.GeneratedMethodAccessor112.invoke(Unknown Source)
    12:24:57,150 ERROR [STDERR] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    12:24:57,150 ERROR [STDERR] at java.lang.reflect.Method.invoke(Method.java:597)
    12:24:57,150 ERROR [STDERR] at org.jboss.mx.interceptor.ReflectedDispatcher.invoke(ReflectedDispatcher.java:155)
    12:24:57,150 ERROR [STDERR] at org.jboss.mx.server.Invocation.dispatch(Invocation.java:94)
    12:24:57,150 ERROR [STDERR] at org.jboss.mx.server.Invocation.invoke(Invocation.java:86)
    12:24:57,150 ERROR [STDERR] at org.jboss.mx.server.AbstractMBeanInvoker.invoke(AbstractMBeanInvoker.java:264)
    12:24:57,150 ERROR [STDERR] at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:659)
    12:24:57,150 ERROR [STDERR] at org.jboss.invocation.unified.server.UnifiedInvoker.invoke(UnifiedInvoker.java:231)
    12:24:57,150 ERROR [STDERR] at sun.reflect.GeneratedMethodAccessor111.invoke(Unknown Source)
    12:24:57,150 ERROR [STDERR] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    12:24:57,150 ERROR [STDERR] at java.lang.reflect.Method.invoke(Method.java:597)
    12:24:57,150 ERROR [STDERR] at org.jboss.mx.interceptor.ReflectedDispatcher.invoke(ReflectedDispatcher.java:155)
    12:24:57,150 ERROR [STDERR] at org.jboss.mx.server.Invocation.dispatch(Invocation.java:94)
    12:24:57,150 ERROR [STDERR] at org.jboss.mx.server.Invocation.invoke(Invocation.java:86)
    12:24:57,150 ERROR [STDERR] at org.jboss.mx.server.AbstractMBeanInvoker.invoke(AbstractMBeanInvoker.java:264)
    12:24:57,150 ERROR [STDERR] at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:659)
    12:24:57,150 ERROR [STDERR] at javax.management.MBeanServerInvocationHandler.invoke(MBeanServerInvocationHandler.java:288)
    12:24:57,150 ERROR [STDERR] at $Proxy14.invoke(Unknown Source)
    12:24:57,150 ERROR [STDERR] at org.jboss.remoting.ServerInvoker.invoke(ServerInvoker.java:809)
    12:24:57,150 ERROR [STDERR] at org.jboss.remoting.transport.socket.ServerThread.processInvocation(ServerThread.java:608)
    12:24:57,150 ERROR [STDERR] at org.jboss.remoting.transport.socket.ServerThread.dorun(ServerThread.java:406)
    12:24:57,150 ERROR [STDERR] at org.jboss.remoting.transport.socket.ServerThread.run(ServerThread.java:173)

  4. Phucchoj said

    Cam on Thay.Bai viet rat chi tiet va huu ich.=))

  5. Trước tiên là em rất cám ơn bài hướng dẫn của thầy vì rất chi tiết và rỏ ràng giúp em hiểu thêm về BMP.
    Thầy ơi nếu em áp dụng BMP để xây dựng website thì em áp dụng thế nào ? vì em chưa xác định được các phương thức truy vấn xử lý mình đặt trên Bean(jar) hay bên Bean(war) để đúng với BMP. Thầy cho em xin một ví dụ đơn giản áp dụng BMP trong website với thầy vì em gần làm đồ án mà em định dùng BMP để làm. ( Em giả vụ mình có csdl người dùng, sản phẩm, hóa đơn, hóa đơn chi tiết) em muốn làm web theo BMP thì em làm sao mới đúng chuẩn, thầy giúp em với.

  6. Võ Văn Hải said

    EJB 3x bây giờ không dùng BMP nữa nên em không nên học kỹ nó làm chi. Chúc em thành công!

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.