Entity Bean 的基础理论
1.1 什么是Entity Bean
Entity Bean(实体Bean)是持久数据组件,代表存储在外部介质中的持久(Persistence)对象或者已有的企业应用系统资源。简单地讲,一个Entity Bean可以代表数据库中的一行记录,多个客户端应用能够以共享方式访问表示该数据库记录的Entity Bean。
那么什么是持久数据组件呢?为什么要使用持久数据组件?理解了这两个问题,也就清楚了Entity Bean的性质及用途。
持久数据组件指的是这样一种对象,它们知道如何将自身放入持久存储的空间中。它们使用一些持久机制,比如序列化、O/R映射。这种对象代表数据,例如使用持久数据组件代表下面这些信息:
● 银行帐号信息,例如帐号、密码和余额;
● 员工信息,例如姓名、部门和工资。
为什么要把这些数据以对象的方式进行处理而不是直接处理数据库中的原始数据,比如相关的记录呢?答案是将数据视为对象是非常便利的,因为能够方便地操作和管理对象,并且它们表现为一个紧凑的形式,此外,通过组件所在的应用服务器,可以获得事务、安全性等服务。
Entity Bean就是这种持久数据组件。Entity Bean知道怎样在一个诸如数据库的存储空间中永久保存自己。Entity Bean以域(field)的方式存储数据,例如银行帐号、密码和余额。依赖于EJB容器提供的事务服务,多个客户端应用能够在保持数据库记录的一致性和完整性前提下实现对数据资源的共享。Entity Bean的生命期相对较长并且其状态是持续的。只要Entity Bean代表的数据库记录存在,该组件对象实例就一直存在,即使EJB容器崩溃,Entity Bean仍然具有生命力。
1.2 Entity Bean的子类型
按照Entity Bean持久性的实现形式,可以将Entity Bean分为容器管理持久性(CMP Container-Managed Persistence)和组件管理持久性(BMP Bean-Managed Persistence)两种模型。在CMP类型EJB组件的实现代码中,组件开发人员不需要为组件的持久性控制方法编写任何数据库操作的代码,而是在组件组装和部署过程中由部署工具自动创建;如果要创建BMP类型EJB组件,则组件程序设计人员需要为所有持久性方法编写控制代码。
1.3 Entity Bean的特点
与数据库中的数据记录相对应,每个实体类型EJB组件包含一个主键(Primary Key)标识,该标识与组件代表的数据库记录主键相同。客户端应用可以利用该主键定位EJB容器中的Entity Bean对象实例,进而定位组件代表的数据库记录。
实体类型EJB组件的主要特点包括:
● Entity Bean提供数据库中记录的视图;
● Entity Bean具有无限制生命期,EJB服务器崩溃也不会影响Entity Bean的存在;
● 多个Entity Bean可以对应同一数据库记录;
● EJB服务器可以利用Entity Bean的passivate方法将Entity Bean缓存到临时存储空间中,同样可以利用activate方法将缓存的Entity Bean重新读入EJB容器并恢复组件对象实例;
● 客户端应用可以利用在Entity Bean的Home接口中定义的创建、删除和查询等方法对组件进行管理。
使用EJB QL开发查询
2.1 什么是EJB QL
在关系数据库的操作中查询是经常使用的,主要是通过select语句完成的。Entity Bean作为代表数据库中数据的持久性组件也同样需要查询操作,即能够找到符合某一查询条件的Entity Bean的实例。Entity Bean的查询操作是通过定义finder()方法完成的。对于CMP,定义finder()方法仅仅是声明一个方法,指明finder()方法的参数,该参数通常与查询条件中的参数对应,真正完成查询的动作是由EJB容器完成的。EJB容器要读取部署描述文件ejb-jar.xml(在*.jar/META-INF里)中的<query>项,该项包含了与finder()方法相对应的查询语句。<query>项中的查询语句遵循的语法规范是EJB QL。
EJB QL的开发查询步骤如下:
1> 在Home接口中增加finder()方法,其参数为查询条件中用到的参数;
2> 在ejb-jar.xml文件的<query>项定义EJB QL语句。
EJB QL是EJB2.0新加入的特性,它实现了如何在Home接口中定义各种查找方法。它以SQL-92为基础,可以由容器自动编译,这使得Entity Bean具有更高的可移植性,并且容易部署。EJB QL语句由select、where、orderby三个子句组成,其后两个子句是可选的。
EJB QL查询语句举例如下:
例1:
SELECT stu FROM Student AS stu WHERE stu.grade > 5
该查询语句的含义是查询grade>5的Student Bean实例。“Student”是抽象模式名(Abstract schema name),在ejb-jar.xml文件中<abstract schema name>项指定的名称。“stu”是Student的别名,引入别名的好处是可以引用所代表对象的字段。stu.grade表示Student的grade字段,称为路径表达式。
例2:
SELECT i FROM Student As i WHERE i.name = ?1
该查询语句的含义是查找名字与finder()方法中的第一个参数相同的Student Bean实例。
WHERE字句的使用说明:
★ 以?n代表相应的finder()方法的输入参数;
★ 字符串类型的值要用单引号括起来(如果值中有单引号,则用双引号代替)WHERE语句中可以使用的表达式和运算符如下:
☆ +,-,*,/,=,<,<=,>=,>,<>,NOT,AND,OR
☆ between
☆ like
☆ in
☆ member of
☆ is null(is not null)
内置函数:
● CONCAT(String first,String second)
● SUBSTRING(String source,int start,int length)
● LOCATE(String source,String patter)
● LENGTH(String source)
实体Bean的BMP和CMP选择
EJB有两种主要类型BMP(Bean managed persistence )和CMP(Container managed persistence ),这两种类型各有优缺点。
BMP是在Bean中完成对数据库JDBC的各种调用,也就是说,在你的实体bean(entity bean)中,明确写入了SQL语句,如"insert .. "或"select ..",并且使用Datasource获得一个数据库资源以及连接(connection)从而对数据库直接进行增加 删除修改。
CMP是由EJB容器自动完成对数据库的操作,你所有做的,就是在实体bean重写入SetXXX或getXXX方法,然
后在ejb-jar.xml中定义cmp-field就可以。
很明显,CMP编写要简单多,而且数据库操作由EJB容器完成应该是一种趋势,但是CMP有个缺点就是不够灵
活,如果我们要完成类似SQL搜索语句的like命令,如"select * from A where name like '%banqiao'",CMP就无法自动帮助我们完成,这样我们就需要BMP自己来写。
在实际应用,一般为了效率考虑,我们尽量使用CMP,但如何为将来有可能使用BMP作好准备,就是说有可
以延伸到BMP的基础。EJB 2.0对CMP的抽象类支持为我们提供了这种实现的基础。
总体思路是,先使用抽象类完成CMP 如果需要BMP 可以extend这个抽象类,然后覆盖原来的方法(用自己的特殊SQL语句操作来覆盖该方法)。
以Java 宠物店(Java Pet Store Demo 1.3)中的地址实体bean:AddressEJB为例:
public abstract class AddressEJB implements EntityBean {
private EntityContext context = null;
// getters and setters for PO CMP fields
public abstract String getFirstName();
public abstract void setFirstName(String name);
public abstract String getLastName();
public abstract void setLastName(String name);
public abstract String getStreet1();
public abstract void setStreet1(String name);
public abstract String getStreet2();
public abstract void setStreet2(String name);
public abstract String getCity();
public abstract void setCity(String name);
public abstract String getState();
public abstract void setState(String name);
public abstract String getCountry();
public abstract void setCountry(String name);
public abstract String getZip();
public abstract void setZip(String name);
public Object ejbCreate(String fName, String lName, String s1,String s2, String cy, String st,String cnty, String pcode)
throws CreateException {
setFirstName(fName);
setLastName(lName);
setStreet1(s1);
setStreet2(s2);
setCity(cy);
setState(st);
setCountry(cnty);
setZip(pcode);
return null;
}
public void ejbPostCreate(String fName, String lName, String street1,String street2, String city, String state,String country, String zip) throws CreateException{}
public void setEntityContext(EntityContext c){ context = c; }
public void unsetEntityContext(){}
public void ejbRemove() throws RemoveException {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void ejbStore() {}
public void ejbLoad() {}
}
在上面的AddressEJB中,我们看到只有setXXX或getXXX的方法。
在相应的部署描述文件ejb-jar.xml中我们看到:
<entity>
<display-name>AddressEJB</display-name>
<ejb-name>AddressEJB</ejb-name>
<local-home>com.sun.j2ee.blueprints.address.ejb.AddressLocalHome</local-home>
<local>com.sun.j2ee.blueprints.address.ejb.AddressLocal</local>
<ejb-class>com.sun.j2ee.blueprints.address.ejb.AddressEJB</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.Object</prim-key-class>
<reentrant>False</reentrant>
<cmp-version>2.x</cmp-version>
<abstract-schema-name>Address</abstract-schema-name>
<cmp-field>
<field-name>firstName</field-name>
</cmp-field>
<cmp-field>
<field-name>lastName</field-name>
</cmp-field>
<cmp-field>
<field-name>street1</field-name>
</cmp-field>
<cmp-field>
<field-name>street2</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>country</field-name>
</cmp-field>
<cmp-field>
<field-name>zip</field-name>
</cmp-field>
<security-identity>
<description></description>
<use-caller-identity></use-caller-identity>
</security-identity>
</entity>
在上面部署文件中,标明了Address数据库字段:
firstName,lastName,street1,street2,city,state,country,zip
一旦我们要使用BMP, 只要继承上面的CMP bean:
public class AddressBeanBMP extends AddressEJB {
用我们自己的BMP方法覆盖AddressEJB中的方法:
ejbLoad() -->从数据库中获取数据(SELECT)
ejbStore() -->修改数据库数据UPDATE)
ejbRemove() -->删除数据库数据(DELETE)
ejbCreate() -->插入新的数据记录(INSERT)
ejbFindByPrimaryKey(primary key) --> 确保 primary key 存在.
ejbFindAllPrimaryKey() -->自己的定义 返回一个primary key所有数据记录的collectionxiam
下面以ejbCreate()为例:
public Object ejbCreate(String fName, String lName, String s1,String s2, String cy, String st,
String cnty, String pcode) throws CreateException {
// insert row into database
this.fName = fName;
this.lName = lName;
this.s1 = s1;
this.s2 = s2;
this.cy=cy;
this.st=st;
this.cnty=cnty;
this.pcode=pcode;
// Insert database record
try {
Connection connection = getConnection();
PreparedStatement statement = connection.prepareStatement
("INSERT INTO Address (firstName,lastName,street1,street2,city,state,country,zip) VALUES (?, ?, ?,?,?,?)");
statement.setString(1, fName);
statement.setString(2, lName);
statement.setString(3, pcode);
statement.setString(4, s1);
statement.setString(5, s2);
statement.setString(6, st);
statement.setString(7, cy);
statement.setString(8, cnty);
if (statement.executeUpdate() != 1) {
statement.close();
connection.close();
throw new CreateException("Could not create: " );
}
statement.close();
connection.close();
}
catch(SQLException e) {
throw new EJBException("Could not create: " );
}
}