Not very popular choice for persistence nowadays, CMP still too often is only readily available persistence framework in Enterprise environment. While not as sexy as hibernate or SQL Maps, CMP 2.X implementation in leading application servers such as BEA Weblogic offers quite good solution for ORM problems. Beside there is a lots of developers familiar with EJB and mature support from different tools from xdoclet to Eclipse wtp/jst.
Let's take a look at what can be done to improve performance of CMP in Weblogic by enabling more aggressive than default caching strategies.
Everybody knows that EJB Container maintains cache or pool of Entity Beans, usually configurable in deployment descriptor. Amazingly lots of developers don't realize that it doesn't mean that once Container loaded particular bean instance from database it won't go to database again while bean instance is kept in pool. Quite contrary, by default Container calls
ejbLoad() to synchronize instance's state from the database at the beginning of every transaction. Basically on every operation with CMP bean, even if bean was loaded seconds ago in previous transaction Container executes SQL select to refresh it. Only while you operating with CMP instance(s) in one transaction Container caches them. This is so called 'Commit Options A/B/C' as described in EJB 2.0 specification (10.5.9).
Obviously reloading state from database in every transaction could have some performance impact. Easy to understand why this is default behavior - this is safest way if database shared between multiple processes and each one of them could change state of persisted object in database while it's cached in EJB pool. In this case we're facing possibility of lost updates - other process updates record in database and then Container overwrites it with potentially stale data from cache. But first of all refreshing data in the beginning of transaction doesn't guarantee against lost updates, it's just reduces possibility of it. This is a well know problem in database world and there are two approaches to deal with it. First approach is so called 'pessimistic concurrency locking', when records in database locked for other updaters for duration of transaction ('select for update'). This is almost never used because of significant overhead and performance impact. Second approach is called 'optimistic concurrency locking', in this case there is no actual locks held in database but process which execute update has some mechanism to detect that record been updated by other process between his read and update. Preferred way to implement this is with 'version' column on the database table (in SQL update increment value for that column, include previous value in 'where' clause and then detect if no rows are updated).
Weblogic has built in support for optimistic concurrency in CMP beans via verify-columns element in weblogic-cmp-rdbms-jar.xml deployment descriptor. It is a good practice to have 'version' column in all (updatable) tables in your database and configure CMPs to use it by enabling optimistic concurrency in concurrency-strategy element in weblogic-ejb-jar.xml
Optimistic concurrency alone doesn't give you any performance improvement, but it's necessary step to enable cache-between-transactions functionality which is not available without enabling optimistic concurrency (for the reasons discussed above). Setting cache-between-transactions to 'true' will result in Container skipping calls to
ejbLoad() if bean instance is already available in the EJB cache. For certain types of applications where the same objects accessed more than once from different transactions over short period of time this can lead to significant performance improvements (in our tests up to 30% for our application).
Naturally your application must be ready to deal with
OptimisticConcurrencyException when concurrency violation is detected by container. When
OptimisticConcurrencyException (subtype of RuntimeException) is thrown Container discards EJB instance from cache. In a cluster, when a bean with Optimistic concurrency is updated, notifications are broadcast to other cluster members to prevent optimistic conflicts. Note that if you have delay-updates-until-end-of-tx set to 'true' (default) your code won't see optimistic exception until transaction commit.
Word of caution. While available for quite a long time, cache-between-transactions functionality never worked stable in Weblogic 7, but it seems to be fixed in version 8.1. We're using it in production applications and quite happy with the results.
About the author
Dmitri Maximovich is an independent consultant specializing in software design, development, and technical training. He has more than twelve years of industry experience and has been involved with J2EE since its inception. His job profile includes designing and developing mission critical applications for financial and pharmaceutical industries. Dmitri lives in Toronto, Canada and has a master's degree in Physics from St. Petersburg university.