EJB design: how to extract Value Object factory methods from entity beans
A good idea when designing entity beans is to keeping bulk accessors of entity beans (ie: Value Object factory methods) separate from the entity bean itself.
- Posted by: Floyd Marinescu
- Posted on: October 27 2000 12:33 EDT
I think this is a great idea, particularly when a client could be interested in many different subsets of an entity beans data (each of which can be returned as a separate type of value object). Removing these methods from the entity bean should clean it up, and leave it as a holder of business methods, not value object factory methods which really have nothing to do with the semantic meaning of the entity bean.
The problem is, how do you implement this, particularly in EJB 2.0? Can you have a separate static Factory class that implements the "getValueObject" method, that internally calls getters on an entity bean? This seems like bad practice to me, since each "getter" call could potentially be remote, so placing the "getValueObject" method on the entity bean could be better for performance.
In EJB 2.0, I also thought of removing value object factory methods from the remote interface to the home interface, since EJB 2.0 now allows placing of business methods on the home interface. This would at least keep factory methods out of the remote interface, but suffers from the same problem as the last option, since even though the method is on the home interface, it can only interact with an EJB through its remote interface.
To further complicate things, EJB 2.0 does not allow returning dependant objects to a client, thus each dependant object will probably have to have an accompanying value object used for marshalling purposes. In this case, we are pretty much forced to place a "getValueObject" methods on the entity bean, one for each value object it supports, because only the entity bean itself has access to its dependant object.
Does anyone have any ideas on how to extract value object creation from an entity bean?
- how to extract Value Object factory methods from entity beans by Charles Bradley on October 27 2000 19:45 EDT
- how to extract Value Object factory methods from entity beans by Stu Charlton on October 27 2000 21:13 EDT
- how to extract Value Object factory methods from entity beans by Rickard Oberg on November 13 2000 05:23 EST
- Astral Clone like Value Object by glenn dickson on August 07 2001 10:25 EDT
I think I kind of understand your point. Could you give a short code example of what you would like to do?
I'm going to post a variation of an email discussion I had with Floyd over this...
- You have two ways of performing bulk access on a business entity: one through a uniform interface, and one through a domain-specific interface. The former standardizes & de-couples whatever "views" or "slices" you want of your bean under a generic interface. The latter is the typical "value object" approach which has the unfortunate side-effect of having duplicate code to do the querying.
Right now I think I'm leaning towards the former approach (generic views) for my beans because it decouples my GUI from the domain and allows me to create a "control" object that has no inwards dependencies. I think the practice du jour of tightly-coupling your JSP pages to these domain-specific JSP beans is a waste of time and maintainance energy (unless your domain and requirements are simple). WebObjects has influenced my thinking here significantly, but I went to a few OOPSLA sessions that got the gears turning...
A typical Entity Bean that supported a generic value object would have an interfaces like "AttributeAccess".
for a simple case AttributeAccess could have three methods:
public Map getAttributes(Collection keysOfAttributes);
public Map getAllAttributes();
public void setAttributes(Map keyAndValuePairs);
This creates a directed graph of generic value objects. Attributes are java.lang objects .. one-to-ones are just references to other value objects, one-to-manys are references to Collections. Bi-directional relationships and back-references add complexity, but shouldn't be too difficult to work out.
- Getting/setting any kind of subset of an entity's attributes at runtime
- Cuts down your entity bean code by many, many lines if you have lots of attributes (assuming you use BMP and eliminate getters/setters/fields for them all and use a runtime Map.)
- using a well-known interface for access (Map) .. if you need something more rich, then you can at least the interface similarily clean
- HashMaps are very fast and lean. they map well to XML too.
- forget type safety (answer: I don't care, we should be using smalltalk anyway)
- don't have the code clarity of get/set methods (answer: hmmm. foo.getFirstName() vs. ((String) foo.get("firstName")) ... i don't see the difference, do you see the difference?)
- CMP wants explicit fields, getters and setters... in that case I would probably create a code generator to make them all and put them in an abstract superclass so I don't have to look at them if i don't want to. The question here is whether getAttributes()/setAttributes() should have this humungo-if-statement that generates the Map result, or if it should be maintained by the getters/setters along side their field values.
If you're using BMP, using the Map vs. not using it depends on how many attributes you have... I had 250+, so I just used the runtime Map... getters/setters have very negligible value.
Unfortunately this means you really don't have any particular place in code that explains what attributes you DO have. This is a problem.
My solution was to have my own global deployment descriptor that listed my attribute to field mappings... I had a stateful session bean handle bound to the JNDI in a well known place that would keep this descriptor in memory and check the disk if it was ever updated. It allows for automatic schema changes.. but has overhead & complexity associated with it... but if you're doing BMP in a medium-complexity application you're probably already rolling your own O/R mapper, so this isn't anything too wierd.
I could also see the potential need to perform generic relationship manipulation... this would require another interface to allow that.
Again, the whole point to generic interfaces is to bring your business logic to the "fore" of your app, and your infrastructure to the "aft". Depending upon abstractions is key...
...That being said, YMMV and follow the XP way of "do the simplest thing that could possiby work".. a lot of these ideas are "deep in the bag of tricks" and should probably be used only when you have a situation where your current solution to value objects are getting out of hand and need refactoring. Ideally a vendor should actually get the (gasp!) balls to do something beyond the J2EE spec to innovate on higher level framework features...
I have solved this problem in my EJBDoclet EJB generation tool (dreambean.com/ejbdoclet.html) as follows:
The bean implements get/set methods a la EJB 2.0. In addition to the usual get/set methods it also include the abstract methods "public FooData getData()" and "public void setData(FooData)" where Foo is the name of the bean. The EJBDoclet tool then generates a subclass of the entity that implements the get/set methods including the getData/setData. This makes the bean containing the logic very clean, and also provides a nice migration method to EJB 2.0 CMP (i.e. just change the generated bean to not include concrete implementations of all getX/setX methods).
In addition, I usually only make the get/setData methods remote so that it is only possible to use the bulk accessors with the bean. The getX/setX methods are hence only used for internal work and moving to/from bulk objects.
For more information see http://dreambean.com/ejbdoclet.html. I am also working on a complete application that uses this tool. Download a working alpha (.ear format) from dreambean.com/download/rickard/EBS-1.0.zip (includes full source).
BTW, I will add the long-tx strategy to the EJBDoclet output.
Just wondering what you would think of this ?
Do you have any idea why I cannot return my Bean. As these articles instruct I call the getAstralClone and "return this;" (within the bean) but receive a casting error. Can you help?(I'm using CMP2.0 J2ee 1.3)
Thanks in advance