Workflow Project 目前状态
版本 0.11

已经完成
1。完成了接口1 和接口2 的方法
2。完成接口3的默认实现
3。完成事务回滚的实现方法-等待测试

未完成
1。接口3的注册与实例化解决方案
2。应用的并发访问问题以及解决数据的脏读问题
3。与具体的某个应用挂接并测试


-事务的回滚
OSWorkFlow的事务回滚是依靠WorkflowContext这个接口来实现的,在New出某个WorkFlow的时候需要声明WorkflowContext的实现类,一般会采用uper.context = new GearWheelWorkFlowContext(_caller);方法
比如这样实现:

public GearWheelWorkFlow(String _caller)
{
super.context = new GearWheelWorkFlowContext(_caller);
}

但OSWorkFlow的WorkflowContext的默认实现BasicWorkFlowContext中根本没有实现setRollbackOnly方法,也就没有了参考的可能

再看看这个接口的其他实现类也都是建立在JTA这样的跨Session的事务服务上,比如它的EJB的实现也是要调用容器提供的JTA实现才行!而JTA的实现比如要JNDI到数据库池,此时的应用光JTA+JNDI就已经宣布 -这样的例子必须生存在应用服务器的环境下!!

可是,我不死心,我记得Hibernate可以实现本地事务,也就是依靠JDBC本身的事务处理能力,而要实现这样的功能就需要在数据库连接的获取上下一些功夫,也就是要保证回滚的数据库连接必须是获取时的那个连接,而存储连接就成了一个需要首先解决的问题。

解决数据库连接的存储问题
目前存储数据库连接除了依靠静态类外,还有一个通用的方法ThreadLocal类,这样获取数据库连接的方法写成了如下的形式:

package com.company.common;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.Logger;
public class DB2ConnectFactory
{
/**
* Logger for this class
*/
private static final Logger logger = Logger.getLogger(DB2ConnectFactory.class);
private static ThreadLocal threadLocal = new ThreadLocal();
//~
private Connection connect = null;
private Statement state = null;
private ResultSet result = null;
private boolean closeConnWhenDone = false;
//~
private String url = "jdbc:db2:WORKFLOW";
private String user = "";
private String password = "";
private String driverClassName = "COM.ibm.db2.jdbc.app.DB2Driver";

public DB2ConnectFactory() throws SQLException
{
this.init();
}

/**
* 获取数据库连接
* @return
* @throws SQLException
*/
public Connection getConn() throws SQLException
{
return (Connection)threadLocal.get();
}

/**
* 初始化数据库,并在缓冲中注册数据库连接
* @throws SQLException
*/
private void init() throws SQLException
{
try
{
// Get connect object
Class.forName(driverClassName);
closeConnWhenDone = true;

connect = DriverManager.getConnection(url, user, password);
state = connect.createStatement();

//Register the connection object in the threadlocal
threadLocal.set(connect);
}
catch (Exception e)
{
e.printStackTrace();
throw new SQLException(e.getMessage());
}
}

}

解决事务回滚
刚才说了需要实现WorkflowContext接口
package com.company.engine.workflow;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.log4j.Logger;
import com.company.common.DB2ConnectFactory;
import com.opensymphony.workflow.WorkflowContext;
public class GearWheelWorkFlowContext implements WorkflowContext
{
/**
* Logger for this class
*/
private static final Logger logger = Logger.getLogger(GearWheelWorkFlowContext.class);

private static ThreadLocal threadLocal = new ThreadLocal();

// ~ Instance fields
// ////////////////////////////////////////////////////////
private String caller;
// ~ Constructors
// ///////////////////////////////////////////////////////////
public GearWheelWorkFlowContext(String caller)
{
this.caller = caller;
}
// ~ Methods
// ////////////////////////////////////////////////////////////////
public String getCaller()
{
return this.caller;
}
/**
* Tranaction : Set Roll back
* @throws SQLException
*/
public void setRollbackOnly()
{
Connection connect = null;
try
{
DB2ConnectFactory factory = new DB2ConnectFactory();
connect = factory.getConn();
if(connect != null) connect.rollback();
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
this.clostConnection(connect);
}
}

private void clostConnection(Connection connect)
{
try
{
if(connect != null) connect.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}

总结
1。我们可以看到由于接口中setRollbackOnly没有异常的声明,方法中即使抛出了异常也要自己"忍了"!看来良好的接口声明其实是非常重要的。

2。而且需要重载原来JDBCWorkflow 中的cleanup方法,将其中的代码屏蔽掉!数据库的关闭放在了setRollbackOnly访访的finally中,原因就是由于我们要统一的管理数据库连接所引发的,我们不能够在WorkFlowStore的每一个方法执行完毕后就关闭连接,因为这样的话你根本没有了事务回滚的可能,所以此时的连接需要在WorkflowContext中来处理。


感触
OSWorkFlow的实现方法并不是像网上所说的那样的优秀和文雅,更像是一个未完成任务的"半成品",Heni被网上鼓吹为大牛,但一个不写注释和文档的人,根本称不上什么大牛!
OSWorkFlow更多的是实现了一个微内核,而它的用户模式是与OSUser这样的框架耦合的(偶已经将这样的耦合打开了,也就是接口3的定义),它的相关数据是与PropertySet框架耦合的(也就是接口2的定义),而且采用OSWorkFlow要经过很原始的修改(比如我实现了DB2下的WorkFlowStore的实现)。

不过也好即使以后不采用OSWorkFlow,自己实现一个这样的引擎也应该没有什么问题的,有时间了我倒是很想看看别的工作流的产品。

posted @ 2006-03-02 21:03 killvin| 编辑 收藏

workflow接口划分

1。应用接口 Application Interface
--interface1 工作流自身提供的服务接口
--interface2 工作流与应用之间的接口(主要是提供相关数据的调用接口)

2。扩展接口 PlugIn Interface
--interface3 工作流与组织机构之间的接口
--interface4 工作流与其他工作流之间的接口

将接口划分成应用接口与扩展接口主要是依据工作流与相关应用的调用关系,比如工作流与组织机构之间,是工作流调用组织机构中的人员信息,所以主动者是WORKFLOW、被动方是组织机构,所以应该采用扩展接口来实现

在扩展接口上应该采用Adapter模式,从而使工作流不局限于某个特定的实现

目前的进展
0。Application Interface接口已经基本实现了
PlugIn Interface接口目前基本完工,但OSWorkflow的实现实在是非常的丑陋,需要更改的地方太多,而且对于Interface3不可以使用它采用的User / Group模型(而且它使用了OSUser这个框架,对于多数的应用程序基本可以说不适合,而且它的User类竟然是Final ?!而且我发现它的很多类的属性都是Protected!也就是说除了他们自己根本没有办法扩展,即使扩展也是很丑陋的方式)

1。现在最大的问题是它的WorkStore接口的扩展,我采用DB2的方式实现了它的接口,但这样的方式会与DB2绑定在一起,如果自己写实现就要根据不同的DB采用不同的SQL语言-也就是不同的方言策略?!而且考虑到性能估计不是什么好主意,看来明天需要更换成HibernateWorkStore的形式,这样工作流的持久层接口将工作在Hibernate之上,看来很完美的解决了这个问题。

2。而且我扩展了它的PropertySet,使其不再依靠JNDI寻找DataSource,而是通过嵌入在程序内部采用JDBC的形式寻找数据库连接,这样我就不必为了验证一个问题去建立那该死的数据库缓冲池了(而且JNDI的形式也就不可避免的要用到容器,太重了!)

3。我编写了UserGroupCondition的实现类,这个类的作用就是调用Interface3的方法,从而判断某个用户是否属于某个组(现在的做法是让WorkStore实现Interface3的偷懒办法,但很乱,看来还是要写一个Adapter去实现interface3才对!)

4。目前工作流引擎的工厂类已经实现完工并测试通过。


用了近一个月的时间完成了这些工作,看起来很少但是基本上大量的时间花费在熟悉工作流规范、WFMC标准、以及学习和扩展OSWorkflow接口上,不过对OSWorkflow的实现基本上掌握了,如果抛开OSWorkflow自己也可以采用自己的方式去实现,或者会考虑使用Spring的方式(Interface3的Adapter不行就采用Spring实现)。

BTW:
OSWorkflow的实现其实比较的丑陋!而且编码根本没有什么规范,接口的定义也是天马行空,看来Heni除了他的大嘴外应该好好的提高自己的技术修养。-实在不敢恭维这位"大师"的编码水平!

posted @ 2006-03-02 21:03 killvin| 编辑 收藏

在Workflow事务回滚中遇到了问题,是这样的

DB2ConnectFactory 中getConn方法
/**
* 获取数据库连接
* @return
* @throws SQLException
*/
public Connection getConn() throws SQLException
{
Object obj = threadLocal.get();
if(obj == null)
{
this.initFactoryStack();
}else
{
connect = (Connection)obj;
}
connect.setAutoCommit(false); //事务的回滚必须建立在将Commit状态为False下,默认是true
logger.debug("Get connect from factory - " + connect.hashCode());
return connect;
}

AbstractWorkflow 的doAction()方法


try {
//transition the workflow, if it wasn't explicitly finished, check for an implicit finish
if (!transitionWorkflow(entry, currentSteps, store, wf, action, transientVars, inputs, ps)) {
checkImplicitFinish(id);
}
} catch (WorkflowException e) {
context.setRollbackOnly(); // 这里调用WorkContext对象的setRollbackOnly()方法,执行事务的回滚
throw e;
}

GearWheelWorkFlowContext 的setRollbackOnly方法
/**
* Tranaction : Set Roll back
* @throws SQLException
*/
public void setRollbackOnly()
{
logger.debug("Context execute setRollbackOnly() !!");
Connection connect = null;
try
{
DB2ConnectFactory factory = new DB2ConnectFactory();
connect = factory.getConn();
logger.debug("Context get connect " + connect.hashCode());

if(connect != null) connect.rollback();
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
this.clostConnection(connect); //这里将关闭数据库连接
}
}


可是这是"异常"情况下的处理流程,如果正常执行呢?
刚开始我想写在CleanUp()方法里,但又一想不行,因为正常执行的流程需要做两个工作
1。将Commit状态更新为true,并提交连接
2。关闭数据库连接


关键就是关闭数据库的连接在哪里写?!现在写在CleanUp()不合适,因为每一个WorkStore的方法都要默认(程序已经写死了,我可不想重载它的所有的方法!!)的关闭数据库连接!
仔细的分析了一下,其实有两个方法可以做到
1。编写Proxy类
2。重载所有AbstractWorkflow中设计到事务的方法,(本来可以重载transitionWorkflow但是方法的类型却为private?!)在它的方法下增加一个"提交"的方法。比如这样

try {
//transition the workflow, if it wasn't explicitly finished, check for an implicit finish
if (!transitionWorkflow(entry, currentSteps, store, wf, action, transientVars, inputs, ps))
{
checkImplicitFinish(id);
}

dosubmit();

} catch (WorkflowException e) {
context.setRollbackOnly(); // 这里调用WorkContext对象的setRollbackOnly()方法,执行事务的回滚
throw e;
}

可以看到方法2比较"烂",看来下一步即使编写方法1的实现


posted @ 2006-03-02 21:02 killvin| 编辑 收藏

早上的时间被该死的WorkflwoStore里的主键生成策略("主键生成策略"来源于Hibernate文档),该死的Sequence,从文档资料上看到DB2是支持Sequence的,按照db2的文档我执行了如下的语句:
create sequence seq_os_wfentry start with 10 increment by 10;
create sequence seq_os_currentsteps;
执行-ok

可是我以前不太了解Sequence的概念,这片资料倒是很有价值
http://www-128.ibm.com/developerworks/db2/library/techarticle/dm-0407zhang/
不过我以为查询Sequence就只需要执行SELECT NEXT VALUE FOR seq_os_wfentry 就ok了,可是谁知道总是报错?!在比较仔细的看了这片文章之后发现,其实根本就无法执行这条SQL!而需要这样
INSERT INTO EMPLOYEE ( SERIALNUMBER, FIRSTNAME, LASTNAME,
SALARY) VALUES(NEXTVAL FOR EMPSERIAL, 'Martin', 'Wong', 1000.00)

可是看看JDBCWorkflowStore的实现,这里是JDBCWorkflowStore的主键生成策略!
protected long getNextEntrySequence(Connection c) throws SQLException {
if (log.isDebugEnabled()) {
log.debug("Executing SQL statement: " + entrySequence);
}

PreparedStatement stmt = null;
ResultSet rset = null;

try {
stmt = c.prepareStatement(entrySequence);
rset = stmt.executeQuery();
rset.next();

long id = rset.getLong(1);

return id;
} finally {
cleanup(null, stmt, rset);
}
}

c.prepareStatement(entrySequence) - 其实执行了一条SQL语句,所以看来JDBCWorkflow根本不支持Sequence生成策略!!

该死的实现方式,看来我要重载其实现方式,不过说真的JDBCWorkflow的编码人员其实水平不匝地!

posted @ 2006-03-02 21:01 killvin| 编辑 收藏

OSPropertySet的最新版本是1.3 Date: 9/22/2003 ,不要使用OSWorkflow中自带的OSPropertySet.jar ,主要是因为它的版本为propertyset-1.3-21Apr04,甚至里面的接口PropertySet竟然私自更换了(这个版本的remove()方法是抽象的,而新版本已经将这个方法命名为Public!!)

posted @ 2006-03-02 21:00 killvin| 编辑 收藏

J2EE(即Java 2 平台企业版)是由Sun公司主持推出的一项中间件技术。从CORBA、IDL到面向消息的系统,中间件技术已经走过了很长的一段路程,如今J2EE作为中间件技术史上的一块具有决定意义的里程碑,正受到业界越来越广泛的重视和采纳。

J2EE,一方面有着一套相当庞大的标准体系和数个不同版本,另一方面,由于市场上应用服务器品种多样,各家开发商使用的术语又不尽相同,因此,围绕着J2EE,常常有不少被人误解的地方。本文将深入探讨J2EE究竟是什么,它到底能做什么。
什么是J2EE?
在试图给J2EE 下一个明确的定义之前,我们首先要了解J2EE 并不简单地只是一门语言、一种工具或一套服务。

· J2EE——Java 2 平台企业版
简单地说,J2EE是一个标准中间件体系结构,旨在简化和规范多层分布式企业应用系统的开发和部署。J2EE方案的实施可显著地提高系统的可移植性、安全性、可伸缩性、负载平衡和可重用性。

J2EE技术出现之前,几家主要的中间件开发商的产品各自为阵,彼此之间缺乏兼容性,可移植性差,难以实现互操作,没有一个被普遍认可的行业标准。J2EE的出现标志着中间件技术在经历了多年的不断摸索和经验总结后,正逐步走向成熟。

J2EE的核心是一组规范和指南,定义了一个使用Java语言开发多层分布式企业应用系统的标准平台。开发人员在这些规范和指南的基础上开发企业级应用,同时由J2EE供应商确保不同的J2EE平台之间的兼容性。由于基于规范的各J2EE平台之间具有良好的兼容性, 因此J2EE应用系统可以部署在不同的应用服务器上,无需或只需进行少量的代码修改。


· J2EE视点
下面我们将从几个不同的侧面来考察J2EE,以期读者能对J2EE有个更全面清晰的印象。

(1)J2EE:多层、分布式中间件语法
采用多层分布式应用模型,J2EE将应用开发划分为多个不同的层,并在每一个层上定义组件。各个应用组件根据他们所在的层分布在同一个或不同的服务器上,共同组成基于组件的多层分布式系统。典型的J2EE四层结构包括客户层、表示逻辑层(Web层)、商业逻辑层和企业信息系统层。

有了J2EE,分布式系统的开发变得简单了,部署的速度也可以加快。J2EE组件的分布与服务器环境无关,所有的资源都可通过分布式目录进行访问。这意味着开发人员不再需要为组件和资源的分布问题耗费精力,从而可以有更多的时间专注于业务逻辑的实现,提高开发效率。

(2)J2EE:企业级应用系统开发平台
J2EE本身是一个标准,一个为企业分布式应用的开发提供的标准平台。而J2EE的实施,则具体表现为诸如BEA Web logic或IBM Web sphere之类的特定Web服务器产品。利用J2EE应用-编程模型开发的企业应用系统,可以部署在不同厂商生产的、但相互兼容的J2EE 应用服务器上。

目前,市场上基于J2EE的Web服务器品种繁多,性能特点各有千秋,每家厂商的产品都有精心设计的独到之处。但与产品个性无关的是,所有的J2EE应用服务器都为企业级应用系统的开发和部署提供了一个共同的基础。

(3)J2EE:电子化应用开发模型
J2EE应用很容易发布到Web、掌上电脑或移动电话等手持设备上。换言之,应用组件可以很轻松地实现电子化。J2EE的应用-编程模型保证组件在向不同类型的客户端移植过程中,商业逻辑和后端系统保持不变。

此外,J2EE平台的其他主要优点还有:自动负载平衡、可伸缩、容错和具有故障排除等功能。部署在J2EE环境中的组件将自动获得上述特性,而不必增加额外的代码开销。

J2EE所有这些特性对于需要构建全天候网络门户的企业来说显得尤为重要。

(4)J2EE:Web应用服务器上广泛采用的标准
可以说,J2EE是首个获得业界广泛认可和采纳的中间件标准。目前几乎所有的一流Web应用服务器,如BEA的Web logic、IBM的Web sphere、HP的应用服务器、Sun的iPlanet和Macromedia的Jrun等,都是基于J2EE的。迄今为止,还没有哪个其他标准能获得如此众多的中间件供应商的一致支持。

而且,有了J2EE,企业的应用开发对于某个特定的开发商或应用服务供应商的依赖性更小。应用组件只要符合J2EE规范,完全可以部署在不同的应用服务器上。为了确保不同厂商的J2EE应用服务器的兼容性和一致性,Sun公司发布了J2EE兼容性测试包。

· J2EE究竟是什么
至此,我们可以试着用一句话来概括J2EE,那就是:J2EE是一个中间件基础架构,有了它,开发者只需要集中精力编写代码来表达企业应用的商业逻辑和表示逻辑,至于其他系统问题,如内存管理,多线程,资源分布和垃圾收集等,都将由J2EE自动完成。

J2EE如何应对挑战?
在这一部分里,我们将探讨J2EE是如何应对企业开发过程中所面临的问题,以及如何为企业未来发展之需要提供空间。

· 独立于硬件配置和操作系统
J2EE运行在Java虚拟机(JVM)上,利用Java本身的跨平台特性,独立于硬件配置和操作系统。Java运行环境(JRE)——JVM的可安装版本加上其他一些重要组件——几乎可以运行于所有的硬件/OS组合。因此,通过采用Java,J2EE使企业免于高昂的硬件设备和操作系统的再投资,保护已有的IT资源。在很多情况下,J2EE还可以直接运行在EIS服务器环境中,从而节约网络带宽,提高性能。


· 坚持面向对象的设计原则
作为一门完全面向对象的语言,Java几乎支持所有的面向对象的程序设计特征。面向对象和基于组件的设计原则构成了J2EE应用编程模型的基础。

J2EE多层结构的每一层都有多种组件模型。因此,开发人员所要做的就是为应用项目选择适当的组件模型组合,灵活地开发和装配组件,这样不仅有助于提高应用系统的可扩展性,还能有效地提高开发速度,缩短开发周期。此外,基于J2EE的应用还具有结构良好,模块化,灵活和高度可重用性等优点。

· 灵活性、可移植性和互操作性
利用Java的跨平台特性,J2EE组件可以很方便地移植到不同的应用服务器环境中。这意味着企业不必再拘泥于单一的开发平台。

J2EE的应用系统可以部署在不同的应用服务器上,在全异构环境下,J2EE组件仍可彼此协同工作。这一特征使得装配应用组件首次获得空前的互操作性。例如,安装在IBM Websphere环境下的EJB,一方面可以直接与Websphere环境下的CICS直接交互,另一方面也可以通过安装在别处的BEA Weblogic 服务器上的EJB进行访问。

· 轻松的企业信息系统集成
J2EE技术出台后不久,很快就将JDBC、 JMS和 JCA等一批标准纳归自身体系之下,这大大简化了企业信息系统整合的工作量,方便企业将诸如legacy system(早期投资系统),ERP和数据库等多个不同的信息系统进行无缝集成。

由于几乎所有的关系型数据库系统都支持JDBC,因此只需借助必要的JDBC驱动程序,J2EE应用就可以和所有主流数据库系统进行通信。类似的,目前业界正冒出一批基于Java连接器体系标准的EI适配器,也用于提供各类legacy system和ERP/CRM的无缝集成。

· 引进面向服务的体系结构
随着Web服务以及SOAP等开放标准的出现,企业异构系统之间的互操作性成为可能。J2EE,作为一个可扩展平台,很自然需要加入Web服务特性。为此,Sun公司发布了一整套称为“JAX包”的API,支持从XML语法分析、XML绑定、SOAP消息发送、注册表查寻、XML RPC到XML消息传递等所有各种Web服务需求。

虽然J2EE平台的出现早于Web服务技术,但它的可扩展能力使它能很好地适应技术的最新发展。我们有理由相信,在未来,J2EE将引入更多的技术进步而不会动摇它的核心框架和应用-编程模型。

结束语
作为一个被业界广泛采用的中间件标准,J2EE是开发可伸缩的、具有负载平衡能力的多层分布式跨平台企业应用的理想平台。J2EE的首要任务在于提供一个标准中间件基础架构,由该基础架构负责处理企业开发中所涉及的所有系统级问题,从而使得开发人员可以集中精力重视商业逻辑的设计和应用的表示,提高开发工作的效率。

J2EE有效地满足了行业需求,提供独立于操作系统的开发环境。基于J2EE的应用系统灵活且易于移植和重用,可运行在不同厂家的Web服务器上。更为重要的是,J2EE是一个开放体系,完全有能力适应未来技术的进步和发展。

posted @ 2006-03-02 20:59 killvin| 编辑 收藏

该文转自guty

O-R Mapping

J2EE的标准是CMP Entity Bean,而实际应用中受到诟病最多的也是它。我们化了整整半年时间研究CMP2.0的开发方法,目前总算能够将代码量减少到70%,并且有希望减少到 90%。我曾经很满足现有的成绩,但是当我真正地阅读了hibernate后,对CMP2.0的信心彻底动摇了。

hibernate至少比CMP2.0有以下优点:
1. 兼容性。 规范一模一样,实现各有不同,这是CMP的现状。用第三方O-R Mapping工具可以解决这个问题。
2. 保护智力投资。在了解了Orion, Weblogic, JBoss的CMP实现后,我不愿意再去学习Websphere 或者Resin的实现了。
3. 性能。
a. local v.s. remote, hibernate、JDO、Castor都是本地调用,CMP2.0虽然也有Local接口,但是Web层还是需要通过Remote接口访问EJB层的数据,序列化、网络调用、创建大量的对象,都是性能降低的原因。
b. transaction,J2EE提出了一个全新的事务模型(method-based descriptor),对程序员的开发确实是个“简化”,记得一本教程建议所有的EJB方法都用Required。但这样的结果是什么?性能极度降低!互锁!没有办法,我们只有再去调节各个方法的Transaction属性,然后又出现 新的互锁...
新的事务模型是不成功的。它试图简化问题,却引入了更为严重的问题。各家厂商的Transaction实现也不尽相同,有的支持Optimistic Lock,有的在VM中同步Entity对象,又是兼容性的一大敌。
hibernate没有试图创造一个更新的模式,相反,它沿用了传统数据库的Transaction编程模式,在对J2EE的Transaction伤透脑筋后看到它,真是十分亲切,感觉自己确实在编程,而不是碰运气填代码了。
4. 动态Query。
Entity Bean很难实现动态Query,这是因为它基于代码自动生成技术,即最终的执行代码是在部署编译时生成的。hibernate则有根本的改变,它基于 reflection机制,运行时动态Query是很自然的事。另外,hibernate几乎支持所有的SQL语法,传统数据库可以做的它就可以做。
5. 发展速度。
I have a dream, 有一天Entity Bean会变得很好。但至少目前来看,Entity Bean是一个不完善的产品,它是大公司政治斗争和妥协的产品,而且习惯性将一些问题“无限期搁置”,典型的例子就是Query(之所以不提其他问题,是因为其他都是Entity Bean的致命伤:))
形成强烈反差的是,hibernate的核心程序员只有一人,但它改进的速度确是Entity Bean无法企及的。
6. 继承和多态。
OO语言的精华在Entity Bean这里是行不通的,我曾经自我安慰将Entity Bean看做一个“内存中的数据表”,才找到了一点平衡。
但当我看到hibernate时,又开始不平衡了。

另外,CMP2.0也有一些缺点是可以弥补的。
1. 代码维护。
大量的接口文件和配置文件,开发和维护的工作量很大。
解决途径:采用xdoclet,可以自动产生众多的接口和配置文件,甚至facade, delegate等高级模式。

至少目前来看,hibernate的缺点有:
1. 代码维护
hibernate提供了自动生成mapping文件“框架”的工具,但还需要手工调节。而这类开发,能想到的最佳模式就是xdoclet的(代码+注释)的模式了。幸好,hibernate的程序员已经向xdoclet项目增加了hibernate的模块。现在需要的是等待xdoclet的下一个 release。

结论:
hibernate至少从文档上超越了Entity Bean很多,我要学习hibernate。

以下是robbin的观点

如果说不使用Session Facade模式的话,我认为EB还是一个很有意义的的东西,因为EB是唯一直接支持跨RMI的持久化方案。但是由于EB的效率和减少跨RMI的网络调用的原因,EB已经完全被封装到SB的后面,EB的分布式调用的功能,EB的安全验证功能,EB的容器事务功能完全被前面的SB给做了,结果EB就只剩下了唯一的ORM功能了,单就ORM这一点来说EB实在是一个非常非常糟糕的东西。那么EB还有什么功能值得我非去用它不可呢?

用 Session Bean + DAO + Hibernate 来取代 Session Bean + Entity Bean,不但能够极大降低软件设计难度,软件开发难度,软件调试难度和软件部署难度,而且还可以提高允许效率,降低硬件要求。

不要把EB直接拿来和Hibernate做比较,两者不是一个范畴的东西,而应该整体比较两种方案:
Session Bean + DAO + Hibernate
Session Bean + Entity Bean
我找不出来第二方案有哪怕一点方面能够比第一方案好的。

CMP可以使用CMR来表示多表之间通过外键关联的关系。但是你仍然会遇到即使没有键关联的表仍然需要连接查询的情况,这是一个非常普遍的现象。

如果是Hibernate,可以在HSQL里面定义outer join,BMP也可以写JDBC,而CMP没有任何办法来解决该问题,除非你把需要的连接查询都定义为CMR,但那样的话,凡是有需要连接查询,或者有键关联的表都必须打在一个包里面。你如果不打在一个jar包里面,如果能够建立CMR?不是我想放在一个jar里面,而是不得不放在一个jar里面。基本上CMP还是非常笨拙的。

CMP的另一大缺点是不能动态SQL,guty已经提到了,一个SQL就要定义一个EJBFinder方法,在编译的时候就确定死了。在实际应用中,经常会遇到不确定查询条件的查询,比如说用户在页面上用下拉列表来选择查询的条件,用户既有可能什么限制条件都不选,也有可能选择某几个条件。这时候你怎么办?假设有n个查询条件,你要写 C1n + C2n + C3n +...+ Cnn(C是组合公式的符合,n是下标,1...n是上标)个EJBFinder方法才行,很恐怖吧。

其实JDBC的PrepareStatement也不能很好的解决这个问题,因为它是按照1,2这样的次序来set参数的。用Statement是肯定不行的,会严重影响数据库,甚至会导致数据库down掉(我的实际经验)。但是Hibernate就解决的不错,因为它可以按照 :name 这样的形式来设定SQL中的Placeholder,这样set参数就可以按照参数名称传递,因为次序不是死的,在程序里面就很容易根据用户选择的查询条件,动态的产生SQL,动态的set参数了。

CMP2.0还有一个大问题是不支持order by,当然你可以在Java里面对取出来的集合排序,但是速度和数据库里面就排好序速度不在一个数量级了。Hibernate不但可以order by,还可以group by,having,子查询,真是没有办法比下去了。

其实对于动态SQL和排序问题,特定的App Server也可以做,但那不是CMP2.0的规范罢了,所以为了可移植性,也不敢随便去用。

在项目开发时, 开发和运行效率以及灵活性是非常重要的指标。由于Entity Bean天生是一种粗粒度的使用方式,这就必定使它在装载的时候有较长的响应时间,也不能自如的支持懒装入的方式,使用成细粒度会使程序变得复杂,以及远程调用细粒度的entity bean是一种非常可怕的行为, 太慢了.

Hibernate正好满足开发和运行效率以及灵活性,说来说去,它可以称做一个OO化的JDBC, 这样大家就不会对Hibernate产生误解及恐惧心理。它支持粗细两种粒度方式,运用起来灵活自如,前提是你必知道如何使用,一个entity bean 实现要N种重复的方法, such as ejbRemove,ejbstore,ejb...., 光类也有一大堆,象Home Interface, Romote Interface..., Primary class if necessary. Hibernate只需要一个就行了。

CMP在进行O/R Mapping方面只是做了最基础的工作而已,完全用CMP做数据层,会发现你在把数据库应该做的工作全部都搬到App Server里面来重新实现一遍,有这必要吗?

CMP是把EJBQL写死在ejb-jar.xml里面的,所以n个条件就需要(c0n+c1n+...cnn )2的n次方个EJBFinder方法,简直没有办法说。

JDBC实现PrepareStatement的动态SQL构造不是不能够,而是非常麻烦,需要写一个非常非常大的if elseif else嵌套的判断。

Hibernate实现起来特别简单,(其实OJB也实现了PrepareStatement的动态SQL构造)这本身并不复杂,但是需要你多写些代码而已,由于CMP把EJBQL写死在配置文件里面了,你连选择的余地都没有。

posted @ 2006-03-02 20:58 killvin| 编辑 收藏

A reader asked a question via a comment a couple months ago that I didn't really have an answer for (and had always kind of wondered the same thing). In the original post (which showed how to use JDBC with ColdFusion), I used the following snippet of code:

Class.forName("jdbc.DriverXYZ");
Connection con = DriverManager.getConnection(url,
  "myLogin", "myPassword");

and the reader wanted to know what the Class.forName(..) method did. The most common answer you'll hear is that it loads the database driver, which, while technically true, is shallow. Where does it get loaded? How does it happen? And why?

To answer the question I started with the JavaDoc for the Class.forName() method. According to the documentation, the method:

... attempts to locate, load, and link the class or interface
I wasn't perfectly clear on what "locate, load, and link" meant, so I did a little digging through the Java Language Specification. According to chapter 12 of the JLS:
Loading refers to the process of finding the binary form of a class or interface type with a particular name, perhaps by computing it on the fly, but more typically by retrieving a binary representation previously computed from source code by a compiler, and constructing, from that binary form, a Class object to represent the class or interface.
Next, again according to the JLS, it must be transformed from it's binary representation to something the Java virtual machine can use, this process is called linking. Finally, the class is initialized, which is the process that executes the static initializer and the initializers for static fields declared in the class.

So then back to the original problem, when Class.forName() is called with an argument like this:

Class.forName("org.gjt.mm.mysql.Driver");

the classloader attempts to load and link the Driver class in the "org.gjt.mm.mysql" package and if successful, the static initializer is run. The MySQL Driver (download the source code) static initializer looks like this:

static {
  try {
    java.sql.DriverManager.registerDriver(new Driver());
  } catch (SQLException E) {
    throw new RuntimeException("Can't register driver!");
  }
}

So it calls a static method in the java.sql.DriverManager class which apparently registers a copy of itself when it loads.

So now I understand the where and the how, what about why? To understand the why you have to look at the next line in the initial code example:

Connection con = DriverManager.getConnection(url,
  "myLogin", "myPassword");

The DriverManager class (view DriverManager source here) returns a database connection given a JDBC URL string, a username and a password. In order to create that connection, the DriverManager class has to know which database driver you want to use. It does that by iterating over the array (internally a Vector) of drivers that have registered with it (ie: the registerDriver(Driver driver) method illustrated above) and calls the acceptsURL(url)) method on each driver in the array, effectively asking the driver to tell it whether or not it can handle the JDBC URL.

So there you have it. Class.forName explained.

posted @ 2006-03-02 20:57 killvin| 编辑 收藏

原文: http://forum.javaeye.com/viewtopic.php?t=17912


搂主的问题问的含含糊糊:flyjie给出了非常详细的解释,
不过就是没有解释String实例化的特殊方面以及Intern()方法的含义

-------
---------------------------------------------------------------------------------
---------------------------------------------------------------------------------
java代码:
String str ;

这样声明str它只支是一个对象的reference,不会产生实际的对象。如果没有初始化str,编译时便会发生错误。
java代码:
String str1=new String("test");
String str2 = "test";

str1是一个新的对象。new关键字的意思就是创建某个新的对象。而str2是一个对象的引用。 它们的内容相同,但内存地址是不一样的。 java中对象的引用存在Stack(栈)中,而对象由Heap(堆)分配空间。

3、引用==变量?  不一定
java代码:

  public class TestString {
public static void main(String[] args) {
String s1 = "test";
String s2 = new String("test");
if (s1 == s2)
System.out.println("s1 == s2");
else
System.out.println("s1 != s2");
if (s1.equals(s2))
System.out.println("s1 equals s2");
else System.out.println("s1 not equals s2");
}
}

我们将 s2 用 new 操作符创建程序输出:s1 != s2 s1 equals s2.
java代码:

s2 = s2.intern();

在你加上这句话后,上面的程序输入:s1 == s2 s1 equals s2

而String a = "test" ; String b = "test" ; a == b 会返回true; 这里a="test"时创建一个在栈中的reference, b=test时jvm发现栈中已存在名为"test"的字符串,直接引用。结论:String 是个对象,要对比两个不同的String对象的值是否相同明显的要用到 equals() 这个方法. 而== 比较的是内存地址的值。

4、private final String a = "test", 这个a属于常量,存放在常量存储空间(CS)中。

5、建议你看看<<深入浅出java虚拟机>>一书。

-------
---------------------------------------------------------------------------------
---------------------------------------------------------------------------------



总结

1. 在学习JAVA的时候就知道==比较的是内存地址.而equals比较的是内存地址对应的值!(可是还是有很多的人问来问去的,真不知道他们JAVA的基础课程是怎么学的?!)

2. JAVA所有的对象都是存放在堆中的!你获取的"对象"仅仅只是对象的引用而已

3. String是比较特殊的对象,特殊在
3.1 > String a = new String("test") -此时你是在堆中实例化了一个字符串对象
3.2 > String b = "test"-此时JVM会先去堆中寻找这样的对象;如果有就返回此对象的引用;如果没有就重新实例化一个这样的对象!基于这样的一个过程所以JAVA要求String不可以更改值的。

3.3 >intern()方法就是试图完成这样的一个寻找过程
When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.

这里有一份详细的参考资料:

关于Java栈与堆的思考 http://www.javafan.net/article/20051123115654293.html


posted @ 2006-03-02 20:56 killvin| 编辑 收藏

URL : http://java.sun.com/docs/books/tutorial/java/javaOO/nested.html

网上的一些关于内部类的概念是不完整的,还是看看SUN的文档上的标准答案。
...

Like other members, a nested class can be declared static (or not). A static nested class is called just that: a static nested class. A nonstatic nested class is called an inner class.

- Nested class分为静态Static nested class 的和非静态的 inner class, 在SUN的眼里只有Nested Class!!


As with static methods and variables, which we call class methods and variables, a static nested class is associated with its enclosing class. And like class methods, a static nested class cannot refer directly to instance variables or methods defined in its enclosing class — it can use them only through an object reference.

- 静态的Static nested class是不可以直接调用它的外部类enclosing class的,但是可以通过外部类的引用来调用,就像你在一个类中写了main方法一样。
...

As with instance methods and variables, an inner class is associated with an instance of its enclosing class and has direct access to that object's instance variables and methods. Also, because an inner class is associated with an instance, it cannot define any static members itself.

-非静态类inner class 可以自由的引用外部类的属性和方法,但是它与一个实例绑定在了以其,不可以定义静态的属性、方法(这点不是很理解,可能需要看JVM的类实现)

...
class EnclosingClass {
...
class InnerClass {
...
}
}


The interesting feature about the relationship between these two classes is not that InnerClass is syntactically defined within EnclosingClass. Rather, it's that an instance of InnerClass can exist only within an instance of EnclosingClass and that it has direct access to the instance variables and methods of its enclosing instance. The next figure illustrates this idea.


-图形化的嵌入类与外部类的关系

posted @ 2006-03-02 20:55 killvin| 编辑 收藏

仅列出标题
共5页: 上一页 1 2 3 4 5 下一页