爱心区 公益下载 公益文章 人在旅途 小博士乐园

>>首页 -> 公益文章 -> 技术文栏 -> java文栏 -> java 高级 -> 利用依赖值对象设计粗粒度BMP



利用依赖值对象设计粗粒度BMP


作者shihuang 来源21tx 加入时间:2005-10-12
摘要:
利用依赖值对象设计粗粒度BMP
来自:www.theserverside.com
By Floyd Marinescu, Senior Architect, The Middleware Company.
Please email comments/suggestions to: floyd@mi...

转载:转载请保留本信息,本文来自
http://www.51dibs.com/info/26416.htm

利用依赖值对象设计粗粒度BMP
来自:www.theserverside.com
By Floyd Marinescu, Senior Architect, The Middleware Company.
Please email comments/suggestions to: [email protected].
翻译photonman :[email protected]
对性能和效率的关注就需要一个用BMP来构造粗粒度实体Bean集的标准途径,就象CMP做的那样(CMP使用其内支持的依赖对象)。
依赖对象为CMP实体Bean构建粗粒度实体Bean集提供了一个自动途径,但是java/ejb/ EJB2.0规范并没有为BMP提供同样的支持。
构建粗粒度的Bean集是一个普遍使用的性能优化策略。这样会使得我们可以面向简单的Java类建模而不是实体Bean,同时减少了内部Bean的远程通信和应用在实体Bean上的事务处理。这些简单的Java类有典型的特点:依赖于父实体的生存周期、没有属于自己的标识和不能被客户端应用远程访问。对于CMP这些对象被称为依赖对象(dependent objects),在EJB2.0规范里面定义了一个标准的途径来定义CMP的依赖对象,把它们的复杂的持久化过程留给应用程序/server/ 服务器。
不幸的是,BMP实体Bean的开发者无法享受依赖对象带来的便利,它们只是为CMP准备的。然而,BMP的开发者也需要创建粗粒度实体Bean集,因此:
BMP开发人员就应该把商务数据建模成简单的Java类,我们叫作依赖值对象(Dependent Value Objects)。依赖值对象是被你的实体Bean创建和保存的。不象CMP的依赖对象,BMP开发者必须显式的处理依赖值对象的代码!
依赖值对象和值对象(Value Objects)类似,在于它们都是简单的Java类,并且都是可以通过网络来传输(这是一个优于CMP依赖对象的地方,CMP的依赖对象是客户端不可见的。)。但是又不同于值对象,依赖值对象是一些持久对象,实体Bean管理它们的生存周期,而值对象仅仅是一个需要传输的数据的批量化结果,而且得到这些数据以后,值对象就被丢弃了,并没有持久化。
一个实体Bean和其保存的依赖值对象的关系可以是1:1、1:N。例如:考虑一个履历(Resume)。一个Resume实体Bean一般只需要一个Address 依赖对象,然而可能对应于保存在一个Collection里面的多个Job Entry 对象,如图1:
图1: ResumeBean 类和依赖类Address、Job Entry的类图
实现BMP的依赖值对象不是一件轻松的事。一个好的实现在于:
 依赖值对象和数据库中表的映射
 不变的依赖值对象
 依赖对象的Lazy loading
 智能的生存期管理?(更新缓冲直到ejbStore, 如:仅仅保存新的或者是被修改过的依赖对象)
 从商务逻辑中分离出持久逻辑
实现依赖值对象的持久化最简单的途径:序列化这些依赖值对象到其父实体Bean所对应的表的一个BLOB字段里。如果是处理一个依赖值对象的集合,这个整个集合就被序列化到这个字段里面。一个改进的选择是:持久你的依赖值对象到一个单独的表,使用JDBC以一个字段一个属性的方式映射你的依赖对象。保持以这种方式持久,可以使得你能通过sql来访问你的依赖值对象,并且你可以直接在数据库的层次来报告和维护你的依赖值对象(如果是个BLOB,你将不能做这些事)。对于我们的例子Resume,Resume实体映射到一个数据库里resume的表,而我们的Address依赖值对象映射到address表,Job Entry映射到jobentries表,如图2所示。因为我们的address和job entries 是依赖值对象,它们没有除了Resume实体的标识以外的标识,这样就不需要它们自己的主键(Primary Key),因为它们被resumeID唯一标识。

图2:EJB 和依赖对象的DB 映射
不可改变
依赖值对象应当是不可改变的(immutable),也就是说它们没有setter方法。在CMP概念里,依赖对象是对客户端不可见的,它们只能在实体Bean内部被访问。这样处理的一个好处就是对于依赖对象的更新只是局限在实体Bean的商务方法(封装)。对于BMP,我们没有碰到这样的限制,因为依赖值对象是常规的Java类。为了达到CMP一样的封装但是又不失可以在网络上传输的优势,它们就应当是被定义成不可改变的。这就保证客户端对依赖值对象的修改不会马上反应到服务器端,也就是说强制客户端只有通过实体Bean的远程接口才能对依赖值对象的各种操作。
Lazy Loading
是一个增强性能的策略,它允许依赖值对象在需要的时候被加载,而不是预加载。它防止了在ejbCreate()方法里面加载所有实体Bean的依赖值对象的无用消耗,特别是在某个客户端并不对这个依赖对象感兴趣时。
Lazy loading是易于实现的。每一个依赖值对象或者它们的集合,都应当有这样的一个getter方法:
 检查这个内部的依赖值对象是不是已经加载过(是null)。
 如果没有加载那么调用数据库加载对象。
 把得到的依赖值对象传给调用者。

一旦从数据库里得到依赖值对象(或者它的集合),那么可以在实体Bean里保存起来,作为一个缓冲数据,用于下一次的调用,就象在Resume实体Bean中的方法getAddress()和getJobs()(参见本模式下面的可以下载的例子)。
智能生存期管理
在BMP的环境下,程序员要负责智能生存期管理的工作。不幸的是这个工作很具有挑战性,特别是在处理依赖值对象的集合时。必须写大量的生存期管理和数据的同步逻辑来跟踪依赖值对象的状态,当它们被创建、更新和删除时。好的BMP实现仅仅当依赖值对象被修改、创建或者删除时更新数据库。它也允许在一个事务环境里对同一个依赖值对象的多操作(更新、删除等等),并且当这个数据库事务结束时只同步这个实体Bean的依赖值对象的最后状态。
从商务逻辑中分离持久逻辑
许多的BMP开发者由于过分关注依赖值对象复杂的生存期管理,他们会选择通过给依赖值对象增加主键(primary key)或脏标识(dirty flags),把持久逻辑混合在商务逻辑里,甚至为了跟踪依赖值对象的生存周期而使得商务方法交织在一起。为了使得在BMP中实现依赖值对象和在CMP中一样简便,每一个开发者都不应该给依赖值对象加入额外的特殊字段,也不应该把持久逻辑和商务逻辑混合在一起。
那么,一个BMP开发者要怎么做才能轻松而聪明的开发依赖值对象的智能生存期管理而保持持久逻辑和商务逻辑相分离呢?答案就是抽象这些细节到一个为了跟踪依赖值对象的生存期而建立的特殊的集合类中。图3展示了一个DepedentValueSet类,Java 2 Set 集合类的一个特殊实现。在实现平常的Set的操作(包括add、remove等等)的同时,这个集合加了几个新的方法包括一个把两个依赖值对象作为参数的update(oldObject,newObject)方法。由于依赖值对象(象CMP的依赖对象)是没有任何的自己的标识的(没有primary key),我们就要求传递原始的对象,这样我们的集合类就知道哪个对象要被更新。

图3:DependentValueSet类
使用这个DependentValueSet就使得BMP的开发者可以把持久逻辑从商务逻辑中分理出来,因为商务方法要做的仅仅是和一个普通的Set的接口和额外的update接口交互。而依赖值对象也可以保持其清晰而简洁,它们自己不需要额外的特殊字段来跟踪它们的状态,就象DependentValueSet处理的那样,所有这些都是透明而显然的。
我们的DependentValueSet实现允许当我们依赖值对象在内部被创建、删除或者修改时,跟踪其生存周期。特别当一个用户在一个事务环境里面对这个依赖值对象执行了多重操作并且同步了这个依赖值对象的最后状态,在ejbStore()只有一次更新。例如:对同一个依赖值对象可以做更新操作多次,但是在ejbStore()方法里面,数据库只需要一次更新操作。
为了支持数据库的同步,我们的DependentValueSet加了5个额外的方法:
• addObjectFromDatabase(Object)
• getObjectsToInsert()
• getObjectsToDelete()
• getObjectsToUpdate()
• databaseHasBeenUpdated()

addObjectFromDatabase是当依赖值对象初次从数据库里取得时调用。它允许跟踪这个对象的数据库的初始状态。getObjectToXXX方法允许BMP开发者查询DependentValueSet来得到哪一个对象应该被插入、删除或更新,同时在ejbStore()方法里写这个依赖值对象的特殊的SQL代码来处理这些更新。一旦同步完成,DepedentValueSet应该唤醒(数据库已被更新),这样它就可以清楚自己的状态以供下一个事务之用。
在我们的Resume例子里面,我们保存了工作的集合在DependentValueSet。实体Bean公开这个job entries的创建、读取、更新和删除操作。每一个都是分派给DependentValueSet的jobentries方法,这个方法处理所有对resume jobs的生存周期的跟踪。在事务结束的时候(ejbStore()),我们查询DependentValueSet来检测那个job entries被创建、删除或更新,根据结果我们执行相应的SQL语句,更新数据库。
每一个Resume仅仅包含一个Address依赖值对象,因此我们不需要DependentValueSet的高级服务。相反我们可以自己处理get和set这个Address对象,同时简单地保持一个isAddressModified标识,这样ejbStore()将会知道这个Address是否在事务环境中被修改,如是那么保存它。

译者注:
本文给出了在实体Bean设计时缓和性能、负载、效率和扩展性的策略。分离持久逻辑和商务逻辑使得代码易于理解和扩充。前几天有网友讨论实体Bean的设计,我想可以从中找一些思路,特此翻译成中文,希望对大家的EJB设计有所帮助。由于本人水平有限,其中错误不正之出不在少数,在此付上原文,以便对照,恳请读者斧正。 ――-photonman

英文原文:
Coarse Grained BMP beans with Dependent Value Objects
By Floyd Marinescu, Senior Architect, The Middleware Company.
Please email comments/suggestions to: [email protected].
Performance and efficiency concerns require a standard way to build coarse-grained entity beans using BMP as well as CMP.
Dependent objects provide an automatic way to build coarse-grained entity beans for CMP entity beans, but the EJB 2.0 specification doesn’t provide an equivalent mechanism for BMP entity beans.
Building your entity beans to be coarse-grained is a common performance optimization. It allows modeling the business objects with plain java classes rather than as entity beans, reducing the inter-remote object communication and transactional overhead associated with entity beans.?These plain classes typically have a life cycle dependent on a parent entity bean, do not have a distinct identity of their own and do not need to be referenced remotely by a client. For CMP, these objects are called dependent objects, and EJB 2.0 defines a standard way to define CMP dependent objects, leaving the complex task of persisting them to the underlying application server.
Unfortunately, BMP entity bean developers cannot leverage dependent objects, they are a CMP construct only. However, BMP developers should still be able to create coarse-grained entity beans,
Therefore:
BMP developers should model dependent business data in plain java classes called Dependent Value Objects.?Dependent Value Objects are stored inside your entity beans and are created, modified and removed by your entity beans. Unlike dependent objects with CMP, BMP developers must explicitly write the persistence code of dependent value objects.
Dependent Value Objects are similar to Value Objects in that they are plain java classes and are transportable over the network (this is one advantage over CMP dependent objects, which cannot be accessed by clients). Unlike Value Objects, Dependent Value Objects are persistent objects whose life cycle is managed by an entity bean, whereas Value Objects only exist to transport data across the network in bulk, and are typically discarded once data has been read.
An entity bean can store dependent value objects in a one to one, or one to many fashion.?For example, consider a Resume. A Resume entity bean would only require one Address dependent value object, whereas it would require multiple Job Entry objects, which could be stored in a Collection inside the Resume entity bean, as in figure 1.

Figure 1: ResumeBean class diagram with Address and Job Entry Dependents
Implementing dependent value objects with BMP is not trivial. A good implementation will feature:
• Dependent value objects that map to tables in a database
• Immutable dependent value objects
• Lazy loading of dependent value objects
• Intelligent lifecycle management?(caching updates until ejbStore, only storing new or modified dependent value objects, etc)
• Partitioning of persistence logic from business logic
One of the easiest ways to persist dependent value objects is to simply serialize them to a SQL BLOB column in the table which your entity bean maps to. If dealing with a collection of dependent value objects, the whole collection could be serialized into this column. A better alternative is to persist your dependent value objects to separate tables, using JDBC to map your objects in an attribute-per-column manner. Persisting your dependent value objects in this manner will allow them to be accessible to sql searches and allow you to perform reporting and maintenance directly on the database (you can’t do this if they are stored as blobs). From our Resume example, our Resume entity bean maps to a resumes table in the database, whereas our Address dependent value object maps to an addresses table and our Job Entries map to a?jobentries table, as in figure 2. Since our addresses and job entries are dependent value objects, they do not have their own identity outside of their parent Resume entity bean, and thus they do not have their own primary key, instead they are identified by a resumeID column in the addresses and job entries table.


Figure 2: EJB and dependent object DB mapping
Dependent Value Object should be made immutable, that is, they should have no set methods.?In CMP, dependent objects cannot be passed to a client, they can only be accessed internally by an entity bean. One advantage to this restriction is that it ensures that any updating of data in an entity bean is done through the entity bean’s own business methods (encapsulation).?In BMP, we are not faced with this restriction since dependent value objects are just regular java classes. In order to achieve the same benefit in BMP but still have the flexibility of passing our dependent value objects to clients, dependent value objects should be made immutable. This will ensure that the client doesn’t make changes to the value object assuming that his changes will be instantly reflected on the server, thus forcing the client to perform all changes through the entity beans remote interface.
Lazy Loading is a performance enhancement that allows dependent value objects to be loaded on demand. It avoids the wasteful practice of loading up all of an entity beans dependent value objects inside ejbCreate(), particularly if a client isn’t interested in the dependent data.
Lazy Loading is easy to implement. Each of your dependent value objects or collections can have get methods which:
• Check to see if the internal dependent value object or collection is has been loaded (is null).
• Calls the database and loads up the requested dependent data if not previously loaded
• Passes the requested dependent value object (or collection) to the caller.
Once the dependent value object (or collection) has been read from the database, it can be cached inside the entity bean for future calls, as in our getAddress() and getJobs() in our Resume Bean (see downloadable example source at bottom of pattern).
In a BMP scenario, intelligent life cycle management is the programmers responsibility. Unfortunately, this is not trivial, particularly when dealing with a collection of dependent objects. A lot of life cycle management and database synchronization logic must be written to track dependent value objects as they are created, updated, and deleted.?A good BMP implementation will only update the database with dependent objects that have been modified, created or deleted.?It will also allow multiple operations (update, delete, etc) on dependent objects within one transaction and only synchronize the final state of an entity bean’s dependent objects with the database at transaction completion (in ejbStore).
Partitioning of persistence logic from business logic. Given the complexities of life cycle management of dependent value objects, many BMP developers choose to mix their persistence logic with their business logic by adding primary keys or dirty flags to dependent value objects, even interlacing their business logic with code necessary to track the life cycle of dependent value objects. To make programming with dependent value objects in BMP as easy as it in CMP, a developer should not add any special fields to his dependent value objects, nor should persistence logic be mixed in with business logic.
So how can a BMP developer achieve intelligent dependent value object lifecycle management and keep persistence logic separate from business logic? The answer is to abstract these details into a special collection class designed to track the lifecycle of dependent value objects that it is storing.?Figure 3 illustrates a DependentValueSet, a special implementation of the Java 2 Set Collection. Along with supporting the normal operations of a Set (including add, remove, etc), the collection adds several new methods including an update(oldObject, newObject) method that takes two dependent value objects as a parameters, the original object and the newly updated one.?Since dependent value objects (like CMP dependent objects) do not have any identity of their own (no primary key), we require that the original object be passed as a parameter so that our Set will know which object to replace.

Figure 3: The DependentValueSet Class
Using the DependentValueSet allows CMP developers to achieve separation of persistence logic from business logic because business methods need only interact with the normal operations of the Set interface and the extra update method. Dependent Value Objects are also kept clean; they do not need any special attributes added to track their state, as the DependentValueSet handles all of that transparently.
Our DependentValueSet implementation allows for intelligent lifecycle management by tracking which of our dependent objects were created, deleted or modified internally. This is extremely powerful as it allows the user to perform multiple operations on dependent value objects within one transaction, and synchronize the final state of our dependent value objects just once in ejbStore(). For example, the same dependent value object can be updated multiple times, but in ejbStore(), only one database update needs to be performed.
To support database synchronization, our DependentValueSet adds 5 extra methods:
• addObjectFromDatabase(Object)
• getObjectsToInsert()
• getObjectsToDelete()
• getObjectsToUpdate()
• databaseHasBeenUpdated()
The addObjectFromDatabase method is meant to be called when dependent value objects are initially loaded from the database. It allows the DependentValueSet to track the objects that were initially in the database. The getObjectToXXX methods allow a BMP developer to query the DependentValueSet to find out which objects should be inserted, deleted, or updated, and write the dependent value object specific SQL code to handle these updates in ejbStore(). Once synchronization is complete, the DependentValueSet should be notified (databaseHasBeenUpdated ) so that it can clear itself up for the next transaction.
From our Resume example, we store our collection of job entries in a DependentValueSet. The entity beans remote interface exposes create, read, update, and remove (CRUD) operations for job entries, each of which delegate to methods on the DependentValueSet called jobentries, which handles all the life cycle tracking of the resumes jobs. At the end of the transaction (in ejbStore() ), we query our DependentValueSet to determine which job entries have been created, removed, or updated, and execute the appropriate SQL accordingly.
Each Resume only requires one Address dependent value object, thus we don’t need the advanced services of our DependetValueSet. Instead, we can manually get and set the object, and simply keep around an isAddresssModifed flag so that ejbStore() will know if our Address has been set in this transaction, and store it in the database if so.



相关文章

相关软件

客户服务中心信箱:[email protected]