一直想做一個 EJB3.0 結合 Struts 的例子,前天剛好從網上找到了一個相關的 Tutorial ,于是小試了一把,雖然只是完成了 Tutorial 上的一小點功能,但是感覺還是有必要寫下來,免得以后還要再去看別人的Tutorial 。
開發環境:??
??? Eclipse3.2 + WTP1.5 , JBoss 4.0.3SP1 , jboss-EJB-3.0_RC5-PFD,MySQL5.0.16
? 環境的搭建可以參考我的學習筆記:
??? EJB 3.0 學習筆記——準備工作 : http://www.tkk7.com/felicity/archive/2006/03/26/37510.html
??? EJB 3.0 學習筆記—— Entity Bean : http://www.tkk7.com/felicity/archive/2006/05/20/47229.html
??? 在MySQL? 中新建數據庫ejbstruts,在JBoss的all\deploy下新建ejbstruts-ds.xml,其內容如下:
<?
xml?version="1.0"?encoding="UTF-8"
?>
?
<!--
?$Id:?mysql-ds.xml,v?1.3.2.1?2004/12/01?11:46:00?schrouf?Exp?$?
-->
<!--
??Datasource?config?for?MySQL?using?3.0.9?available?from:
http://www.mysql.com/downloads/api-jdbc-stable.html?
-->
?
<
datasources
>
??
<
local-tx-datasource
>
????
<
jndi-name
>
EJBStrutsDS
</
jndi-name
>
????
<
connection-url
>
jdbc:mysql://localhost:3306/ejbstruts
</
connection-url
>
????
<
driver-class
>
com.mysql.jdbc.Driver
</
driver-class
>
????
<
user-name
>
test
</
user-name
>
????
<
password
></
password
>
????
<
exception-sorter-class-name
>
org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter
</
exception-sorter-class-name
>
????
<!--
?sql?to?call?when?connection?is?created
????<new-connection-sql>some?arbitrary?sql</new-connection-sql>
??????
-->
????
<!--
?sql?to?call?on?an?existing?pooled?connection?when?it?is?obtained?from?pool?
????<check-valid-connection-sql>some?arbitrary?sql</check-valid-connection-sql>
??????
-->
?
????
<!--
?corresponding?type-mapping?in?the?standardjbosscmp-jdbc.xml?(optional)?
-->
????
<
metadata
>
???????
<
type-mapping
>
mySQL
</
type-mapping
>
????
</
metadata
>
??
</
local-tx-datasource
>
</
datasources
>
?
新建工程:
?最后的程序會打包成一個EAR部署到JBoss,所以在Eclipse中創建三個新的工程:
??1、EAR Project:EJBStruts???????
選好 Target Runtiem為 JBoss后Finish。
?2、Java EJB3 Project: EJBStrutsEJB
由于WTP1.5還不支持EJB3.0的直接創建,這里新建的是一個普通的Java Project,之后會用ant來創建ejb jar。

由于是普通的Java Project,之后直接Finish好了。

?3、? WEB Project:EJBStrutsWeb

這里可以將工程添加到前面建好的EAR中。

EJB模塊
向EJBStrutsEJB工程中導入User Library:EJB3_JBoss
新建兩個EJB Entities:Book 和Customer
/**
?*?Book.java
?
*/
package
?com.lzy.library.domain;
import
?java.io.Serializable;?
import
?javax.persistence.Column;
import
?javax.persistence.Entity;
import
?javax.persistence.GeneratedValue;
import
?javax.persistence.GenerationType;
import
?javax.persistence.Id;
import
?javax.persistence.JoinColumn;
import
?javax.persistence.ManyToOne;
import
?javax.persistence.Table;?
@Entity
@Table(name?
=
?
"
BOOK
"
)
public
?
class
?Book?
implements
?Serializable?{
?
/**
??*?
??
*/
?
private
?
static
?
final
?
long
?serialVersionUID?
=
?
1L
;?
?
private
?Integer?id;?
?
private
?String?title;?
?
private
?String?author;?
?
private
?Customer?customer;?
?
public
?Book()?{
??
super
();
?}?
?
public
?Book(Integer?id,?String?title,?String?author)?{
??
super
();
??
this
.id?
=
?id;
??
this
.title?
=
?title;
??
this
.author?
=
?author;
?}?
?@Column(name?
=
?
"
AUTHOR
"
)
?
public
?String?getAuthor()?{
??
return
?author;
?}?
?
public
?
void
?setAuthor(String?author)?{
??
this
.author?
=
?author;
?}?
?@Id
?@GeneratedValue(strategy?
=
?GenerationType.AUTO)
?@Column(name?
=
?
"
ID
"
)
?
public
?Integer?getId()?{
??
return
?id;
?}?
?
public
?
void
?setId(Integer?id)?{
??
this
.id?
=
?id;
?}?
?@Column(name?
=
?
"
TITLE
"
)
?
public
?String?getTitle()?{
??
return
?title;
?}?
?
public
?
void
?setTitle(String?title)?{
??
this
.title?
=
?title;
?}?
?@ManyToOne
?@JoinColumn(name?
=
?
"
CUSTOMER_ID
"
)
?
public
?Customer?getCustomer()?{
??
return
?customer;
?}?
?
public
?
void
?setCustomer(Customer?customer)?{
??
this
.customer?
=
?customer;
?}?
?@Override
?
public
?String?toString()?{
??
return
?
"
Book:?
"
?
+
?getId()?
+
?
"
?Title?
"
?
+
?getTitle()?
+
?
"
?Author?
"
????
+
?getAuthor();
?}?
}
?
/**?*/
/**
?*?Customer.java
?
*/
package
?com.lzy.library.domain;?

import
?java.io.Serializable;
import
?java.util.ArrayList;
import
?java.util.List;?

import
?javax.persistence.CascadeType;
import
?javax.persistence.Column;
import
?javax.persistence.Entity;
import
?javax.persistence.FetchType;
import
?javax.persistence.GeneratedValue;
import
?javax.persistence.GenerationType;
import
?javax.persistence.Id;
import
?javax.persistence.OneToMany;
import
?javax.persistence.Table;?


/**?*/
/**
?*?
@author
?rouserli
?*
?
*/
?

@Entity
@Table(name?
=
?
"
CUSTOMER
"
)

public
?
class
?Customer?
implements
?Serializable?
{

?
/**?*/
/**
??*?
??
*/
?
private
?
static
?
final
?
long
?serialVersionUID?
=
?
1L
;?

?
private
?Integer?id;?

?
private
?String?name;?

?
private
?List?books?
=
?
new
?ArrayList();?


?
public
?Customer()?
{
??
super
();
??
//
?TODO?Auto-generated?constructor?stub
?}
?


?
public
?Customer(Integer?id,?String?name)?
{
??
super
();
??
this
.id?
=
?id;
??
this
.name?
=
?name;
?}
?

?@Id
?@GeneratedValue(strategy?
=
?GenerationType.AUTO)
?@Column(name?
=
?
"
ID
"
)

?
public
?Integer?getId()?
{
??
return
?id;
?}
?


?
public
?
void
?setId(Integer?id)?
{
??
this
.id?
=
?id;
?}
?

?@Column(name?
=
?
"
NAME
"
)

?
public
?String?getName()?
{
??
return
?name;
?}
?


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

?@OneToMany(cascade?
=
?CascadeType.ALL,?fetch?
=
?FetchType.EAGER,?mappedBy?
=
?
"
customer
"
,?targetEntity?
=
?Book.
class
)

?
public
?List?getBooks()?
{
??
return
?books;
?}
?


?
public
?
void
?setBooks(List?books)?
{
??
this
.books?
=
?books;
?}
?

?@Override

?
public
?String?toString()?
{
??
return
?
"
Customer:?
"
?
+
?getId()?
+
?
"
?Name?
"
?
+
?getName();
?}
}
在工程下新建
META-INF目錄,該目錄下新建persistence.xml文件。
?
<?
xml?version="1.0"?encoding="UTF-8"
?>
<
persistence
>
???
<
persistence-unit?
name
="test"
>
??????
<
jta-data-source
>
java:/EJBStrutsDS
</
jta-data-source
>
??????
<
properties
>
???????
<
property?
name
="hibernate.dialect"
?value
="org.hibernate.dialect.MySQLDialect"
/>
???????
????????
<
property?
name
="hibernate.hbm2ddl.auto"
?value
="update"
/>
??????
</
properties
>
???
</
persistence-unit
>
</
persistence
>
然后新建DAO接口BookDao(Local)和CustomerDao(Local),以及各自的實現BookDaoImpl(Stateless)和CustomerDaoImpl(Stateless),在后面的Web工程中,將通過這兩個DAO接口進行數據庫操作。由于這部分可供參考的例子很多,略去源碼。
新建文件builder.xml
<?
xml?version="1.0"
?>
?

<!--
?=======================================================================?
-->
<!--
?JBoss?build?file???????????????????????????????????????????????????????
-->
<!--
?=======================================================================?
-->
?

<
project?
name
="JBoss"
?default
="ejbjar"
?basedir
="."
>
?

???
<
property?
file
="../local.properties"
?
/>
???
<
property?
environment
="env"
/>
???
<
property?
name
="src.dir"
?value
="${basedir}/src"
/>
???
<
property?
name
="jboss.home"
?value
="E:/Programming/Servers/jboss-4.0.3SP1/"
/>
???
<
property?
name
="jboss.server.config"
?value
="all"
/>
???
<
property?
name
="build.dir"
?value
="${basedir}/build"
/>
???
<
property?
name
="build.classes.dir"
?value
="${build.dir}/classes"
/>
?

???
<!--
?Build?classpath?
-->
???
<
path?
id
="classpath"
>
??????
<!--
?So?that?we?can?get?jndi.properties?for?InitialContext?
-->
??????
<
pathelement?
location
="${basedir}"
/>
??????
<
fileset?
dir
="${jboss.home}/lib"
>
?????????
<
include?
name
="**/*.jar"
/>
??????
</
fileset
>
??????
<
fileset?
dir
="${jboss.home}/server/${jboss.server.config}/lib"
>
?????????
<
include?
name
="**/*.jar"
/>
??????
</
fileset
>
??????
<
fileset?
dir
="${jboss.home}/server/${jboss.server.config}/deploy/ejb3.deployer"
>
?????????
<
include?
name
="*.jar"
/>
??????
</
fileset
>
??????
<
fileset?
dir
="${jboss.home}/server/${jboss.server.config}/deploy/jboss-aop-jdk50.deployer"
>
?????????
<
include?
name
="*.jar"
/>
??????
</
fileset
>
??????
<
pathelement?
location
="${build.classes.dir}"
/>
???
</
path
>
?

???
<
property?
name
="build.classpath"
?refid
="classpath"
/>
?

???
<!--
?===================================================================?
-->
???
<!--
?Prepares?the?build?directory????????????????????????????????????????
-->
???
<!--
?===================================================================?
-->
???
<
target?
name
="prepare"
>
??????
<
mkdir?
dir
="${build.dir}"
/>
??????
<
mkdir?
dir
="${build.classes.dir}"
/>
???
</
target
>
?

???
<!--
?===================================================================?
-->
???
<!--
?Compiles?the?source?code????????????????????????????????????????????
-->
???
<!--
?===================================================================?
-->
???
<
target?
name
="compile"
?depends
="prepare"
>
??????
<
javac?
srcdir
="${src.dir}"
?????????destdir
="${build.classes.dir}"
?????????debug
="on"
?????????deprecation
="on"
?????????optimize
="off"
?????????includes
="**"
>
?????????
<
classpath?
refid
="classpath"
/>
??????
</
javac
>
???
</
target
>
?

???
<
target?
name
="ejbjar"
?depends
="compile"
>
??????
<
jar?
jarfile
="build/EJBStrutsEJB.jar"
>
?????????
<
fileset?
dir
="${build.classes.dir}"
>
????????????
<
include?
name
="**/*.class"
/>
?????????
</
fileset
>
????????
<
fileset?
dir
="."
>
???????????
<
include?
name
="META-INF/persistence.xml"
/>
????????
</
fileset
>
??????
</
jar
>
??????
<
copy?
file
="build/EJBStrutsEJB.jar"
?todir
="${jboss.home}/server/${jboss.server.config}/deploy"
/>
???
</
target
>
???

???
<
target?
name
="clean"
>
??????
<
delete?
dir
="${build.dir}"
/>
??????
<
delete?
file
="${jboss.home}/server/${jboss.server.config}/deploy/EJBStrutsEJB.jar"
/>
???
</
target
>
?
</
project
>
Ant build后將可以看到EJBStrutsEJB.jar。
Web(struts)模塊:
這部分更加簡單,完成一個最簡單的功能:向數據庫中添加測試數據。
只需要在相應的Action類里得到DAO,然后操作數據庫。
首先將EJBStrutsEJB中的EJBStrutsEJB.jar添加到工程的build path 中:

然后創建相應的Action類:
/**
/**?
?*?Common.java
?
*/
package
?com.lzy.library.web.action;?
/**
?*?
@author
?rouserli
?*
?
*/
public
?
class
?Common?{
?
private
?Common(){
?}
?
public
?
static
?
final
?String?JNDI_PREFIX?
=
?
"
LearnEJB3/
"
;
//
??forward?used?as?success
?
public
?
static
?
final
?String?SUCCESS?
=
?
"
success
"
;
//
??forward?used?for?failure
?
public
?
static
?
final
?String?FAILURE?
=
?
"
failure
"
;
//
??forward?used?for?self
?
public
?
static
?
final
?String?SELF?
=
?
"
self
"
;
//
??session?key?for?customer
?
public
?
static
?
final
?String?CUSTOMER_KEY?
=
?
"
customer_key
"
;
//
??session?key?for?cart
?
public
?
static
?
final
?String?CART_KEY?
=
?
"
cart
"
;
}
/**
?*?
?
*/
package
?com.lzy.library.web.action;?
import
?java.util.Random;?
import
?javax.naming.Context;
import
?javax.naming.InitialContext;
import
?javax.naming.NamingException;
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.lzy.library.dao.BookDao;
import
?com.lzy.library.dao.CustomerDao;
import
?com.lzy.library.dao.impl.BookDaoImpl;
import
?com.lzy.library.dao.impl.CustomerDaoImpl;
import
?com.lzy.library.domain.Book;
import
?com.lzy.library.domain.Customer;?
/**
?*?
@author
?rouserli
?*?
?
*/
public
?
class
?CreateSampleDataAction?
extends
?Action?{?
?@Override
?
/**
??*?Method?execute
??*?
??*?
@param
?mapping
??*?
@param
?form
??*?
@param
?request
??*?
@param
?response
??*?
@return
?ActionForward
??
*/
?
public
?ActionForward?execute(ActionMapping?mapping,?ActionForm?form,
???HttpServletRequest?request,?HttpServletResponse?response)?{
??System.out.println(
"
CreateSampleData
"
);
??BookDao?bookDao;
??CustomerDao?customerDao;
??Random?r?
=
?
new
?Random();
??
try
?{
???Context?context?
=
?
new
?InitialContext();
???
???System.out.println(
"
Look?up?BookDaoImpl:
"
+
BookDaoImpl.LocalJNDIName);
???System.out.println(
"
Look?up?CustomerDaoImpl:
"
+
CustomerDaoImpl.LocalJNDIName);
???bookDao?
=
?(BookDao)?context.lookup(
"
EJBStruts/
"
?
+
?BookDaoImpl.LocalJNDIName);
???customerDao?
=
?(CustomerDao)?context
?????.lookup(
"
EJBStruts/
"
?
+
?CustomerDaoImpl.LocalJNDIName);
??}?
catch
?(NamingException?e)?{
???e.printStackTrace();
???
throw
?
new
?RuntimeException(e);
??}
??System.out.println(
"
to?return?from?action
"
);
??
??Book?book?
=
?
new
?Book(
null
,?
"
EJB?3?Developer?
"
?
+
?r.nextInt(
100
),
????
"
Sebastian
"
);
??bookDao.save(book);
??
??Customer?customer?
=
?
new
?Customer(
null
,?
"
Sebastian?
"
?
+
?r.nextInt(
100
));
??
??
??customerDao.save(customer);
?
??
return
?mapping.findForward(Common.SUCCESS);
?}?
}
在src文件夾中新建
jndi.properties:
java.naming.factory.initial
=
org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs
=
org.jboss.naming:org.jnp.interfaces
java.naming.provider.url
=
localhost
頁面文件:
welcom.jsp
<%
@?page?language
=
"
java
"
?contentType
=
"
text/html;?charset=GBK
"
?pageEncoding
=
"
GBK
"
%>
<%
@?taglib?uri
=
"
?http://struts.apache.org/tags-bean?
"
?prefix
=
"
bean
"
%>
<%
@?taglib?uri
=
"
?http://struts.apache.org/tags-html?
"
?prefix
=
"
html
"
%>
<%
@?taglib?uri
=
"
?http://struts.apache.org/tags-tiles?
"
?prefix
=
"
tiles
"
%>
?

<!
DOCTYPE?HTML?PUBLIC?"-//W3C//DTD?HTML?4.01?Transitional//EN"
>
?
<
html
>
<
head
>
<
meta?
http-equiv
="Content-Type"
?content
="text/html;?charset=GBK"
>
<
title
>
navigation
</
title
>
</
head
>
<
body
>
<
table
>
?
<
TR
>
??
??
<
TD
><
html:link?
action
="createSampleData"
>
Create?Sample?Data
</
html:link
>
??
</
TD
>
????
?
</
TR
>
</
table
>
</
body
>
</
html
>
?

EAR工程打包、部署、運行:
修改application.xml如下:
<?
xml?version="1.0"?encoding="UTF-8"
?>
<
application?
id
="Application_ID"
?version
="1.4"
?xmlns
="http://java.sun.com/xml/ns/j2ee"
?xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
?xsi:schemaLocation
="http://java.sun.com/xml/ns/j2eehttp://java.sun.com/xml/ns/j2ee/application_1_4.xsd"
>
?
<
display-name
>
?EJBStruts
</
display-name
>
?
<
module?
id
="WebModule_1155011213937"
>
??
<
web
>
???
<
web-uri
>
EJBStrutsWeb.war
</
web-uri
>
???
<
context-root
>
EJBStrutsWeb
</
context-root
>
??
</
web
>
?
</
module
>
??
<
module
>
????
<
ejb
>
EJBStrutsEJB.jar
</
ejb
>
??
</
module
>
</
application
>
確認EAR工程的模塊依賴無誤后Export出ear包。
將導出的EJBStruts.ear文件拷到JBoss的all/deploy目錄下,以all模式啟動JBoss。
確認啟動無誤之后可以在你的瀏覽器中打開welcom.jsp,點擊鏈接,如果執行正確,就會發現數據已經寫入數據庫中。

Reference:
??? EJB 3 Struts Framework Integration Tutorial, by Sebastian Hennebrueder
??? OReilly.Enterprise.JavaBeans.3.0.5th.Edition, by Bill Burke , Richard Monson-Haefel