JBoss EJB3(Entity Beans)备忘记
EJB3 不只是标准的 J2EE 编程接口, 它还提供新的企业应用程式开发方案.
EJB3 使用 POJOs (Plain Old Java Object), Java annotation 及 依赖注入设计模式.
使代码分离及便于测试.
在 multi tier 的企业应用系统中, 典型地分为两种 objects.
Application logic components 及 Persistent data objects.
前者主要是处理 business processes. 如计算结帐价格.
后者则是处理生命周期较长的 business data. 如将结帐价格存入资料库.
Entity Beans 为 ORM (Object Relational Mapping) 的实作.
是一种模组化并对应关系式资料库. 它是一个完全 POJO 为基楚的储存框架,
当开发资料库相关的应用程式时, 开发员典型地需要管理两种不同的 model 结构.
第一种 model 结构为 java objects ,
java objects model 资料存于内存中, 整个应用程式建构于这些资料 objects,
Java 编程语言及其 APIs 就是设计成有效率地控制这些 objects.
开发员则利用这些 APIs 来开发应用程式.
第二种则为关联式 model 结构.
资料存于关联式资料库里, 有利于分布式环境及有效率地提高大量资料的搜寻.
处理这些 java objects 及 关联式资料表 需要不同的语法及 APIs,
因此开发员需要同时模组化相同的资料两次, 及处理两种资料所在的系统及中间的转换,
这是非常没有效率的事情.
Entity beans 则是设计成处理这两种资料的桥梁, 并且它是简单的 java objects,
可以在其上利用标记简单地定义如何存入资料库,
而这些 objects 及 relational 资料映射关系自动地交由 EJB3 容器处理,
开发员则不用关心资料库的结构, 管理及定义访问的APIs.
而 EntityManager 提供的访问接口及使用 EJB QL 来处理这些 objects instance.
Entity beans 及其 callback methods 的生命周期:
可以在 entity beans 定义下面的标记, 容器会自动管理及唤起这些被标记的 methods.
@PrePersist: 此标记为当 entity 存入在资料库 前 被唤起.
@PostPersist: 此标记为当 entity 存入在资料库 后 被唤起.
@PreRemove: 此标记为当 entity 在资料库里删除 前 被唤起.
@PostRemove: 此标记为当 entity 在资料库里删除 后 被唤起.
@PreUpdate: 此标记为当 entity 在资料库里更新 前 被唤起.
@PostUpdate: 此标记为当 entity 在资料库里更新 后 被唤起.
@PostLoad: 此标记为当 entity 从资料库取得 后 被唤起.
@Remove: 这标记并不是 callback methods, 当应用程式呼叫此 method 时,
bean instance 会从 EntityManager managed context 里移除.
这时侯 bean 变成分离状况及不能使用.
亦可选择建立新 class 并标记这些 callback methods,
然后在 entity bean 里使用 @EntityListener 标记这新 class 为 callback listener.
如下例所示:
@EntityListener(CostomizeEntityListener.class)
为使这备忘记更简化, 故这里并不实作这些唤回函数.
本编主要介绍如何实作简单的 Entity Beans.
要熟识 Entity Beans, 需要了解的知识实在不少,
本编结尾提供连结, 可免费下载 Mastering EJB 3.0 PDF 版本.
开始备忘记:
这次备忘记以 Shopping Cart 作为例子, 是一种 One-To-Many 的映射关系实作.
------------ -----------
| | 1 * | |
| Order | <---------> | Item |
| | | |
------------ -----------
[1] Eclipse 启动 JBoss Server
[2] Eclipse 建立 Shopping Cart Project
[3] 建立 Shopping cart Entity Beans
[4] 建立 D:/eclipse_wtp/workspace/ShoppingCartEJB3/src/META-INF/persistence.xml
[5] 建立 Shopping cart Stateless Session Beans
[6] 建立 Shopping cart Stateful Session Beans
[7] 建立客户端测试程式
[8] 使用 ANT 建立 EJB-JAR 并执行 ShoppingCartClient 程式
[9] 使用 HSQL Database Manager 浏览资料库
[1] Eclipse 启动 JBoss Server:
Eclipse: Windows -> Show View -> Other
-->> JBoss-IDE -> Server Configuration 就会显示 JBoss Server Configuration console
然后 right client 它按 start , JBoss 就会启动, 如下图所示:
[2] Eclipse 建立 Shopping Cart Project:
Eclipse: File -> New -> Other -> EJB 3.0 -> EJB 3.0 Project
Project Name: ShoppingCartEJB3 -> Next
选择上一编已建立的 JBoss 4.0.x: jboss_configuration [default](not running)
打开后右键点选 JBoss 4.0.x -> new
然后按 Finish. ShoppingCartEJB3 project 就建立了
[3] 建立 Shopping cart Entity Beans:
/*----------------------- Order.java ---------------------*/
package ejb3.joeyta.domain;
import java.util.ArrayList;
import java.util.Collection;
import javax.persistence.CascadeType;
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;
@SuppressWarnings("serial")
@Entity // 表示此为 Entity Beans
@Table(name = "PURCHASE_ORDER") // 表示将建立的 Table 名为 PURCHASE_ORDER
public class Order implements java.io.Serializable
{
private int id;
private double total;
private Collection<Item> items;
@Id @GeneratedValue(strategy=GenerationType.AUTO)
// AUTO 定义由 persistence provider 决定怎样产生 ID
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public double getTotal()
{
return total;
}
public void setTotal(double total)
{
this.total = total;
}
public void addPurchase(String product, int quantity, double price)
{
if (items == null) items = new ArrayList<Item>();
Item item = new Item();
item.setOrder(this);
item.setProduct(product);
item.setQuantity(quantity);
item.setPrice(quantity * price);
items.add(item);
total += quantity * price;
}
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy="order")
public Collection<Item> getItems()
{
return items;
}
public void setItems(Collection<Item> items)
{
this.items = items;
}
}
/*----------------------- Order.java ---------------------*/
/*----------------------- Item.java ---------------------*/
package ejb3.joeyta.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
@SuppressWarnings("serial")
@Entity // 表示此为 Entity Beans
public class Item implements java.io.Serializable
{
private int id;
private double price;
private int quantity;
private String product;
private Order order;
@Id @GeneratedValue(strategy=GenerationType.AUTO)
// AUTO 定义由 persistence provider 决定怎样产生 ID
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public double getPrice()
{
return price;
}
public void setPrice(double price)
{
this.price = price;
}
public int getQuantity()
{
return quantity;
}
public void setQuantity(int quantity)
{
this.quantity = quantity;
}
public String getProduct()
{
return product;
}
public void setProduct(String product)
{
this.product = product;
}
@ManyToOne // 定义 Many-To-One 关系
@JoinColumn(name = "order_id") // 定义使用 foreign key, 即 order_id 对应到 Order 里的 id
public Order getOrder()
{
return order;
}
public void setOrder(Order order)
{
this.order = order;
}
}
/*----------------------- Item.java ---------------------*/
以上 Order 与 Item 为 One-To-Many 的关系.
使用 EJB3 Annotation 比 设定 Hibernate mapping file 还要轻松.
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy="order")
类似 Hibernate,
OneToMany 表示为 One-To-Many mapping.
cascade 定义 Order 的每个 DML 是否关联至 Item.
cascade = CascadeType.ALL 表示所有DML均关联, 例如 Order delete 时, 所有相关的 Item 亦会 delete.
fetch 可定义 lazy initialization
fetch = FetchType.EAGER 表示不使用 lazy initialization,
例如当 load Order 时, 所有相关 Item 也一起 load 出来. 如使用 LAZY 刚 Item 使用时才 load 出来.
mappedBy 定义 Order 与 Item 相向关系, Persistence manager 根据此值了解相向对应关系.
mappedBy="order" 表示 Item 里的 order property 与此对应, 即 Hibernate 里的 inverse. 防止 duplication DML.
这里如果没明确设定 @Table , Persistence manager 就会根据 Class 名来建立 Table.
这里如果没明确设定 @Column , Persistence manager 就会根据 Class 里的 Property 名来建立 column.
这里如果不使用 EJB3 Annotation,
Persistence provider 预设会搜寻 META-INF 下的 orm.xml
亦可以在 META-INF/persistence.xml 里设定 mapping file orm.xml. 然后在 orm.xml 里设定 OR Mapping.
jboss.xml 则可设定 JNDI XML Binding.
[4] 建立 D:/eclipse_wtp/workspace/ShoppingCartEJB3/src/META-INF/persistence.xml:
<!--------------------- persistence.xml -------------------->
<?xml version="1.0" encoding="UTF-8"?>
<persistence>
<persistence-unit name="joeyta">
<jta-data-source>java:/DefaultDS</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
</properties>
</persistence-unit>
</persistence>
<!--------------------- persistence.xml -------------------->
这个档案定义资料库的相关部署.
joeyta 为 unit name, EntityManager 需要指定相应的 unit name.
java:/DefaultDS 表示使用 D:/jboss/server/default/deploy 下 xxxxx-ds.xml 结尾资料库设定档对应的 jndi name.
这实作里使用 JBoss 预设的 HSQLDB, 即 D:/jboss/server/default/deploy/hsqldb-ds.xml
<property name="hibernate.hbm2ddl.auto" value="create-drop" /> 定义:
当project deploy 时就会自动产生 database schema. 当project undeploy 时就会自动清除 database schema.
[5] 建立 Shopping cart Stateless Session Beans:
/*----------------------- ShoppingCartDAO.java ---------------------*/
package ejb3.joeyta.sessions;
import java.util.List;
public interface ShoppingCartDAO {
public List findByProduct(String product);
public List loadAllOrders();
}
/*----------------------- ShoppingCartDAO.java ---------------------*/
/*----------------------- ShoppingCartDAOBean.java ---------------------*/
package ejb3.joeyta.sessions;
import java.util.List;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Stateless // 定义 Stateless Session Beans
@Remote(ShoppingCartDAO.class) // 定义 ShoppingCartDAO.class 为 Remote interface
public class ShoppingCartDAOBean {
@PersistenceContext(unitName = "joeyta")
private EntityManager manager;
// 这里定义将 EntityManager inject 进来. unitName="joeyta" 为上面定义的 persistence.xml
public List findByProduct(String product) {
return manager.createQuery("from Item o where o.product = :product")
.setParameter("product", product).getResultList();
}
public List loadAllOrders(){
return manager.createQuery("from Order").getResultList();
}
}
/*----------------------- ShoppingCartDAOBean.java ---------------------*/
[6] 建立 Shopping cart Stateful Session Beans:
/*----------------------- ShoppingCart.java ---------------------*/
package ejb3.joeyta.sessions;
import ejb3.joeyta.domain.Order;
public interface ShoppingCart {
public void buy(String product, int quantity, double price);
public Order getOrder();
public void checkout();
}
/*----------------------- ShoppingCart.java ---------------------*/
/*----------------------- ShoppingCartBean.java ---------------------*/
package ejb3.joeyta.sessions;
import javax.ejb.Remote;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import ejb3.joeyta.domain.Item;
import ejb3.joeyta.domain.Order;
@SuppressWarnings("serial")
@Stateful // 定义此为 Stateful Session Beans
@Remote(ShoppingCart.class) // 定义 ShoppingCart.class 为 Remote interface
public class ShoppingCartBean implements ShoppingCart, java.io.Serializable {
@PersistenceContext(unitName = "joeyta")
private EntityManager manager;
// 这里定义将 EntityManager inject 进来. unitName="joeyta" 为上面定义的 persistence.xml
private Order order;
public void buy(String product, int quantity, double price) {
if(order == null) order = new Order();
order.addPurchase(product, quantity, price);
}
public Order getOrder() {
return order;
}
@Remove // @Remove 表示当呼叫 checkout(), 就会清除这个 Instance
public void checkout() {
manager.persist(order);
System.out.println("checkout");
System.out.println("*******************************");
System.out.println("Print total order list on server:");
for(Item item : order.getItems()){
System.out.println("Item:" + item.getProduct() + " Price:" + item.getPrice());
}
System.out.println("Order id:" + order.getId() + " Total Price: " + order.getTotal());
}
}
/*----------------------- ShoppingCartBean.java ---------------------*/
[7] 建立客户端测试程式:
/*----------------------- ShoppingCartClient.java ---------------------*/
package ejb3.joeyta.clients;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import javax.naming.InitialContext;
import ejb3.joeyta.domain.Item;
import ejb3.joeyta.domain.Order;
import ejb3.joeyta.sessions.ShoppingCart;
import ejb3.joeyta.sessions.ShoppingCartDAO;
public class ShoppingCartClient {
public static InitialContext getInitialContext() throws javax.naming.NamingException {
// 这里亦可在 CLASSPATH 下建立 jndi.properties
Properties p = new Properties();
p.put(InitialContext.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
p.put(InitialContext.URL_PKG_PREFIXES, " org.jboss.naming:org.jnp.interfaces");
p.put(InitialContext.PROVIDER_URL, "jnp://localhost:1099");
return new javax.naming.InitialContext(p);
}
public static void main(String[] args) throws Exception {
InitialContext ctx = getInitialContext();
ShoppingCart cart = (ShoppingCart) ctx.lookup("ShoppingCartBean/remote");
System.out.println("Buying Cake");
cart.buy("Cake", 1, 3.9); // 买cake
System.out.println("Buying Bread");
cart.buy("Bread", 1, 2.9); // 买bread
System.out.println("Checkout");
cart.checkout(); // 结帐
ShoppingCartDAO cartDAO = (ShoppingCartDAO) ctx.lookup("ShoppingCartDAOBean/remote");
System.out.println("*******************************");
System.out.println("Print total order list on client:"); // 列印所有 order list
List orders = cartDAO.loadAllOrders();
for (Iterator iter = orders.iterator(); iter.hasNext();) {
Order order = (Order) iter.next();
for(Item item : order.getItems()){
System.out.println("Item:" + item.getProduct() + " Price:" + item.getPrice());
}
System.out.println("Order id:" + order.getId() + " Total Price: " + order.getTotal());
}
System.out.println("*******************************");
System.out.println("Print cake order list on client:"); // 列印所有 cake order list
List orderList = cartDAO.findByProduct("Cake");
for (Iterator iter = orderList.iterator(); iter.hasNext();) {
Item element = (Item) iter.next();
System.out.println(element.getProduct() + " "
+ element.getQuantity() + " " + element.getPrice());
}
}
}
/*----------------------- ShoppingCartClient.java ---------------------*/
这里当 选购项目时使用 Shopping Cart 的 Statefull Session Beans.
列印己选购项目使用 DAO (Data Access Model) 的 Stateless Session Beans.
项目结构如下所示:
[8] 使用 ANT 建立 EJB-JAR 并执行 ShoppingCartClient 程式:
在 ShoppingCartEJB3 project 下建立 build.xml [ ANT build File ]
内容如下:
<!------------------------- build.xml ------------------------------->
<?xml version="1.0"?>
<project name="JBoss" default="run.client" basedir=".">
<property environment="env" />
<property name="src.dir" value="${basedir}/src" />
<property name="resources" value="${basedir}/META-INF" />
<property name="jboss.home" value="${env.JBOSS_HOME}" />
<property name="classes.dir" value="bin" />
<path id="classpath">
<fileset dir="${jboss.home}/client">
<include name="**/*.jar" />
</fileset>
<pathelement location="${classes.dir}" />
<pathelement location="${basedir}/client-config" />
</path>
<target name="clean">
<delete file="${basedir}/ShoppingCart.jar" />
<delete file="${jboss.home}/server/default/deploy/ShoppingCart.jar" />
</target>
<target name="ejbjar" depends="clean">
<jar jarfile="ShoppingCart.jar">
<fileset dir="${classes.dir}">
<include name="ejb3/joeyta/domain/*.class" />
<include name="ejb3/joeyta/sessions/*.class" />
<include name="META-INF/*.xml" />
</fileset>
</jar>
<copy file="ShoppingCart.jar" todir="${jboss.home}/server/default/deploy" />
</target>
<target name="run.client">
<java classname="ejb3.joeyta.clients.ShoppingCartClient" fork="yes" dir=".">
<classpath refid="classpath" />
</java>
</target>
</project>
<!------------------------- build.xml ------------------------------->
build.xml 项目:
classpath 描述所需的 libraries 及 classes 的位置.
clean 清除项目所产生的 ShoppingCart.jar 及 JBoss deploy 里的 ShoppingCart.jar
ejbjar 产生 ShoppingCart.jar
run.ShoppingCartClient 执行 Client 测试程式 ShoppingCartClient
点选 build -> Run As -> 3. Ant Build ->> ejbjar
JBoss 自动 deploy 后就会产生相应的 Database Schema.
Eclipse Console 输出为
Buildfile: D:/eclipse_wtp/workspace/ShoppingCartEJB3/build.xml
clean:
[delete] Deleting: D:/eclipse_wtp/workspace/ShoppingCartEJB3/ShoppingCart.jar
[delete] Deleting: D:/jboss/server/default/deploy/ShoppingCart.jar
ejbjar:
[jar] Building jar: D:/eclipse_wtp/workspace/ShoppingCartEJB3/ShoppingCart.jar
[copy] Copying 1 file to D:/jboss/server/default/deploy
BUILD SUCCESSFUL
Total time: 4 seconds
JBoss Console 输出为
15:48:14,835 INFO [SchemaExport] Running hbm2ddl schema export
15:48:14,835 INFO [SchemaExport] exporting generated schema to database
15:48:14,850 INFO [SchemaExport] schema export complete
15:48:14,850 INFO [NamingHelper] JNDI InitialContext properties:{java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory, java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces}
15:48:14,866 INFO [JmxKernelAbstraction] installing MBean: jboss.j2ee:jar=ShoppingCart.jar,name=ShoppingCartBean,service=EJB3 with dependencies:
15:48:14,866 INFO [JmxKernelAbstraction] persistence.units:jar=ShoppingCart.jar,unitName=joeyta
15:48:14,975 INFO [EJBContainer] STARTED EJB: ejb3.joeyta.sessions.ShoppingCartBean ejbName: ShoppingCartBean
15:48:14,991 INFO [JmxKernelAbstraction] installing MBean: jboss.j2ee:jar=ShoppingCart.jar,name=ShoppingCartDAOBean,service=EJB3 with dependencies:
15:48:14,991 INFO [JmxKernelAbstraction] persistence.units:jar=ShoppingCart.jar,unitName=joeyta
15:48:15,022 INFO [EJBContainer] STARTED EJB: ejb3.joeyta.sessions.ShoppingCartDAOBean ejbName: ShoppingCartDAOBean
15:48:15,053 INFO [EJB3Deployer] Deployed: file:/D:/jboss/server/default/deploy/ShoppingCart.jar
点选 build -> Run As -> 3. Ant Build ->> run.client
Eclipse Console 输出为
Buildfile: D:/eclipse_wtp/workspace/ShoppingCartEJB3/build.xml
run.client:
[java] log4j:WARN No appenders could be found for logger (org.jboss.remoting.Client).
[java] log4j:WARN Please initialize the log4j system properly.
[java] Buying Cake
[java] Buying Bread
[java] Checkout
[java] *******************************
[java] Print total order list on client:
[java] Item:Cake Price:3.9
[java] Item:Bread Price:2.9
[java] Order id:1 Total Price: 6.8
[java] *******************************
[java] Print cake order list on client:
[java] Cake 1 3.9
BUILD SUCCESSFUL
Total time: 8 seconds
如下图所示:
JBoss Console 输出为
15:49:58,379 INFO [STDOUT] checkout
15:49:58,379 INFO [STDOUT] *******************************
15:49:58,379 INFO [STDOUT] Print total order list on server:
15:49:58,379 INFO [STDOUT] Item:Cake Price:3.9
15:49:58,379 INFO [STDOUT] Item:Bread Price:2.9
15:49:58,379 INFO [STDOUT] Order id:1 Total Price: 6.8
如下图所示:
进入 HSQL Database Manager 后, 执行下面 DML.
select * from purchase_order x,item y where x.id = y.order_id;
就会出现如下图所示:
EJB3 使用 POJOs (Plain Old Java Object), Java annotation 及 依赖注入设计模式.
使代码分离及便于测试.
在 multi tier 的企业应用系统中, 典型地分为两种 objects.
Application logic components 及 Persistent data objects.
前者主要是处理 business processes. 如计算结帐价格.
后者则是处理生命周期较长的 business data. 如将结帐价格存入资料库.
Entity Beans 为 ORM (Object Relational Mapping) 的实作.
是一种模组化并对应关系式资料库. 它是一个完全 POJO 为基楚的储存框架,
当开发资料库相关的应用程式时, 开发员典型地需要管理两种不同的 model 结构.
第一种 model 结构为 java objects ,
java objects model 资料存于内存中, 整个应用程式建构于这些资料 objects,
Java 编程语言及其 APIs 就是设计成有效率地控制这些 objects.
开发员则利用这些 APIs 来开发应用程式.
第二种则为关联式 model 结构.
资料存于关联式资料库里, 有利于分布式环境及有效率地提高大量资料的搜寻.
处理这些 java objects 及 关联式资料表 需要不同的语法及 APIs,
因此开发员需要同时模组化相同的资料两次, 及处理两种资料所在的系统及中间的转换,
这是非常没有效率的事情.
Entity beans 则是设计成处理这两种资料的桥梁, 并且它是简单的 java objects,
可以在其上利用标记简单地定义如何存入资料库,
而这些 objects 及 relational 资料映射关系自动地交由 EJB3 容器处理,
开发员则不用关心资料库的结构, 管理及定义访问的APIs.
而 EntityManager 提供的访问接口及使用 EJB QL 来处理这些 objects instance.
Entity beans 及其 callback methods 的生命周期:
可以在 entity beans 定义下面的标记, 容器会自动管理及唤起这些被标记的 methods.
@PrePersist: 此标记为当 entity 存入在资料库 前 被唤起.
@PostPersist: 此标记为当 entity 存入在资料库 后 被唤起.
@PreRemove: 此标记为当 entity 在资料库里删除 前 被唤起.
@PostRemove: 此标记为当 entity 在资料库里删除 后 被唤起.
@PreUpdate: 此标记为当 entity 在资料库里更新 前 被唤起.
@PostUpdate: 此标记为当 entity 在资料库里更新 后 被唤起.
@PostLoad: 此标记为当 entity 从资料库取得 后 被唤起.
@Remove: 这标记并不是 callback methods, 当应用程式呼叫此 method 时,
bean instance 会从 EntityManager managed context 里移除.
这时侯 bean 变成分离状况及不能使用.
亦可选择建立新 class 并标记这些 callback methods,
然后在 entity bean 里使用 @EntityListener 标记这新 class 为 callback listener.
如下例所示:
@EntityListener(CostomizeEntityListener.class)
为使这备忘记更简化, 故这里并不实作这些唤回函数.
本编主要介绍如何实作简单的 Entity Beans.
要熟识 Entity Beans, 需要了解的知识实在不少,
本编结尾提供连结, 可免费下载 Mastering EJB 3.0 PDF 版本.
开始备忘记:
这次备忘记以 Shopping Cart 作为例子, 是一种 One-To-Many 的映射关系实作.
------------ -----------
| | 1 * | |
| Order | <---------> | Item |
| | | |
------------ -----------
[1] Eclipse 启动 JBoss Server
[2] Eclipse 建立 Shopping Cart Project
[3] 建立 Shopping cart Entity Beans
[4] 建立 D:/eclipse_wtp/workspace/ShoppingCartEJB3/src/META-INF/persistence.xml
[5] 建立 Shopping cart Stateless Session Beans
[6] 建立 Shopping cart Stateful Session Beans
[7] 建立客户端测试程式
[8] 使用 ANT 建立 EJB-JAR 并执行 ShoppingCartClient 程式
[9] 使用 HSQL Database Manager 浏览资料库
[1] Eclipse 启动 JBoss Server:
Eclipse: Windows -> Show View -> Other
-->> JBoss-IDE -> Server Configuration 就会显示 JBoss Server Configuration console
然后 right client 它按 start , JBoss 就会启动, 如下图所示:
[2] Eclipse 建立 Shopping Cart Project:
Eclipse: File -> New -> Other -> EJB 3.0 -> EJB 3.0 Project
Project Name: ShoppingCartEJB3 -> Next
选择上一编已建立的 JBoss 4.0.x: jboss_configuration [default](not running)
打开后右键点选 JBoss 4.0.x -> new
然后按 Finish. ShoppingCartEJB3 project 就建立了
[3] 建立 Shopping cart Entity Beans:
/*----------------------- Order.java ---------------------*/
package ejb3.joeyta.domain;
import java.util.ArrayList;
import java.util.Collection;
import javax.persistence.CascadeType;
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;
@SuppressWarnings("serial")
@Entity // 表示此为 Entity Beans
@Table(name = "PURCHASE_ORDER") // 表示将建立的 Table 名为 PURCHASE_ORDER
public class Order implements java.io.Serializable
{
private int id;
private double total;
private Collection<Item> items;
@Id @GeneratedValue(strategy=GenerationType.AUTO)
// AUTO 定义由 persistence provider 决定怎样产生 ID
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public double getTotal()
{
return total;
}
public void setTotal(double total)
{
this.total = total;
}
public void addPurchase(String product, int quantity, double price)
{
if (items == null) items = new ArrayList<Item>();
Item item = new Item();
item.setOrder(this);
item.setProduct(product);
item.setQuantity(quantity);
item.setPrice(quantity * price);
items.add(item);
total += quantity * price;
}
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy="order")
public Collection<Item> getItems()
{
return items;
}
public void setItems(Collection<Item> items)
{
this.items = items;
}
}
/*----------------------- Order.java ---------------------*/
/*----------------------- Item.java ---------------------*/
package ejb3.joeyta.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
@SuppressWarnings("serial")
@Entity // 表示此为 Entity Beans
public class Item implements java.io.Serializable
{
private int id;
private double price;
private int quantity;
private String product;
private Order order;
@Id @GeneratedValue(strategy=GenerationType.AUTO)
// AUTO 定义由 persistence provider 决定怎样产生 ID
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public double getPrice()
{
return price;
}
public void setPrice(double price)
{
this.price = price;
}
public int getQuantity()
{
return quantity;
}
public void setQuantity(int quantity)
{
this.quantity = quantity;
}
public String getProduct()
{
return product;
}
public void setProduct(String product)
{
this.product = product;
}
@ManyToOne // 定义 Many-To-One 关系
@JoinColumn(name = "order_id") // 定义使用 foreign key, 即 order_id 对应到 Order 里的 id
public Order getOrder()
{
return order;
}
public void setOrder(Order order)
{
this.order = order;
}
}
/*----------------------- Item.java ---------------------*/
以上 Order 与 Item 为 One-To-Many 的关系.
使用 EJB3 Annotation 比 设定 Hibernate mapping file 还要轻松.
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy="order")
类似 Hibernate,
OneToMany 表示为 One-To-Many mapping.
cascade 定义 Order 的每个 DML 是否关联至 Item.
cascade = CascadeType.ALL 表示所有DML均关联, 例如 Order delete 时, 所有相关的 Item 亦会 delete.
fetch 可定义 lazy initialization
fetch = FetchType.EAGER 表示不使用 lazy initialization,
例如当 load Order 时, 所有相关 Item 也一起 load 出来. 如使用 LAZY 刚 Item 使用时才 load 出来.
mappedBy 定义 Order 与 Item 相向关系, Persistence manager 根据此值了解相向对应关系.
mappedBy="order" 表示 Item 里的 order property 与此对应, 即 Hibernate 里的 inverse. 防止 duplication DML.
这里如果没明确设定 @Table , Persistence manager 就会根据 Class 名来建立 Table.
这里如果没明确设定 @Column , Persistence manager 就会根据 Class 里的 Property 名来建立 column.
这里如果不使用 EJB3 Annotation,
Persistence provider 预设会搜寻 META-INF 下的 orm.xml
亦可以在 META-INF/persistence.xml 里设定 mapping file orm.xml. 然后在 orm.xml 里设定 OR Mapping.
jboss.xml 则可设定 JNDI XML Binding.
[4] 建立 D:/eclipse_wtp/workspace/ShoppingCartEJB3/src/META-INF/persistence.xml:
<!--------------------- persistence.xml -------------------->
<?xml version="1.0" encoding="UTF-8"?>
<persistence>
<persistence-unit name="joeyta">
<jta-data-source>java:/DefaultDS</jta-data-source>
<properties>
<property name="hibernate.hbm2ddl.auto" value="create-drop" />
</properties>
</persistence-unit>
</persistence>
<!--------------------- persistence.xml -------------------->
这个档案定义资料库的相关部署.
joeyta 为 unit name, EntityManager 需要指定相应的 unit name.
java:/DefaultDS 表示使用 D:/jboss/server/default/deploy 下 xxxxx-ds.xml 结尾资料库设定档对应的 jndi name.
这实作里使用 JBoss 预设的 HSQLDB, 即 D:/jboss/server/default/deploy/hsqldb-ds.xml
<property name="hibernate.hbm2ddl.auto" value="create-drop" /> 定义:
当project deploy 时就会自动产生 database schema. 当project undeploy 时就会自动清除 database schema.
[5] 建立 Shopping cart Stateless Session Beans:
/*----------------------- ShoppingCartDAO.java ---------------------*/
package ejb3.joeyta.sessions;
import java.util.List;
public interface ShoppingCartDAO {
public List findByProduct(String product);
public List loadAllOrders();
}
/*----------------------- ShoppingCartDAO.java ---------------------*/
/*----------------------- ShoppingCartDAOBean.java ---------------------*/
package ejb3.joeyta.sessions;
import java.util.List;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Stateless // 定义 Stateless Session Beans
@Remote(ShoppingCartDAO.class) // 定义 ShoppingCartDAO.class 为 Remote interface
public class ShoppingCartDAOBean {
@PersistenceContext(unitName = "joeyta")
private EntityManager manager;
// 这里定义将 EntityManager inject 进来. unitName="joeyta" 为上面定义的 persistence.xml
public List findByProduct(String product) {
return manager.createQuery("from Item o where o.product = :product")
.setParameter("product", product).getResultList();
}
public List loadAllOrders(){
return manager.createQuery("from Order").getResultList();
}
}
/*----------------------- ShoppingCartDAOBean.java ---------------------*/
[6] 建立 Shopping cart Stateful Session Beans:
/*----------------------- ShoppingCart.java ---------------------*/
package ejb3.joeyta.sessions;
import ejb3.joeyta.domain.Order;
public interface ShoppingCart {
public void buy(String product, int quantity, double price);
public Order getOrder();
public void checkout();
}
/*----------------------- ShoppingCart.java ---------------------*/
/*----------------------- ShoppingCartBean.java ---------------------*/
package ejb3.joeyta.sessions;
import javax.ejb.Remote;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import ejb3.joeyta.domain.Item;
import ejb3.joeyta.domain.Order;
@SuppressWarnings("serial")
@Stateful // 定义此为 Stateful Session Beans
@Remote(ShoppingCart.class) // 定义 ShoppingCart.class 为 Remote interface
public class ShoppingCartBean implements ShoppingCart, java.io.Serializable {
@PersistenceContext(unitName = "joeyta")
private EntityManager manager;
// 这里定义将 EntityManager inject 进来. unitName="joeyta" 为上面定义的 persistence.xml
private Order order;
public void buy(String product, int quantity, double price) {
if(order == null) order = new Order();
order.addPurchase(product, quantity, price);
}
public Order getOrder() {
return order;
}
@Remove // @Remove 表示当呼叫 checkout(), 就会清除这个 Instance
public void checkout() {
manager.persist(order);
System.out.println("checkout");
System.out.println("*******************************");
System.out.println("Print total order list on server:");
for(Item item : order.getItems()){
System.out.println("Item:" + item.getProduct() + " Price:" + item.getPrice());
}
System.out.println("Order id:" + order.getId() + " Total Price: " + order.getTotal());
}
}
/*----------------------- ShoppingCartBean.java ---------------------*/
[7] 建立客户端测试程式:
/*----------------------- ShoppingCartClient.java ---------------------*/
package ejb3.joeyta.clients;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import javax.naming.InitialContext;
import ejb3.joeyta.domain.Item;
import ejb3.joeyta.domain.Order;
import ejb3.joeyta.sessions.ShoppingCart;
import ejb3.joeyta.sessions.ShoppingCartDAO;
public class ShoppingCartClient {
public static InitialContext getInitialContext() throws javax.naming.NamingException {
// 这里亦可在 CLASSPATH 下建立 jndi.properties
Properties p = new Properties();
p.put(InitialContext.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
p.put(InitialContext.URL_PKG_PREFIXES, " org.jboss.naming:org.jnp.interfaces");
p.put(InitialContext.PROVIDER_URL, "jnp://localhost:1099");
return new javax.naming.InitialContext(p);
}
public static void main(String[] args) throws Exception {
InitialContext ctx = getInitialContext();
ShoppingCart cart = (ShoppingCart) ctx.lookup("ShoppingCartBean/remote");
System.out.println("Buying Cake");
cart.buy("Cake", 1, 3.9); // 买cake
System.out.println("Buying Bread");
cart.buy("Bread", 1, 2.9); // 买bread
System.out.println("Checkout");
cart.checkout(); // 结帐
ShoppingCartDAO cartDAO = (ShoppingCartDAO) ctx.lookup("ShoppingCartDAOBean/remote");
System.out.println("*******************************");
System.out.println("Print total order list on client:"); // 列印所有 order list
List orders = cartDAO.loadAllOrders();
for (Iterator iter = orders.iterator(); iter.hasNext();) {
Order order = (Order) iter.next();
for(Item item : order.getItems()){
System.out.println("Item:" + item.getProduct() + " Price:" + item.getPrice());
}
System.out.println("Order id:" + order.getId() + " Total Price: " + order.getTotal());
}
System.out.println("*******************************");
System.out.println("Print cake order list on client:"); // 列印所有 cake order list
List orderList = cartDAO.findByProduct("Cake");
for (Iterator iter = orderList.iterator(); iter.hasNext();) {
Item element = (Item) iter.next();
System.out.println(element.getProduct() + " "
+ element.getQuantity() + " " + element.getPrice());
}
}
}
/*----------------------- ShoppingCartClient.java ---------------------*/
这里当 选购项目时使用 Shopping Cart 的 Statefull Session Beans.
列印己选购项目使用 DAO (Data Access Model) 的 Stateless Session Beans.
项目结构如下所示:
[8] 使用 ANT 建立 EJB-JAR 并执行 ShoppingCartClient 程式:
在 ShoppingCartEJB3 project 下建立 build.xml [ ANT build File ]
内容如下:
<!------------------------- build.xml ------------------------------->
<?xml version="1.0"?>
<project name="JBoss" default="run.client" basedir=".">
<property environment="env" />
<property name="src.dir" value="${basedir}/src" />
<property name="resources" value="${basedir}/META-INF" />
<property name="jboss.home" value="${env.JBOSS_HOME}" />
<property name="classes.dir" value="bin" />
<path id="classpath">
<fileset dir="${jboss.home}/client">
<include name="**/*.jar" />
</fileset>
<pathelement location="${classes.dir}" />
<pathelement location="${basedir}/client-config" />
</path>
<target name="clean">
<delete file="${basedir}/ShoppingCart.jar" />
<delete file="${jboss.home}/server/default/deploy/ShoppingCart.jar" />
</target>
<target name="ejbjar" depends="clean">
<jar jarfile="ShoppingCart.jar">
<fileset dir="${classes.dir}">
<include name="ejb3/joeyta/domain/*.class" />
<include name="ejb3/joeyta/sessions/*.class" />
<include name="META-INF/*.xml" />
</fileset>
</jar>
<copy file="ShoppingCart.jar" todir="${jboss.home}/server/default/deploy" />
</target>
<target name="run.client">
<java classname="ejb3.joeyta.clients.ShoppingCartClient" fork="yes" dir=".">
<classpath refid="classpath" />
</java>
</target>
</project>
<!------------------------- build.xml ------------------------------->
build.xml 项目:
classpath 描述所需的 libraries 及 classes 的位置.
clean 清除项目所产生的 ShoppingCart.jar 及 JBoss deploy 里的 ShoppingCart.jar
ejbjar 产生 ShoppingCart.jar
run.ShoppingCartClient 执行 Client 测试程式 ShoppingCartClient
点选 build -> Run As -> 3. Ant Build ->> ejbjar
JBoss 自动 deploy 后就会产生相应的 Database Schema.
Eclipse Console 输出为
Buildfile: D:/eclipse_wtp/workspace/ShoppingCartEJB3/build.xml
clean:
[delete] Deleting: D:/eclipse_wtp/workspace/ShoppingCartEJB3/ShoppingCart.jar
[delete] Deleting: D:/jboss/server/default/deploy/ShoppingCart.jar
ejbjar:
[jar] Building jar: D:/eclipse_wtp/workspace/ShoppingCartEJB3/ShoppingCart.jar
[copy] Copying 1 file to D:/jboss/server/default/deploy
BUILD SUCCESSFUL
Total time: 4 seconds
JBoss Console 输出为
15:48:14,835 INFO [SchemaExport] Running hbm2ddl schema export
15:48:14,835 INFO [SchemaExport] exporting generated schema to database
15:48:14,850 INFO [SchemaExport] schema export complete
15:48:14,850 INFO [NamingHelper] JNDI InitialContext properties:{java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory, java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces}
15:48:14,866 INFO [JmxKernelAbstraction] installing MBean: jboss.j2ee:jar=ShoppingCart.jar,name=ShoppingCartBean,service=EJB3 with dependencies:
15:48:14,866 INFO [JmxKernelAbstraction] persistence.units:jar=ShoppingCart.jar,unitName=joeyta
15:48:14,975 INFO [EJBContainer] STARTED EJB: ejb3.joeyta.sessions.ShoppingCartBean ejbName: ShoppingCartBean
15:48:14,991 INFO [JmxKernelAbstraction] installing MBean: jboss.j2ee:jar=ShoppingCart.jar,name=ShoppingCartDAOBean,service=EJB3 with dependencies:
15:48:14,991 INFO [JmxKernelAbstraction] persistence.units:jar=ShoppingCart.jar,unitName=joeyta
15:48:15,022 INFO [EJBContainer] STARTED EJB: ejb3.joeyta.sessions.ShoppingCartDAOBean ejbName: ShoppingCartDAOBean
15:48:15,053 INFO [EJB3Deployer] Deployed: file:/D:/jboss/server/default/deploy/ShoppingCart.jar
点选 build -> Run As -> 3. Ant Build ->> run.client
Eclipse Console 输出为
Buildfile: D:/eclipse_wtp/workspace/ShoppingCartEJB3/build.xml
run.client:
[java] log4j:WARN No appenders could be found for logger (org.jboss.remoting.Client).
[java] log4j:WARN Please initialize the log4j system properly.
[java] Buying Cake
[java] Buying Bread
[java] Checkout
[java] *******************************
[java] Print total order list on client:
[java] Item:Cake Price:3.9
[java] Item:Bread Price:2.9
[java] Order id:1 Total Price: 6.8
[java] *******************************
[java] Print cake order list on client:
[java] Cake 1 3.9
BUILD SUCCESSFUL
Total time: 8 seconds
如下图所示:
JBoss Console 输出为
15:49:58,379 INFO [STDOUT] checkout
15:49:58,379 INFO [STDOUT] *******************************
15:49:58,379 INFO [STDOUT] Print total order list on server:
15:49:58,379 INFO [STDOUT] Item:Cake Price:3.9
15:49:58,379 INFO [STDOUT] Item:Bread Price:2.9
15:49:58,379 INFO [STDOUT] Order id:1 Total Price: 6.8
如下图所示:
进入 HSQL Database Manager 后, 执行下面 DML.
select * from purchase_order x,item y where x.id = y.order_id;
就会出现如下图所示:
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.