当前位置:天才代写 > tutorial > JAVA 教程 > 关于EJB的耐久化工具技能阐明

关于EJB的耐久化工具技能阐明

2017-11-11 08:00 星期六 所属: JAVA 教程 浏览:552

副标题#e#

表格型的干系型数据库与树型Java工具之间的映射问题是一个至今争论不休的问题,亏得此刻已经有了一些好的办理方案。在本文中,我们将先容EJB技能是奈何用自已特定的方法来办理这个问题的。

只要是涉及到生存及查询信息 ,那绝大大都应用措施都需要与干系数据库打交道。但由于干系数据库与Java工具在布局上有着本质的区别,关于它们之间的映射干系对付那些Java开拓者们来说,是一个很令人头痛的问题。干系型数据库是以表格方法存储数据的,而Java工具是以树型方法表示的。这种范例上的不匹配激发了各类百般的工具耐久化办理方案,用来缩小干系世界与工具世界之间的鸿沟。EJB框架正是这种办理方案之一。

工具的耐久性

今朝有许多差异的东西呈现,使得开拓人员可以将JAVA工具转化为数据库中的字段或记录,可能将数据库中的字段或记录还原为JAVA工具。这些处理惩罚涉及到要将树型的JAVA工具序列化到数据库中,反之亦然。此事情的焦点是奈何在担保最小机能损失的前提下,来完成这项事情。

EJB框架提供了这样一个工具耐久化机制。我们将在本文中接头这种机制,但首先我们照旧对EJB构架作一个全面的认识。

企业级JavaBeans(EJB)

EJB技能可以这样界说:它是一个基于JAVA处事端的,为漫衍式应用提供的一个可复用的组件框架。所有的贸易逻辑、陈设界说、工具耐久性都由这个框架统一打点,EJB框架的一些特色如下:

· EJB是一种运行在处事端情况下的JAVA工具。

· EJB能漫衍在差异的呆板长举办长途会见,但对客户端来而言,挪用EJB与挪用当地JavaBean一样利便。

· EJB容器对EJB举办统一打点。

尽量企业级JavaBean的名字与普通JavaBean在定名上有些相像,但它们在设计上有着本质上的区别。为了能让你更清楚地认识到这点,我们最好先相识一下EJB的根基观念、几种EJB组件模式和其设置情况。

EJB的运行情况

从本质上来说,EJB只是实现了特定接口的普通JAVA工具,但这个工具必需运行在一个特定的情况:EJB容器中。假如离开了EJB容器,EJB是无法运行的。EJB与EJB容器之间的干系有时候被称为"反向挪用"――可能叫"好莱坞道理"(别接洽我,到时候我会给你打电话的)。

EJB容器是一种用来打点EJB的运行时情况。它容纳并打点差异范例的EJB,这与JAVA servlet容器打点servlet有些雷同。EJB容器认真初始化EJB,并给其提供系统级的处事。

当客户端措施要挪用某一个EJB时并不直接与EJB打交道,客户端与EJB被容器断绝起来。

EJB容器提供的处事

当开拓者建设一系列的类与接口,用来构成一个EJB时,容器会为他们提供如下的系统级处事:

· 事务处理惩罚

· 安详打点

· EJB的耐久化打点

· EJB的长途会见

· EJB的生命周期打点

· 数据库毗连池

· EJB的实例池打点

由于EJB容器认真为EJB提供这种底层处事,使得一个EJB开拓者只需存眷详细应用的贸易逻辑,从而淘汰了许多不须要的贫苦。

EJB的范例

EJB类型界说了以下三种差异范例的EJB范例:

· 动静驱动EJB(MDB)

· 会话EJB

· 实体EJB

当客户端与会话EJB或实体EJB交互时,它们的通信方法是同步通信。而动静驱动EJB(MDB)则只与JMS举办交互,它相当于JMS中的一个宣布/订阅主题。


#p#副标题#e#

动静驱动EJB

动静驱动EJB事情在异步通信模式下。一个动静驱动EJB充当一个动静侦听者的脚色,它侦功用JMS的宣布/订阅主题中传来的动静。

EJB容器打点着动静驱动EJB的生命周期,然而与会话EJB和实体EJB差异之处在于客户端并不能直接挪用它的要领。动静驱动EJB是通过一个名为onMessage的回调函数来吸收客户端的动静的。

会话EJB

会话EJB的特点是不能同时被多个客户端共享。当客户端挪用会话EJB的要领时,先颠末EJB容器处理惩罚,然后再由容器对会话EJB举办挪用。会话EJB处理惩罚开拓者编写贸易逻辑,容器再将处理惩罚功效返回给客户端。会话EJB不能在多个会话中耐久生存。它分为两种范例:有状态的会话EJB和无状态的会话EJB。

有状态的会话EJB

当一个客户端与某一个有状态的会话EJB开启一个会话时,这个EJB为客户端维护了一个会话状态。这体现着客户端向此EJB发出差异的挪用请求之间担保EJB的成员变量值不会丢失。

一旦客户端竣事与有状态的会话EJB的交互后,EJB容器会自动销毁它。于是整个会话竣事,而且此有状态的会话EJB所生存的状态数据会全部丢失。

无状态会话EJB

#p#分页标题#e#

无状态会话EJB并不为客户端生存任何状态数据。你可以这样认为:客户端每次对无状态会话EJB的挪用城市发生一个新的EJB实例,因此所有的状态信息都不会生存。 同样,EJB容器也不会耐久化任何无状态会话EJB,因此开拓者必需意识到客户端与无状态会话EJB之间举办交互时,所有的状态数据都是姑且的。无状态会话EJB的这种特性使得容器可以反复地利用它的实例,因此无状态会话EJB能获得比有状态会话EJB更好的机能。

实体EJB

实体EJB表达的的是一种耐久存储的贸易逻辑,凡是存储于干系型数据库中。实体EJB与干系型数据库有如下的相似之处:

· 实体EJB是耐久的――它可以在应用措施的生命周期之外存在,甚至可以在EJB容器的生命周期以外存在。

· 实体EJB答允共享会见――多个客户端可以共享同一个实体EJB,而容器认真打点它们之间的同步。

· 实体EJB有主键――主键用来确定实体EJB的一个独一实例,操作它可以找到一个特定的耐久化实体。

· 实体EJB有事务的观念――由于客户端能并发会见并修改它的数据,因此事务打点长短常重要的。事务打点属性被显示地界说在陈设描写文件中,而容器认真打点事务的界线。

要实现工具-干系映射,那实体EJB必需能提供插入、更新、查询、删除的操纵。而用于打点实体EJB工具与数据源之间的映射的进程被称为耐久化。换句话说,耐久化是一个将信息写入外部数据源的一个进程。EJB类型界说了实体EJB的两种耐久化方法:Bean自身打点的耐久化(BMP)和容器打点的耐久化(CMP)。

Bean自身打点的耐久化(BMP)

假如你选用BMP,那你必需在你的代码中认真维护所有的耐久化发。那么所有的数据层会见代码都必需由开拓者来完成,这种方法能带给开拓者更大的机动性。

容器打点的耐久化(CMP)

假如你选用CMP,那你不消编写数据层会见代码,EJB容器将会为你打点所有的耐久化。因此,数据层会见代码与数据源之间是松耦合的。这能减轻开拓者的代码编写量,而且使得CMP能陈设到差异厂商的应用处事器中,也不必体贴详细的数据源(拜见图1)。

关于EJB的经久化东西技术阐发

图1 会话EJB与实体EJB的干系:此图显示了EJB容器在客户端与EJB实例中充当的署理脚色。

#p#副标题#e#

EJB陈设与运行时情况

我们将以JBoss3.0.2作为EJB陈设与运行时情况的处事器。我们将设计一个简朴的WEB应用,它答允建设用户帐号,用户通过会见WEB欣赏器,而WEB欣赏器通过挪用一个servlet来取得这个帐号,这个servlet与一个实体EJB彼此通信(拜见图2)。

关于EJB的经久化东西技术阐发

图2.通过web会见EJB:此图显示了一个客户端请求是奈何从客户端传到应用层的。当这个作为节制器的servlet吸收到客户请求后,它将这个请求转化一个业务请求并向业务处事层挪用相应的处事。业务处事层利用一个或多个实体EJB来从数据层中取得或生存数据。

编写并陈设一个实体EJB

以下四个步调是开拓一个实体EJB的典范流程:

1. 为你的实体EJB编写相应的类及接口

2. 编写相应的陈设描写文件

3. 将实体EJB及相应的陈设描写文件打包成为一个jar文件

4. 陈设此实体EJB

一个实体EJB至少由以下三个类(接口)构成:

组件接口――在本例中我们只思量从同一JVM虚拟机中会见实体EJB,因此我们需要担任javax.ejb.EJBLocalObject接口。

2. package com.jeffhanson.datatier.ejb;
3.
4. import javax.ejb.EJBLocalObject;
5.
6. public interface LocalUser extends EJBLocalObject
7. {
8. public String getUserID(); //主键
9. public String getFullName();
10. public String setAddress(String address);
11. public String getAddress();
12. public String setCity(String city);
13. public String getCity();
14. public String setState(String state);
15. public String getState();
16. public String setZip(String zip);
17. public String getZip();
}
18. Home接口――同样,由于我们处于同一JVM虚拟机中,因此我们需要担任javax.ejb.EJBLocalHome接口。
19. package com.jeffhanson.datatier.ejb;
20.
21. import javax.ejb.CreateException;
22. import javax.ejb.FinderException;
23. import javax.ejb.EJBLocalHome;
24. import java.util.Collection;
25. public interface LocalUserHome extends EJBLocalHome
26. {
27. public LocalUser create(String userID,
28. String fullName,
29. String address,
30. String city,
31. String state,
32. String zip)
33. throws CreateException;
34.
35. public Collection findByFullName(String fullName)
36. throws FinderException;
37.
38. public LocalUser findByPrimaryKey(String userID)
39. throws FinderException;
}

#p#副标题#e#

Bean类――假如你要开拓会话EJB,那么需要实现javax.ejb.SessionBean接口,假如是实体EJB,那么需要实现javax.ejb.EntityBean接口(拜见列表1)。

列表1. EJB类

#p#分页标题#e#

package com.jeffhanson.datatier.ejb;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;
import javax.ejb.CreateException;
import java.util.Locale;
public class UserEJB
implements EntityBean
{
 // 地域缺省设为美国英语
 private Locale locale = Locale.US;
 transient private EntityContext ctx;
 public String USERID;
 public String FULLNAME;
 public String ADDRESS;
 public String CITY;
 public String STATE;
 public String ZIP;
 public UserEJB()
 {}
 public void setLocale(Locale locale)
 {
  this.locale = locale;
 }
 //会见CMP域相关的要领
 public void setUserID(String userID)
 {
  USERID = userID;
 }
 public String getUserID() //主键
 {
  return USERID;
 }
 public void setFullName(String fullName)
 {
  FULLNAME = fullName;
 }
 public String getFullName()
 {
  return FULLNAME;
 }
 public void setAddress(String address)
 {
  ADDRESS = address;
 }
 public String getAddress()
 {
  return ADDRESS;
 }
 public void setCity(String city)
 {
  CITY = city;
 }
 public String getCity()
 {
  return CITY;
 }
 public void setState(String state)
 {
  STATE = state;
 }
 public String getState()
 {
  return STATE;
 }
 public void setZip(String zip)
 {
  ZIP = zip;
 }
 public String getZip()
 {
  return ZIP;
 }
 public String ejbCreate(String userID,
  String fullName,
  String address,
  String city,
  String state,
  String zip)
 {
  System.out.println("ejbCreate called with userID: " + userID);
  setUserID(userID);
  setFullName(fullName);
  setAddress(address);
  setCity(city);
  setState(state);
  setZip(zip);
  return userID;
 }
 public void ejbPostCreate(String userID,
  String fullName,
  String address,
  String city,
  String state,
  String zip)
 throws CreateException
 {
  // 容器在挪用ejbCreate()要领后会自动挪用它
  System.out.println("ejbPostCreate called with userID: " + userID);
 }
 public void setEntityContext(EntityContext ctx)
 {
  this.ctx = ctx;
  System.out.println("setEntityContext called");
 }
 public void unsetEntityContext()
 {
  ctx = null;
 }
 public void ejbActivate()
 {
  // 当此EJB被载入内存之前容器会自动挪用它
 }
 public void ejbPassivate()
 {
  // 当此EJB被互换入牢靠存储器之前
  // 容器会自动挪用它
 }
 public void ejbLoad()
 {
  // 容器挪用,用来更新实
  // 体EJB的状态
 }
 public void ejbStore()
 {
  // 容器挪用,用来将实体
  // EJB的状态存储入数据库中
 }
 public void ejbRemove()
 {
  // 当实体EJB从数据中删除之前被
  // 容器挪用
 }
}

#p#副标题#e#

陈设描写文件

要将你开拓的EJB陈设到EJB容器中去的话,那你必需为此容器提供一个陈设描写文件。陈设描写文件是一个XML名目标文档,文件名为ejb-jar.xml,内里包括有Bean的耐久范例以及事务属性。你必需将这个文件与编写的Java类一起打包到一个jar或ear文件中去。

ejb-jar.xml是由SUN公司提供的一个尺度陈设描写文件,JBoss尚有别的一个名为jaws.xml的陈设描写文件(拜见列表2),这个文件描写了CMP界说以及其耐久属性。

列表2. The ejb-jar.xml Deployment Descriptor

#p#分页标题#e#

<?xml version="1.0"?>
<!DOCTYPE ejb-jar PUBLIC
"-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN"
"http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd">
<ejb-jar>
<display-name>Users</display-name>
<enterprise-beans>
<entity>
<description>关闭一个用户工具</description>
<ejb-name>UserEJB</ejb-name>
<local-home>com.jeffhanson.datatier.ejb.LocalUserHome</local-home>
<local>com.jeffhanson.datatier.ejb.LocalUser</local>
<ejb-class>com.jeffhanson.datatier.ejb.UserEJB</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.String</prim-key-class>
<reentrant>False</reentrant>
<cmp-field><field-name>USERID</field-name></cmp-field>
<cmp-field><field-name>FULLNAME</field-name></cmp-field>
<cmp-field><field-name>ADDRESS</field-name></cmp-field>
<cmp-field><field-name>CITY</field-name></cmp-field>
<cmp-field><field-name>STATE</field-name></cmp-field>
<cmp-field><field-name>ZIP</field-name></cmp-field>
<primkey-field>USERID</primkey-field>
</entity>
</enterprise-beans>
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>UserEJB</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>

列表2描写了主键的名字和范例,以及实体EJB的哪些域将被耐久化。假如干系数据库中没有一个名为"UserEJB"的表的话,那容器会自动成立一个相应的表。

在列表3中,每一个域都界说了一个相应的cmp-field元素。当容器自动成立一个新表时,它会由此得知要建设那些新的字段,并将域与特定的字段对应起来。

列表3. jaws.xml陈设描写文件

<?xml version="1.0" encoding="ISO-8859-1"?>
<jaws>
<datasource>java:/DefaultDS</datasource>
<type-mapping>Hypersonic SQL</type-mapping>
<enterprise-beans>
<entity>
<ejb-name>UserEJB</ejb-name>
<create-table>true</create-table>
<table-name>UserEJB</table-name>
<remove-table>false</remove-table>
<tuned-updates>false</tuned-updates>
<read-only>false</read-only>
<time-out>300</time-out>
<cmp-field>
<field-name>USERID</field-name>
<column-name>USERID</column-name>
</cmp-field>
<cmp-field>
<field-name>FULLNAME</field-name>
<column-name>FULLNAME</column-name>
</cmp-field>
<cmp-field>
<field-name>ADDRESS</field-name>
<column-name>ADDRESS</column-name>
</cmp-field>
<cmp-field>
<field-name>CITY</field-name>
<column-name>CITY</column-name>
</cmp-field>
<cmp-field>
<field-name>STATE</field-name>
<column-name>STATE</column-name>
</cmp-field>
<cmp-field>
<field-name>ZIP</field-name>
<column-name>ZIP</column-name>
</cmp-field>
</entity>
</enterprise-beans>
</jaws>

UserEJB的陈设要领如下:先将陈设描写文件与编译好的类一起打成一个jar包,然后将这个包放在JBoss处事器的deploy目次下就可以了,JBoss会自动发明这个包,并自动举办陈设。

#p#副标题#e#

EJB客户端

在本例中,你可以在同一JVM虚拟机中会见EJB。这种设计简化了我们的一些事情,要得到EJB的home接口,我们需要举办一个非凡的下溯造型。列表4简述了UserService工具,它用来会见我们的EJB。

列表4. UserService工具

public class UserService
{
 private static LocalUserHome home = null;
 private static LocalUserHome getUserHome()
 throws NamingException
 {
  if (home != null)
  {
   return home;
  }
  // 取得一个上下文
  InitialContext ctx = new InitialContext();
  // 取得对UserEJB的一个引用
  System.out.println("Looking up EJB...");
  Object objRef = ctx.lookup("local/UserEJB");
  // 取得UserEJB的home接口
  home = (LocalUserHome)objRef;
  return home;
 }
 public static UserInfo getUser(String userID)
 {
  UserInfo userInfo = null;
  try
  {
   LocalUserHome home = getUserHome();
   LocalUser localUser = null;
   try
   {
    System.out.println("Finding user...");
    localUser = home.findByPrimaryKey(userID);
   }
   catch (FinderException e)
   {
   }
   if (localUser == null)
   {
    System.out.println("Creating user...");
    localUser = home.create(userID,"John " + userID + " Doe","123 anywhere st.","Seattle","WA","87654");
   }
   System.out.println("EJB returned User ID: " + localUser.getUserID());
   userInfo = convertToUserInfo(localUser);
   System.out.println("User FullName: " + localUser.getFullName());
  }
  catch (Exception e)
  {
   System.err.println(e.toString());
  }
  return userInfo;
 }
 private static UserInfo convertToUserInfo(LocalUser localUser)
 {
  UserInfo userInfo;
  userInfo = new UserInfo();
  userInfo.setId(localUser.getUserID());
  userInfo.setFullName(localUser.getFullName());
  userInfo.setAddress(localUser.getAddress());
  userInfo.setCity(localUser.getCity());
  userInfo.setState(localUser.getState());
  userInfo.setZip(localUser.getZip());
  return userInfo;
 }
}

#p#分页标题#e#

请留意在列表4中,UserService工具首先试着通过findByPrimaryKey()要领来找到一个EJB的实例,并在这个要领中通报一个用户ID的参数。假如没有找到实体EJB的实例,UserService工具将挪用EJB的home接口中的create()要领来建设一个新的EJB实例。

findByPrimaryKey()要领在挪用进程中首先被EJB容器拦截,EJB容器试着从数据库中找出一个与用户ID沟通主键的记录。create()要领将在数据库中插入一个主键便是用户ID的记录。

#p#副标题#e#

EJB查询语言(EJB QL)

EJB类型提供了一种名为EJB QL的工具查询语言,用来查询CMP。这种工具查询语言实际上被容器转化为SQL语言。

EJB QL语言用来实现EJB的home接口中的find()查询要领,并执行实体EJB界说的一些内部的select要领。这些EJB QL一般放在应用措施的陈设描写文件中。

以下XML代码(选自于jaws.xml文件)界说了在UserEJB的home接口中findByState()要领与相应EJB QL之间的映射。这个示例措施用这个EJB QL查询语言及相应的find()要领寻找给出状态的某个用户。

<entity>
...
<query>
<query-method>
<method-name>findByState</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</query-method>
<ejb-ql>
[!CDATA[
SELECT DISTINCT object(u)
FROM UserEJB u
WHERE u.STATE = ?1]] </ejb-ql>
</query>
</entity>

在以上的界说傍边,ejb-ql标签界说了实际利用的EJB QL。当你只想查找一个特定范例的工具时,必需要在查询语言中插手object要害字。别的这个查询中的"?1"的浸染雷同于JDBC中的PrepareStatement,表白这个查询是一个参数化查询。假如有多个参数,则依次为"?2"、"?3"以此类推,这些参数与查询要领findByState()中的参数表从"?1"开始一一对应。

在上例中,"?1"将被findByState()要领的第一个名为state的参数所代替。除此之外,CMP尚有一个名为findAll()或findByPrimaryKey()的内置要领,你只需要界说它们,而不需实现任何代码,EJB容器会为你自动生成关于这两个要领的代码。

在列表5中,UserService类界说了一个名为getUsersByState()的要领。这个要领挪用UserEJB的home接口上的findByState()要领,EJB容器会拦截这个要领,然后执行界说在jaws.xml文件中的EJB QL查询语言。

列表5. 通过用户状态来查找用户信息

public static UserInfo[] getUsersByState(String state)
{
 UserInfo[] users = null;
 // 找出所有收入高于John的职员
 try
 {
  LocalUserHome home = getUserHome();
  Collection userList = home.findByState(state);
  System.out.println("Found userList");
  if (userList != null)
  {
   users = new UserInfo[userList.size()];
   int i = 0;
   Iterator iter = userList.iterator();
   while (iter.hasNext())
   {
    LocalUser localUser = (LocalUser)iter.next();
    users[i++] = convertToUserInfo(localUser);
   }
  }
 }
 catch (NamingException e)
 {
  System.err.println(e.toString());
 }
 catch (FinderException e)
 {
  System.err.println(e.toString());
 }
 return users;
}

#p#分页标题#e#

由于Java树型工具与表格型的干系数据库中在构架上的差别,对付开拓者来说,将Java工具耐久化到干系型数据库中这一事情是一件很是巨大的工作。它们之间的这种差别激发了若干种工具耐久技能的发生,以使得干系型世界与工具世界之间的鸿沟日益缩小。EJB框架提供了一个基于容器打点的耐久化机制,假如利用恰当,将会是一种提供应开拓者的优秀办理方案

 

    关键字:

天才代写-代写联系方式