创建一个实用的Session Bean
HelloWorldSession例子的主要目的是帮助你熟悉一个session bean的整体结构。现在你已经熟悉了session bean的结构,你可以写一个更实用的bean了。特别地,你可以写一个由数据库中接收数据的bean。
以下的例子,假定你拥有一个SQL表格,里面包含有产品的代码和价格,你也可以使用以下SQL命令建立它:
create table price
(product_code varchar(10) not null primary key,
price decimal(10,2) not null)
Pricing session bean可以列出全部有效的产品代码,并且可以返回某个产品代码的价格,该代码由Remote接口指定,如6.9列表所示:
Listing 6.9 Source Code for Pricing.java
package usingj2ee.pricing;
import java.rmi.*;
import javax.ejb.*;
/** Defines the methods you can call on a Pricing session */
public interface Pricing extends EJBObject
{
/** Returns all the available product codes */
public String[] getProductCodes() throws RemoteException;
/** Returns the price for a specific product code */
public double getPrice(String productCode)
throws RemoteException, InvalidProductCodeException;
}
Pricing session bean并不需要记得某个客户的任何信息,所以可以用一个无状态的session bean实现。PricingHome接口如列表6.10所示,它仅需要一个create方法。
Listing 6.10 Source Code for PricingHome.java
package usingj2ee.pricing;
import java.rmi.*;
import javax.ejb.*;
/** Defines the methods for creating a Pricing session */
public interface PricingHome extends EJBHome
{
/** Creates a Pricing session bean */
public Pricing create() throws RemoteException, CreateException;
}
当一个session bean需要访问一个数据库连接时,它通常在setSessionContext方法中分配一个连接,最后在ejbRemote方法中释放它。当然,如果你拥有一个数据库的连接,在容器调用ejbPassivate方法时,你必须准备关闭它,在容器调用ejbActivate时,重新得到连接。
<!-- 分页 -->你将会发现大部分的EJB开发者使用一个方法来返回一个连接;这样你就以后就可以修改得到连接的方法,而不会影响使用这些连接的代码。你也应该使用DataSource对象来创建连接。DataSource让修改数据库的驱动变得容易,并且可以在需要的时候使用连接池。
列表6.11展示了Pricing session bean的实现类PricingImpl
Listing 6.11 Source Code for PricingImpl.java
package usingj2ee.pricing;
import java.rmi.*;
import java.util.*;
import javax.ejb.*;
import java.sql.*;
import javax.sql.*;
import javax.naming.*;
/** The implementation class for the Pricing bean */
public class PricingImpl implements SessionBean
{
/** The session context provided by the EJB container. A session bean must
hold on to the context it is given. */
private SessionContext context;
/** The database connection used by this session */
private Connection conn;
/** An EJB must have a public, parameterless constructor */
public PricingImpl()
{
}
/** Called by the EJB container to set this session's context */
public void setSessionContext(SessionContext aContext)
{
context = aContext;
}
/** Called by the EJB container when a client calls the create() method in
the Home interface */
public void ejbCreate()
throws CreateException
{
try
{
// Allocate a database connection
conn = getConnection();
}
catch (Exception exc)
{
throw new CreateException(
"Unable to access database: "+exc.toString());
}
}
/** Called by the EJB container to tell this session bean that it is being
suspended from use (it's being put to sleep). */
public void ejbPassivate()
throws EJBException
{
try
{
// Shut down the current database connection
conn.close();
conn = null;
}
catch (Exception exc)
{
throw new EJBException("Unable to close database connection: "+
exc.toString());
}
}
/** Called by the EJB container to wake this session bean up after it
has been put to sleep with the ejbPassivate method. */
public void ejbActivate()
throws EJBException
{
try
{
// When the bean wakes back up, get a database connection again
conn = getConnection();
}
catch (Exception exc)
{
throw new EJBException(
"Unable to access database: "+exc.toString());
}
}
/** Called by the EJB container to tell this session bean that it has been
removed, either because the client invoked the remove() method or the
container has timed the session out. */
public void ejbRemove()
throws EJBException
{
try
{
// Shut down the current database connection
conn.close();
conn = null;
}
catch (Exception exc)
{
throw new EJBException("Unable to close database connection: "+
exc.toString());
}
}
/** Returns a list of the available product codes */
public String[] getProductCodes()
throws EJBException
{
Statement s = null;
try
{
s = conn.createStatement();
ResultSet results = s.executeQuery(
"select product_code from price");
Vector v = new Vector();
// Copy the results into a temporary vector
while (results.next())
{
v.addElement(results.getString("product_code"));
}
// Copy the vector into a string array
String[] productCodes = new String[v.size()];
v.copyInto(productCodes);
return productCodes;
}
catch (Exception exc)
{
throw new EJBException("Unable to get product codes: "+
exc.toString());
}
finally
{
// Close down the statement in a finally block to guarantee that it gets
// closed, whether an exception occurred or not
try
{
s.close();
}
catch (Exception ignore)
{
}
}
}
/** Gets the price for a particular product code */
public double getPrice(String productCode)
throws EJBException, InvalidProductCodeException
{
PreparedStatement ps = null;
try
{
// It's always better to use a prepared statement than to try to insert
// a string directly into the query string. This way you don't have to
// worry if there's a quote in the product code
ps = conn.prepareStatement(
"select price from price where product_code = ?");
// Store the product code in the prepared statement
ps.setString(1, productCode);
ResultSet results = ps.executeQuery();
// If there are any results, get the first one (there should only be one)
if (results.next())
{
return results.getDouble("price");
}
else
{
// Otherwise, if there were no results, this product code doesn't exist
throw new InvalidProductCodeException(productCode);
}
}
catch (SQLException exc)
{
throw new EJBException("Unable to get price: "+
exc.toString());
}
finally
{
// Close down the statement in a finally block to guarantee that it gets
// closed, whether an exception occurred or not
try
{
ps.close();
}
catch (Exception ignore)
{
}
}
}
protected Connection getConnection()
throws SQLException, NamingException
{
// Get a reference to the naming service
InitialContext context = new InitialContext();
// Get the data source for the pricing database
DataSource ds = (DataSource) context.lookup(
"java:comp/env/jdbc/PriceDB");
// Ask the data source to allocate a database connection
return ds.getConnection();
}
}
PricingImpl中的getConnection值得留意一下。它使用JNDI(命名服务)来定位到一个名字为java:comp/env/jdbc/PriceDB的数据源。java:comp/env指的是你的session bean使用的JNDI naming context。当session bean被配置在一个EJB容器时,容器为你的bean设置了一个带有各个项目的naming context,这些项目是在你配置bean时建立的。java:comp/env naming context让你将逻辑名字和各种不同资源联系起来。这样你就可以在编写bean时,无需知道数据源或者Home接口的准确名字。当在配置bean到容器中时,设置bean使用的名字和真正资源名相关联。这样bean就并不绑定到某个特别的资源名字,可以提升bean的移植性。
在配置Pricing bean时,必须指定jdbc/PriceDB的一个别名。如果使用J2EE SDK带有的Cloudscape数据库,这个别名必须是jdbc/Cloudscape。否则,必须在EJB服务器中建立一个数据源,指向要使用的数据库。当配置Pricing bean时,指定jdbc/PriceDB引用的数据源名字。jdbc/PriceDB是一个逻辑名,Pricing bean可以使用多种不同的数据库,只要在配置bean时修改一下关联就可以了。
如果使用不同的数据源,可以在配置的时候修改它。还可以建立一个default.properties文件,里面包含有你需要使用的驱动和数据库信息。例如,对于Oracle数据库,可以使用以下的default.properties文件:
jdbc.drivers=oracle.jdbc.driver.OracleDriver
jdbc.datasources=jdbc/Oracle|jdbc:oracle:thin:@localhost:1521:orcl
这样就建立了一个可选的数据源,它的名字是jdbc/Oracle,jdbc/PriceDB关联可以修改以便使用Oracle数据源。再次说明的是,你并不需要修改Pricing bean,你只要修改配置属性。
假定正在运行J2EE SDK配置工具,在配置工具的资源引用部分(Resource References section)设置了jdbc/PriceDB naming项目,如图6.8所示。
********图6.8**************
资源引用对话框允许你设置一个session bean的naming context
Prcing bean和HelloWorldSession bean在配置上只有一点不同,这就是你必须在JNDI Names的标记页中指定jdbc/PriceDB的别名,如图6.9所示。
********图6.9**************
JNDI names页让你设置bean使用的各种JNDI别名
写一个客户来测试Pricing bean是很简单的,这个程序和你已经看到过的其它客户程序类似。列表6.12展示了Pricing的测试客户程序。
Listing 6.12 Source Code for TestPricing.java
package usingj2ee.pricing;
import java.util.*;
import javax.naming.*;
import javax.rmi.*;
public class TestPricing
{
public static void main(String[] args)
{
try
{
/** Creates a JNDI naming context for location objects */
Context context = new InitialContext();
/** Asks the context to locate an object named "Pricing" and expects the
object to implement the PricingHome interface */
PricingHome home = (PricingHome)
PortableRemoteObject.narrow(
context.lookup("Pricing"),
PricingHome.class);
/** Asks the Home interface to create a new session bean */
Pricing session = (Pricing) home.create();
/** Get a list of valid product codes */
String[] codes = session.getProductCodes();
for (int i=0; i < codes.length; i++)
{
System.out.println(codes[i]+": "+
session.getPrice(codes[i]));
}
try
{
session.getPrice("f00b4r");
}
catch (InvalidProductCodeException exc)
{
System.out.println("Got invalid product code exception: "+
exc.toString());
}
/** Destroy this session */
session.remove();
}
catch (Exception exc)
{
exc.printStackTrace();
}
}
}
最后,列表6.10展示了pricing测试客户的输出。要注意的是,无论是在源代码或者输出中,都没有地方显示该bean由一个数据库中得到数据。
<!-- 分页 -->
***********图6.10*******************
客户并不知道session bean由数据库中得到数据
注意
可以使用以下命令为pricing数据库加入数据
INSERT INTO price (product_code, price) VALUES ('A1', 1.59);
问题解答
配置问题
Q:为什么配置工具不产生EAR或者JAR文件?
A:在实现类中实现的方法,有可能违反了EJB的限制或者要求。许多配置工具都带有一个选项,可测试兼容性。例如,在J2EE SDK中,这个选项被称为Verifier;在WebLogic的配置工具中,它被称为Check Compliance。而且对windows的检查也可能包含有错误的信息。某些工具并不会一直弹出窗口告诉你哪里错了,你可以检查各种的log文件,它们放在J2EE SDK的子目录logs中。
Q:为什么工具不产生客户JAR文件?
A:除了Remote和Home接口类外,可以不需要其它的东西,所以无需要工具创建JAR文件。不过在大多数的情况下,客户端的JAR文件是必需的,这是因为配置工具会产生一些用作客户端开发的实用类。
运行时的问题
Q:为什么我的客户端程序不能定位JNDI naming服务?
A:首先,要确认你的EJB服务器正在运行。接着,需要在命令行定义初始的naming context factory 类。查看你的EJB服务器的文档,看你是否需要其它的命令行选项。还有,确定bean是被配置了的。如果使用配置工具来配置bean,然后重新启动服务器,你的服务器可能已经忘掉了那个bean。这时你可以尝试重新配置。还有,确认客户程序请求的名字和你为bean配置的JNDI名字是一样的,必须完全一致,包括大小写。
Q:为什么在访问数据源时出现错误?
A:可能没有为你的EJB服务器设置好数据源,也可能是没有正确地设置关联,即EJB使用的逻辑名和JNDI目录使用的真正数据源名。
分享到:
相关推荐
3. **JSP与JavaBean整合**:阐述如何在JSP页面中使用JavaBean,包括实例化Bean、设置和获取属性值,以及使用session和request范围存储Bean。 4. **EL和JSTL**:介绍EL表达式语言,它是JSP中简化数据访问的工具,...
"jscn_weblogic(循序渐进)"很可能是针对初学者的一个学习资源包,旨在帮助用户逐步掌握WebLogic的各项功能和操作。 在深入探讨WebLogic之前,我们需要了解Java EE(Java Enterprise Edition)的概念,它是Java...
【Jsp+JavaBean循序渐进教程】深入解析 Jsp+JavaBean技术是Java Web开发中的核心组合,主要用于构建动态网页应用。本教程旨在逐步引导读者理解并掌握JavaBean的基本原理及其在Jsp中的具体应用。 1. **JavaBean的...
理解JNDI(Java Naming and Directory Interface)查找机制对于在J2EE环境中使用JDBC至关重要。 3. **Java技术分析**: 这部分可能涵盖了Java的基础语法、面向对象编程概念、异常处理、多线程、集合框架、IO流、...
对于初学者,理解J2EE的关键在于循序渐进。首先,学习基础的Java语法,然后深入理解Servlet和JSP,接着接触EJB和JMS的概念。同时,了解容器如何管理和部署这些组件,以及JNDI和JTA在其中的作用。随着经验的增长,...
本书从最基础的JSP开发开始,循序渐进地介绍了JSP 开发技术,并涵盖了许多高级主题,如需要在企业级Web应用中使用的特性—Enterprise JavaBeans、JDBC 2.0、数据库连接池和自定义标签库。本书既适合初学者阅读,也...
J2EE的学习是一个循序渐进的过程,初学者应从基础入手,逐步深入到高级主题。这个资料集合提供了从基础到精深的知识体系,对于想要踏入J2EE领域的学习者来说,是一份非常有价值的参考资料。通过系统地学习和实践,你...
8. **Spring与Hibernate集成**:Spring通过HibernateTemplate和SessionFactoryBean等类简化了Hibernate的使用,它处理了Session的生命周期和异常,使得开发者可以专注于业务逻辑,而不必关心底层持久化细节。...
《Java Web开发技术大全:JSP+Servlet+Struts+Hibernate+Spring+Ajax》通过对SSH中的各种技术循序渐进地讲解,使读者尽快掌握开发基于SSH的Web程序的方法。《Java Web开发技术大全:JSP+Servlet+Struts+Hibernate+...
《Java Web开发技术大全:JSP+Servlet+Struts+Hibernate+Spring+Ajax》通过对SSH中的各种技术循序渐进地讲解,使读者尽快掌握开发基于SSH的Web程序的方法。《Java Web开发技术大全:JSP+Servlet+Struts+Hibernate+...