Java Development News:
A Critique of EJB 3
By Hani Suleiman
13 Jul 2004 | TheServerSide.com
Expert Group Thanks
Before launching into the shortcomings of this draft, I'd like to publicly thank the expert group for the excellent work they've done.
It's clear from even the most casual glances that EJB3 is exciting stuff that is a bold step forward, and will hopefully silence many of the EJB detractors and bring them back into the fold!
I am also grateful that the expert group is so open and eager for feedback. It's certainly much more motivating to contribute and respond when one doesn't feel that it's landing on deaf ears.
Having said all that, the purpose of this article is not to highlight new features or explain annotations. Instead, I will (surprisingly) focus on the gaps and flaws in the draft as it stands currently. There are three areas that I feel need a lot more thought. Descriptors, the EntityManager, and 'magic' methods.
The first glaring omission in the draft is the lack of descriptors of any shape or form. While the final specification will support ejb 2.1 style beans (with a number of backported enhancements from 3.0), the 'new' bean style seems to eschew descriptors altogether.
As anyone who has been working with persistence engines for the last few years knows, hardcoding your schema and sql information into your source files is generally something to be avoided. API's like Hibernate and other O/R mapping tools owe some of their success to the fact that they make this separation very easy and natural, and rarely forcing you to think about your database schema when working with your java objects. Almost every persistence API (including EJB 1.1 onwards) has drilled into us that tightly coupling our database structure to our object model is a Bad Thing.
Thus, it's very surprising that the draft forces you to specify all manner of sql specific information in source file annotations. There isn't even any attempt at disguising the underlying 'sql'ness of entities. Annotations are called 'column', table names go right in there, as well as queries, joins, and catalogs.
In terms of how it should work, it should be possible to use deployment descriptors to override annotations as well as a number of defaults (for example, transactionality, isolation levels, etc).
The other oddity in this regard is the glaring omission of backward compatibity or any form. While we've been reassured that EJB 2.1 will continue to be mandated (which I think is excellent, since it proves that betting on a spec will not result in you having the rug pulled from under you in a year or two), there is no mention of the overlap or common functionality in both. Hopefully this will materialize in the next draft or two.
The EntityManager API leaves much to be desired. It suffers from a number of serious flaws that I sincerely hope will be addressed for the next draft.
The create method, for example, has a couple of problems. The first of which is the fact that its behaviour is not consistent, nor does it provide feedback. If you try to create an entity with a primary key that already exists, the create methods acts as a no-op. This is unintuitive as well as unhelpful, why not throw an exception, or at the very least return a boolean to signify whether something actually happened or not?
The next issue with the create method is symptomatic of an underlying design issue that I am not entirely comfortable with. Put simply, there are too many different combinations of entity states.
This is best illustrated with a simple example. Before launching into this example, it's important to understand that EJB3 introduces the concept of disconnected entities. This makes sense when considering remote entities, where you're free to make all your modifications, then persist them via the entity manager.
Now, let's say you have a reasonably complex object graph that you have constructed, and now wish to persist. You pass in your object to the create method. What's the state of our entity now? Well, our reference to it is still valid, so we've ended up with a disconnected entity, where modifications are not persisted no matter what your flush mode is. But wait! There's actually another instance of it living in the container! This second instance is managed, and you can request it from the container. Calls on this second (identical!) entity will in fact be persisted without forcing you to pass it back to the entity manager. Given an instance of this entity, there's no way of determining whether it is disconnected or not, and whether changes to it will actually be persisted.
This issue has another downside; caching. In the above example there are now two identical instances of
the same entity, with strong references to both. Twice as much memory is used, and neither instance is
particularly eligible for garbage collection. This issue can be trivially address though by changing the
semantics of the create method. It could, for example, return a managed instance of the entity, and the
spec could make it very clear that the usage should be something like
entityManager.create(myEntity). In the spirit of openness, it would be greatly appreciated to learn
of the reasoning behind these dubious choices, and how they came about in the first place.
Another troubling issue is the prevalence of 'magic' values. This takes the form of magic EJB level methods (for example, 'ejbRemove'). In many cases, all you need to do is add this method to your bean for it to be called. There is no interface to ensure compile-time checking. All the benefits of string typing and interfaces are tossed aside in the name of convenience and ease of use. Another example is magic interfaces, where you just need to have your EJB class name suffixed by 'Bean', 'Impl', or 'EJB' for the interface name to be deduced based on the trimming of that suffix.
I think these magic values are symptomatic of 'ease of use' gone far too far. Lazy, sloppy (and often very vocal) developers love them. It would be truly awful if this 'feature' were to remain, tossing aside all the years of indoctrination that have been drilled into every developer; core OO concepts and the importance of interfaces and contracts. EJB developers everywhere will suddenly be at the mercy of simple spelling mistakes that can have disastrous consequences. I for one would love to know why this approach was chosen, as it's certainly not intuitive. Why was the tried and tested interface approach discarded?
Finally, the current draft has a fairly woeful set of examples, where it bothers list any at all. Many of the included ones also demonstrate fairly distressing behaviour like public fields. However, this sort of thing is to be expected in such an early draft, and one can safely assume that they will get better with every successive draft.
There are also a number of areas in the draft where the expert group has explicitly asked for reader feedback, so here it is! The first of these is disallowing composite primary keys consisting of a number of fields of the entity itself. Removing this I think would be a bad idea, as it's fully supported in EJB 2.1.
Another suggested option is adding an INSERT operation in EJBQL. As the question in the draft says, such an operation is fairly useless, given that I have yet, in all these years of writing EJB's, ever thought 'if only I could insert an entity via EJBQL!'. Granted, someone somewhere might have this thought, but for the life of me I cannot think of a good argument for it.
In conclusion, I'd like to reiterate my thanks to the expert group and the extraordinary lengths they've gone to to get the population at large involved. Despite all the criticism, the spec is promising and is many leaps forward from previous iterations. Keep up the good work, and I'm sure I speak for the community at large when I say that we all eagerly await the next draft to poke and prod at!
About the authorHani Suleiman
Hani Suleiman is the CTO of Formicary, a company that focusses on financial consulting services and portal solutions. Hani is also the author of the bileblog, a controversial blog discussing all things Java.