Entity Bean Features
The next set of WebLogic Server-specific features covers the capabilities related to entity beans. There are a large number of entity bean capabilities provided by WebLogic Server; far more than any other type of EJB component. In a sense, this reflects the complexity of the problem entity beans are being asked to solve and the need for features above and beyond the J2EE specification to create enterprise-class applications. There are many possible strategies for achieving good performance with proper transactional controls and concurrency. No single strategy is best is all situations, and the EJB container relies on the application developer to configure entity-bean persistence properly for best performance.This section covers strategies and best practices in four major areas: concurrency, caching, tuning queries and persistence operations, and advanced CMP features. You should be aware of all possible strategies and their implications before choosing a strategy for your entity bean-based J2EE application.
Concurrency Strategies
The basic load-and-store cycle used to read and write entity-bean data on a per- transaction basis is sufficient, in theory, to ensure that clients always interact with a bean containing the latest data in the database. This simplistic approach might be sufficient for small applications with few users and limited performance requirements, but it fails to meet the needs of a true enterprise-class architecture for two reasons:
No caching of bean data. In this simplistic approach, every transaction with an entity bean involves a database operation to retrieve bean data from the persistent store. You saw this behavior in the simple PersonBMPBean example application earlier. The bean-instance caching defined by the EJB specification does not cache the contents of the beans, only the instance created in the pool. You therefore need some facility for safely caching bean data to improve performance where appropriate.
No concurrency support. The simplistic load-and-store cycle does not specify how to handle multiple clients interacting with the same entity bean during this life cycle. To make matters worse, if the application is clustered across multiple servers, the clients accessing the beans may be using different EJB containers running on different JVMs on different machines. Coordinating concurrent access to bean data across multiple containers is also a requirement for enterprise-class systems.
WebLogic Server provides a robust set of caching and concurrency options to address these two limitations. You should recognize that caching of data and controlling concurrent access to data are strongly interrelated. WebLogic Server combines approaches for both sides of the problem in a set of concurrency strategies:
Exclusive concurrency strategy, or exclusive locking, addresses the concurrency side of the problem by using in-memory locking to serialize access to specific bean instances in an EJB container. Caching can be configured using the cache-between-transactions feature, described later in this chapter, in a nonclustered environment.
Database concurrency strategy, or database locking, addresses the concurrency side of the problem by relying on the underlying database technology to reject changes made by two simultaneous transactions. Caching data between transactions is not possible with this strategy.
Read-only concurrency strategy is more of a caching solution than a concurrency solution. Bean data is kept in memory and loaded from the database as a result of time-out or explicit invalidation. Concurrent access to bean data is allowed through the creation of multiple beans.
Optimistic concurrency strategy, or optimistic locking, uses the classic client/server approach of ensuring that the underlying database rows have not been updated by any other client during the life cycle of the bean. Caching is possible using the cache-between-transactions feature in both clustered and nonclustered environments. Only CMP entity beans may use this strategy.
We’ll discuss each of these concurrency strategies in detail in the sections that follow.The desired concurrency strategy is defined on a per-bean basis in the weblogic-ejb-jar.xml descriptor file in the entity-cache element in the entity descriptor:
<entity-descriptor>
<entity-cache>
...
<concurrency-strategy>Exclusive</concurrency-strategy>
</entity-cache>
...
</entity-descriptor>
The default concurrency strategy in WebLogic Server 6.x and later is database concurrency. Prior versions used exclusive concurrency by default.
Exclusive Concurrency Strategy
The exclusive concurrency strategy uses in-memory locking to serialize access to bean instances in an EJB container. When a client enlists a specific entity bean instance in a transaction by calling a method on the bean interface, the EJB container creates an exclusive lock on that bean instance held for the duration of the transaction. Other clients attempting to enlist the same bean in a transaction are forced to wait for the first transaction to complete before acquiring the lock on the instance. Because the first client’s lock is not released until the transaction completes and modified data is flushed to the database, the next client will always see the latest committed data when the container loads the data on its behalf.Table 6.3 presents a simplified example of this process for two clients attempting to access the same entity bean at the same time. Clearly the activity becomes more complex if additional EJB components are involved in the transaction, but the example represents the primary activities in the process.
Client #1 Thread Activity | Client #2 Thread Activity |
---|---|
The client acquires a reference to bean 101. | The client acquires a reference to bean 101. |
The client invokes a method on the bean interface. | |
The EJB container intercepts the request, starts the transaction, enlists bean 101 in the transaction, and places an exclusive lock on bean 101 in the lock manager. | |
The EJB container loads the data from the database into the bean instance. | The client invokes a method on the bean interface |
The EJB container passes the request to a method on the bean instance. | The EJB container intercepts the request, starts the transaction, and blocks the thread, waiting to obtain an exclusive lock on bean 101. |
The bean instance processes the request and returns. | The thread is blocked. |
The EJB container saves the bean instance’s state to the database. | The thread is blocked. |
The EJB container commits the transaction and removes the exclusive lock on bean 101. | The thread is blocked. |
The results are returned to the client. | The EJB container enlists bean 101 in the transaction and places an exclusive lock on bean 101 in the lock manager. |
The EJB container loads the data from the database into the bean instance. | |
The EJB container passes the request to a method on the bean instance. | |
The bean instance processes the request and returns. | |
The EJB container saves the bean instance’s state to the database. | |
The EJB container commits the transaction and removes the exclusive lock on bean 101. | |
The results are returned to the client. |
There are a number of problems with this concurrency strategy when applied to realistic, enterprise-class applications:
Most enterprise-class applications are deployed in a cluster, and each server instance in the cluster will deploy the EJB components in a separate EJB container. Because exclusive locking is performed by each EJB container and is local to that container only, there is no concurrency control across servers in the cluster. In other words, if two clients were talking to bean instances representing the same business object on two different servers, the second client would not be blocked by the first client. In effect, clients going to different servers use database concurrency while clients going to the same server use exclusive concurrency.
The EJB container does not discriminate between read and write operations for locking, so clients wishing to read data from the bean are blocked in the same way as clients performing updates. This can produce significant response-time degradation if contention occurs for the same entity beans.
Exclusive locking brings with it the problem of potential deadlocks. Deadlocks occur when two clients access a set of entity beans in a different order, such that each client thread is blocked waiting for the other client thread to release the lock it needs to proceed. Such deadlocks are difficult to avoid unless you take extreme care to ensure that all access to multiple entity beans proceeds in a consistent order in every component of the application.
Some of these issues are the result of requiring the EJB container to maintain strong transactional and isolation controls, or ACID properties, for the underlying data. The container cannot, for example, allow read access to a bean instance involved in another transaction without violating basic isolation rules. The exclusive concurrency strategy is no longer the default strategy in WebLogic Server. Use this strategy sparingly, if at all, and remember that the locking is on a per-container basis.
Best Practice | Avoid exclusive concurrency unless synchronizing access to the underlying data source is critical to the correct operation of the system. Recognize that clustered environments generally defeat exclusive locking because of its per-container implementation. Be aware of potential deadlocks when using exclusive locking, and control the order of lock acquisition throughout your application to avoid deadlocks. |
Configuring the exclusive concurrency strategy requires the following element in the entity-descriptor for the entity bean:
<entity-descriptor>
<entity-cache>
...
<concurrency-strategy>Exclusive</concurrency-strategy>
</entity-cache>
...
</entity-descriptor>
There are no other options or elements associated with the exclusive concurrency strategy. Note that this strategy may be combined with the cache between transactions caching strategy described later in this chapter in a single server environment.
Database Concurrency Strategy
The database concurrency strategy relies on the underlying database technology to reject changes made by two simultaneous transactions. Rather than enforce exclusive access to a given bean, the container gives all clients their own copies of the entity bean at the start of their individual transactions and allows the database to catch and reject attempts to access the underlying data in ways that violate the isolation level in use. Essentially, the database concurrency strategy in its simplest form represents no concurrency control at all in the EJB container.
If multiple clients obtain references to the same bean and call only get methods, as opposed to set methods, this concurrency strategy has no effect on the life cycle of the bean instances. Each bean is loaded, processes the get requests, and is removed from the cache. Because the CMP logic is smart enough to avoid executing an update statement when only get methods are called, no database updates occur and no concurrency issues exist. Each bean life cycle is independent. If multiple clients obtain references to the same bean and do call set methods on the bean, the final database-update step in the bean life cycle will be performed by one client before the others, and the database locking will prevent subsequent commits from succeeding under most conditions. Specific database technologies and isolation levels affect this behavior, of course, but Table 6.4 illustrates the normal case in more detail.
CLIENT #1 THREAD ACTIVITY | CLIENT #2 THREAD ACTIVITY |
---|---|
The client acquires a reference to bean 101. | The client acquires a reference to bean 101. |
The client invokes a method on the bean interface. | |
The EJB container intercepts the request, starts the transaction, instantiates bean 101 in the cache, and enlists bean 101 in the transaction. | |
The EJB container loads the data from the database into the bean instance. | The client invokes a method on the bean interface. |
The EJB container passes the request to a method on the bean instance. | The EJB container intercepts the request, starts the transaction, instantiates bean 101 in the cache, and enlists bean 101 in the transaction. |
The bean instance processes the request and returns. | The EJB container loads the data from the database into the bean instance. |
The EJB container saves the bean instance’s state to the database. | The EJB container passes the request to the method on the bean instance. |
The EJB container commits the transaction. | The bean instance processes the request and returns. |
The results are returned to the client. | The EJB container saves the bean instance’s state to the database. |
The EJB container attempts to commit the transaction. The database raises an exception because of the previous commit by Client #1. | |
The database exception is returned to the client. |
Because Client #1 performs the commit before Client #2, the transaction in the Client #2 thread is doomed to fail during either the database update step or the final transaction commit step. Where it fails depends on the specific database technology. The database concurrency strategy has two significant advantages over the exclusive strategy:
No locking or synchronization is performed by the EJB container. This eliminates the potential for deadlocks in the EJB container and improves performance considerably for applications that suffer from contention related to certain entity beans.
The database concurrency strategy works in a cluster because it relies on the RDBMS, a shared service, for concurrency control, rather than on the individual EJB containers.
There are three big disadvantages with the database concurrency strategy, however:
Transactions may fail during the database update or commit step in the bean life cycle, raising a database-related exception to the client. Client code must catch this exception and decide if it is safe to retry the entire transaction again. The entire transaction rolls back, not just the portion related to the entity bean that caused the exception.
WebLogic Server does not allow the use of the cache-between-transactions feature with this concurrency strategy. As you will see when this feature is covered later, cache-between-transactions eliminates the need to reload the data from the database for a bean found in the cache, potentially representing a large performance gain. Because the database concurrency strategy always provides and loads a new bean for each client, it is incompatible with this caching feature.
You are essentially moving any potential deadlocks from the EJB container down to the database.
Despite these disadvantages, database concurrency represents a solid starting point and is preferred over exclusive concurrency in most applications.
Best Practice | The database concurrency strategy is preferred over the exclusive strategy in most applications, but it suffers from the lack of support for caching data between transactions. |
The database concurrency strategy is the default in WebLogic 6.x and later, so failing to explicitly set the caching strategy on a bean causes WebLogic Server to use database concurrency. You may also declare the strategy explicitly using this element:
<entity-descriptor>
<entity-cache>
...
<concurrency-strategy>Database</concurrency-strategy>
</entity-cache>
...
</entity-descriptor>
Read-Only Concurrency Strategy
The read-only concurrency strategy in WebLogic 7.x and later is an improved version of the standard read-only entity beans available in WebLogic 5.x and 6.x. This improved read-only concurrency strategy provides a mechanism for the long-term caching of entity bean data in the EJB container, while eliminating the previous requirement for exclusive access to the cached bean instance.When using this strategy, the container will populate a particular bean instance the first time it is referenced by a client by reading the bean’s state from the database. Subject to max-beans-in-cache limitations, this bean instance then remains in memory indefinitely and is used by the container during subsequent invocations by new clients. In previous versions of WebLogic Server, this cached bean had to be accessed by clients using an exclusive locking scheme, creating the potential for deadlocks and performance bottlenecks. WebLogic Server 7.x and later changes the default behavior of read-only beans to avoid this exclusive locking by copying bean data from the cached bean instance to a separate instance for each client request.The EJB container never tries to save the bean’s state at the end of transactions involving read-only beans, so no changes can be made to the persistent data through these beans.An entity bean is declared to be read-only by setting the concurrency-strategy element to ReadOnly. Beans with this setting use the new caching mechanism that avoids exclusive locks on the cached bean data. To configure beans to use exclusive locking on the cached bean data, thereby simulating the behavior of read-only beans in WebLogic 5.x and 6.x, use the ReadOnlyExclusive concurrency strategy.The read-timeout-seconds element may also be included with both ReadOnly and ReadOnlyExclusive beans:
<entity-cache>
...
<read-timeout-seconds>600</read-timeout-seconds>
<concurrency-strategy>ReadOnly</concurrency-strategy>
</entity-cache>
This element adds another check during each method invocation on the cached bean to verify that the cached data has not expired. If the data is older than the time-out setting, the container reloads the bean’s state from the persistent store to refresh the cache. The method invocation that caused the refresh, and all subsequent calls by clients, will receive bean instances containing a copy of the updated data. The default value of read-timeout-seconds is 0, meaning that no time-out checks will occur and the bean’s state will be loaded from the persistent store only during initial bean creation and after explicit invalidation, a topic covered later in this chapter.It is also possible to configure entire groups of entity beans to have the ReadOnly or ReadOnlyExclusive concurrency strategy using the weblogic-application.xml descriptor file. This capability is part of the combined-caching-support feature introduced in WebLogic Server 7.0, which will be discussed later in this chapter.
Optimistic Concurrency Strategy
The optimistic concurrency strategy enforces concurrency using the classic client/server approach of ensuring that the underlying database rows have not been updated by any other client during the transaction. This strategy is available only for CMP entity beans.Optimistic locking schemes take advantage of the fact that most access to bean data is for read purposes. Each client thread is given its own copy of the entity bean, much like the database strategy, and checks are performed during the final database update to ensure that no changes have occurred in the database during the bean life cycle. We’ll discuss the specific types of checks that can be performed and the techniques used by WebLogic Server to ensure concurrency in a moment. Table 6.5 presents a simple example for two clients accessing and modifying the same bean.
CLIENT #1 THREAD ACTIVITY | CLIENT #2 THREAD ACTIVITY |
---|---|
The client acquires a reference to bean 101. | The client acquires a reference to bean 101. |
The client invokes a method on the bean interface. | |
The EJB container intercepts the request, starts the transaction, instantiates bean 101 in the cache, and enlists bean 101 in the transaction. | |
The EJB container loads the data from the database into the bean instance and saves key attribute values in the bean for concurrency checks in the database update step. | The client invokes a method on the bean interface. |
The EJB container passes the request to a method on the bean instance. | The EJB container intercepts the request, starts the transaction, instantiates bean101 in the cache, and enlists bean 101 in the transaction. |
The bean instance processes the request and returns. | The EJB container loads the data from the database into the bean instance and saves key attribute values in the bean for concurrency checks in the database update step. |
The EJB container performs the database update using the previous key attribute values in the WHERE clause, verifies that the update modified the row, and completes successfully. | The EJB container passes the request to a method on the bean instance. |
The EJB container commits the transaction. | The bean instance processes the request and returns. |
The results are returned to the client. | The EJB container performs the database update using the previous key attribute values in the WHERE clause, senses that the update didn’t modify the database, rolls back the transaction, and raises an exception. |
An OptimisticConcurrency Exception is returned to the client. |
The optimistic concurrency strategy ensures that the row in the database has not changed during the life cycle of the bean. This is accomplished in WebLogic Server by saving, in the entity bean instance, the values of specific fields as they existed during the database read invocation. These saved values are then used in database update operations to verify that the database row has not changed by including them in the WHERE clause of the SQL UPDATE statement.WebLogic Server supports four different techniques for checking the saved values in the bean instance against the current contents of the database row:
Check all fields read during transaction, which requires that all of the fields read from the database during the transaction still have their original values in the database row.
Check fields modified during transaction, which requires that any fields being modified by the UPDATE statement itself still have their original values in the database row.
Check a version column, which requires that a specific numeric column in the mapped table still contains the value it did during the database read.
Check a timestamp column, which requires that a specific timestamp column in the mapped table still contains the value it did during the database read.
The Optimistic strategy is declared in the weblogic-ejb-jar.xml file:
<entity-cache>
...
<concurrency-strategy>Optimistic</concurrency-strategy>
</entity-cache>
The technique used to validate the bean values during the database update process is defined in the CMP descriptor file, weblogic-cmp-rdbms-jar.xml, in a verify-columns element inside the table mapping section:
<weblogic-rdbms-jar>
<weblogic-rdbms-bean>
<ejb-name>PersonCMPEJB</ejb-name>
<data-source-name>MasteringDataSource</data-source-name>
<table-map>
<table-name>PERSON</table-name>
<field-map>
<cmp-field>id</cmp-field>
<dbms-column>ID</dbms-column>
</field-map>
...
<field-map>
<cmp-field>lastName</cmp-field>
<dbms-column>LASTNAME</dbms-column>
</field-map>
<verify-columns>Modified</verify-columns>
</table-map>
</weblogic-rdbms-bean>
</weblogic-rdbms-jar>
Valid values for verify-columns are Read, Modified, Version, and Timestamp.The Read and Modified techniques require no other elements in the descriptor because the container will construct the WHERE clause used in the row update based on columns read or modified as appropriate. The Version and Timestamp techniques require an additional optimistic-column element in the table mapping section defining the table column used for these techniques:
<table-map>
<table-name>PERSON</table-name>
...
<verify-columns>Timestamp</verify-columns>
<optimistic-column>LAST_MODIFIED</optimistic-column>
</table-map>
The column used for Version or Timestamp checking may be included in the container-managed fields for the CMP bean, but this is not required. You may not call the set method for this field.
Best Practice | The Version and Timestamp techniques are the most direct implementations of optimistic concurrency and are recommended for most applications. The Read and Modified techniques work well if there are no timestamp or version columns available in the table, but they may create long WHERE clauses that must be parsed and evaluated by the underlying database system for every update, hurting performance. |
Regardless of the technique you use, WebLogic Server includes appropriate additional criteria on the WHERE clause in the internal database update operation to enforce concurrency. For example, if the PersonCMPBean defined earlier in this chapter used optimistic locking with a Modified technique, and the firstName and lastName attributes were modified during the bean life cycle, the executed SQL statement would be as follows:
UPDATE PERSON SET FIRSTNAME = ? , LASTNAME = ?
WHERE ID = ? AND FIRSTNAME = ? AND LASTNAME = ?
If this statement fails to update a row in the database, the EJB container assumes another client has either deleted the row or another client or process changed one of the modified fields. In either case, there is a concurrency problem, and the container throws an OptimisticConcurrencyException:
weblogic.ejb.OptimisticConcurrencyException:
Optimistic concurrency violation.
Instance of bean ‘PersonCMPEJB’ with primary key ‘101’ was changed by
another transaction.
It is up to the caller performing the business method that caused the exception to determine if it is safe to retry the operation by starting the transaction again, reacquiring the bean instance with updated data from the database, and reapplying the desired changes. Normally, it is not safe simply to reapply the changes without asking for user permission. It may also be necessary to check that the row still exists in the database to determine the proper course of action. The safest technique is to report the concurrency exception to the user and ask him or her to determine the correct action.
Best Practice | Include exception-handling code in client applications and session faade beans to trap and handle OptimisticConcurrency Exceptions thrown by the container at the end of a bean life cycle. In some cases, the operation can simply be retried, but often the user must be informed of the error and given the opportunity to select a course of action. |
The optimistic strategy allows the use of the cache-between-transactions feature, discussed next, to eliminate unnecessary database reads if the bean is already present in the cache.One final nuance related to optimistic concurrency and Web applications is worth discussing: There is a difference between enforcing concurrency in the context of a container transaction and ensuring that multiple Web users do not perform conflicting updates. To see the difference, consider a typical Web application having an HTML form used to edit bean data. The form might be populated with entity bean data during one transaction, sent to the user’s browser for update, posted to a controller or action JSP page after changes are made, and used to modify bean data during a second transaction. Optimistic concurrency ensures integrity during each of the two transactions, but it does not preclude two users from viewing the same bean simultaneously and submitting conflicting changes one at a time. To understand why, recall that optimistic concurrency rereads the bean data from the database, including the version or timestamp column, at the start of each of the two transactions for each user individually. The second transaction will succeed for both users because the checked columns will not be changing during the duration of the second transaction.
If this scenario is a concern you need to supplement the optimistic concurrency behavior in WebLogic Server with one additional check: The version or timestamp in the database must not be different from the value read during the original HTML form generation. The easiest way to implement this check is to include on the HTML form a hidden field containing the version number or timestamp and then compare this value with the value read from the database at the start of the second transaction. If they are different, some other process modified the data between the time the form was created and the time it was submitted, exactly what you are trying to catch and prevent. The combination of this type of application-level check and the server-based optimistic concurrency logic provides a strong level of concurrency control for your application.
Warning | Optimistic concurrency in WebLogic Server’s EJB container handles concurrency issues only within a single transaction context. Most distributed applications may require some sort of optimistic concurrency control across multiple transactions to prevent data loss or corruption. Implement this cross-transaction checking by verifying that the version of the data displayed to the user hasn’t changed. Perform this verification at the beginning of the update transaction before making any changes to the underlying data. |
Caching Strategies
The following sections describe caching techniques available in the WebLogic Server EJB container. Note that caching in the container will improve performance for applications with the right types of EJB operations, but it is no silver bullet. It may add complexity for little or no benefit in applications that perform mostly write operations, for example, or consume heap space better used by other components in the application. Our goal in this section is to help you understand the specific advantages, disadvantages, and ramifications of each caching technique to aid in your evaluation and implementation of these strategies.
Caching between Transactions
The normal life cycle of an entity bean includes a database read operation at the beginning and a database update operation at the end of the life cycle. The read-only concurrency strategy was one mechanism for avoiding the read operation for beans with data suitable for long-term caching. WebLogic Server also provides a second mechanism for avoiding database reads, the cache-between-transactions feature, which essentially caches bean data in the EJB container for use by multiple clients. This feature replaces the db-is-shared descriptor element available in WebLogic Server before version 7.0.An entity bean configured to use caching between transactions will be loaded from the database only in the following circumstances:
No bean instance containing cached data is available in the cache. This might be the first client to access the bean in this EJB container, or the cache management algorithms may have flushed the bean from the cache to manage memory.
The entity bean is involved in a transaction that is rolled back. The container must reload the contents of the bean from the database because no backup copy is kept to reset the bean to its original state.
A finder method other than findByPrimaryKey() is invoked to locate matching beans, and the bean is configured with finders-loan-bean set to true. This feature will be discussed later in this chapter.
Optimistic concurrency is used in conjunction with caching between transactions in a clustered environment, and a different EJB container in the cluster has informed this container that a client updated the bean.
Configuring a bean to use caching between transactions is accomplished by setting the cache-between-transactions element in the entity-cache to true:
<entity-cache>
<concurrency-strategy>Optimistic</concurrency-strategy>
<cache-between-transactions>True</cache-between-transactions>
</entity-cache>
Caching between transactions is not available for all concurrency strategies in all deployment architectures. As Table 6.6 shows, only the optimistic concurrency strategy allows caching between transactions in both clustered and nonclustered environments.
CONCURRENCY STRATEGY | CBT ALLOWED IN NONCLUSTERED ENVIRONMENT? | CBT ALLOWED IN CLUSTERED ENVIRONMENT? |
---|---|---|
Exclusive | Yes, and exclusive locking behavior is maintained on the cached bean instance. | No, containers have no mechanism to publish changes to others. |
Database | No. | No. |
Read-only | Not applicable; read-only beans have different rules for database reads based on time-outs. | Not applicable; read-only beans have different rules for database reads based on time-outs. |
Optimistic | Yes. | Yes, containers inform each other via multicast when bean data changes. |
The exclusive concurrency strategy may be combined with caching between transactions to eliminate the need to load bean data from the database at the start of the bean life cycle. This combination is available only in a single-server environment at the current time.
The optimistic concurrency strategy, combined with caching between transactions, provides a very powerful mechanism for obtaining high performance with a minimal chance for data corruption. Recall that the optimistic strategy does not rely on locking, within either the EJB container or the database, to avoid concurrency conflicts proactively. Instead, the strategy senses conflicts during the database update process, using one of the four techniques described earlier, and works very effectively in a clustered environment or in the presence of other applications or processes performing updates to the database.In addition, when bean changes are committed to the database in one EJB container, a multicast approach is used to invalidate cached beans in all other containers in the cluster. The next time a client in these containers uses an instance of the invalidated bean, its data will be retrieved from the database to ensure accuracy and avoid concurrency conflicts during subsequent updates.The only serious problem with the combination of optimistic concurrency and caching between transactions is the potential for stale data in the cached beans if changes are made to the database through applications or processes not participating in the WebLogic Server cluster. The cached data will continue to be used by clients for read purposes, and the staleness of the data will not be sensed until a client attempts to update something in the database and produces a concurrency conflict. The potential for stale data is a price you pay for the performance benefits of caching, a fair trade in many cases.
Best Practice | Combining caching between transactions with optimistic concurrency provides a very powerful caching mechanism that is cluster-aware and very efficient. Use caching between transactions with optimistic concurrency as your default configuration for entity beans in WebLogic Server unless application requirements dictate otherwise. |
Read-Only Multicast Invalidation
Entity beans configured with read-only concurrency provide a convenient long-term caching facility and, as we will discuss in the next section, can be combined with standard entity beans to form a matched pair of beans in what is called a read-mostly pattern. Before discussing this pattern, however, we want to address the simpler case of read-only beans deployed alone and identify the mechanisms available for invalidating cached data.The first invalidation mechanism, the read-timeout-seconds configuration parameter, was discussed earlier in this chapter. The read-timeout-seconds parameter provides a mechanism for automatic, periodic invalidation of cached data. After the time-out period expires, the next client request will cause the bean data to be reloaded from the database during that request. Note that read-timeout-seconds is not a timer; Nothing actually happens when the time-out expires. The bean is simply invalid the next time it is used. No database read activity will take place until a client uses a reference to the bean with expired contents.
The second invalidation mechanism, multicast invalidation, allows the programmatic invalidation of cached entity bean data on all servers in the WebLogic Server cluster. Invalidation is accomplished by obtaining a reference to the home or local home interface for the read-only entity bean and using one of the invalidate methods defined in the CachingHome or CachingLocalHome interfaces:
package weblogic.ejb;
public interface CachingHome
{
public void invalidate(Object pk) throws RemoteException;
public void invalidate(Collection pks) throws RemoteException;
public void invalidateAll() throws RemoteException;
}
As indicated in this interface definition, you may invalidate one bean, a collection of beans, or all read-only beans of the type associated with this home interface. For example, the following code invalidates a specific read-only PersonCMPROBean instance using the CachingLocalHome for the bean:
CachingLocalHome home =
(CachingLocalHome)ctx.lookup(“PersonCMPROHomeLocal”);
home.invalidate(new Integer(101));
The home interface will not extend the CachingHome or CachingLocalHome interface unless the bean uses read-only concurrency. This multicast invalidation technique is not available for any other concurrency strategies. The ability to invalidate cached data programmatically is one of the only reasons to consider read-only concurrency and its close relative, the read-mostly pattern.
Read-Mostly Pattern
The read-mostly pattern is a specific entity bean deployment approach in which a matched pair of entity beans, one using read-only concurrency and one using exclusive, database, or optimistic concurrency, represents the same persistent data. As illustrated in Figure 6.4, clients requiring read access to bean data use the read-only bean to take advantage of the automatic caching provided by that strategy. Clients requiring traditional read and write access to bean data use the standard entity bean deployed with one of the other types of concurrency. Sounds great, but how is the read-only data kept in synch with the actual data being modified through the standard entity bean?

Figure 6.4: Read-mostly pattern.
A key element of this deployment approach is the automatic invalidation facility provided by WebLogic Server. Based on the multicast invalidation capability discussed in the previous section, automatic invalidation ensures that attribute changes made through the standard entity bean are reflected in the read-only bean the next time it is accessed by a client. The standard bean is linked to the read-only bean through an invalidation-target element in the
weblogic-ejb-jar.xml file:
<weblogic-enterprise-bean>
<ejb-name>PersonCMPEJB</ejb-name>
<entity-descriptor>
<entity-cache>
<concurrency-strategy>Optimistic</concurrency-strategy>
</entity-cache>
<persistence>
...
</persistence>
<invalidation-target>
<ejb-name>PersonCMPROEJB</ejb-name>
</invalidation-target>
</entity-descriptor>
<local-jndi-name>PersonCMPHomeLocal</local-jndi-name>
</weblogic-enterprise-bean>
<weblogic-enterprise-bean>
<ejb-name>PersonCMPROEJB</ejb-name>
<entity-descriptor>
<entity-cache>
<read-timeout-seconds>600</read-timeout-seconds>
<concurrency-strategy>ReadOnly</concurrency-strategy>
</entity-cache>
...
</entity-descriptor>
<local-jndi-name>PersonCMPROHomeLocal</local-jndi-name>
</weblogic-enterprise-bean>
After the transaction involving the standard bean commits, the EJB container uses multicast to invalidate the read-only bean on all servers in the cluster. This invalidation operation occurs after the transaction is committed, to eliminate the potential for race conditions in which a read-only bean reloads its data from the database before the updates are committed, possibly loading old values again.
Implementing the Read-Mostly Pattern
The read-mostly pattern is commonly implemented using two separate CMP entity beans configured to read from and write to the same database table. This dual-bean-class technique is fairly straightforward and won’t be covered in this text.As an alternative to the dual-bean-class technique, the read-mostly pattern can also be implemented by deploying the same bean twice, once as a standard bean and once as a read-only bean, using the same bean and interfaces classes for each deployment. For example, the PersonCMPBean component discussed earlier in this chapter can be deployed in a read-mostly pattern by modifying the descriptor files to declare two separate EJB components that utilize the same bean, local interface, and home interface classes. Because we are deploying the same bean class twice, both deployments must define the same CMP fields, finders, and home/select methods for the EJB compilation process to work properly. The companion Web site provides example descriptors showing how to deploy the PersonCMPBean component twice in this fashion.Clients that wish to access the read-only version of the bean look up the home interface using the JNDI name used for that deployment, PersonCMPROHomeLocal, whereas clients that require the standard read-write version of the bean use the JNDI name PersonCMPHomeLocal. In both cases, the home interface obtained by the lookup will implement the same interface, PersonCMPHomeLocal, and will define the same set of finder methods. The bean reference returned by either home interface will implement the same local interface, PersonCMPLocal, eliminating the need for client code to reflect which type of entity bean is being used. It therefore becomes much easier to add the read-mostly pattern to an application later if the single-bean-class approach is used because the existing code can continue to use the same local interface.
Best Practice | The single-bean-class technique allows the configuration of the read-mostly pattern with a minimum of impact on client code and is preferred over the creation of multiple bean classes to implement the pattern. |
One downside of the single-bean-class technique is the fact that the read-only bean still contains set methods, and the single shared local interface for the bean also makes these methods available to clients. If a client should modify the bean data using a reference to the read-only bean, the change will be reflected in the cached bean data on the current server, but not saved to the database or propagated to other servers in the cluster. The next time the read-only bean is invalidated, the bean data will be reread from the database and the errant change will disappear. Be careful, therefore, to perform no set operations on read-only bean references when using the single-bean-class technique.
Warning | Clients must be careful to perform set operations on standard bean references, rather than read-only bean references, to avoid losing changes. |
BMP entity beans can avoid this problem by modifying the set methods in the shared bean class to assert that they are not being called on an instance of the bean deployed using read-only concurrency, using code similar to the following:
if (entityContext.getEJBHome() instanceof CachingHome) {
throw new EJBException(“Attempt to call set on ReadOnly bean!”);
}
The container for CMP entity beans generates set methods automatically, so this safety code cannot be added directly to these methods. You might consider creating separate set methods that perform this check and call the internal set methods, placing only the safe versions of the methods in the local interface for the bean, as one reasonable solution.
Note | Nothing in the read-mostly pattern dictates that the two beans must have the same attributes, methods, or even that they must read from the same database table. The read-only bean, for example, might have only a subset of the attributes or might include different methods for returning value objects or collections of bean attributes.Remember that the container does not attempt to copy data from the standard bean to the read-only bean during invalidation; it simply marks the read-only bean invalid and relies on a database read to refresh the cached data. Any standard CMP entity bean can define an invalidation-target element referencing any read-only entity bean in the same EJB archive. |
The standard entity bean in the read-mostly pattern may use any of the other concurrency strategies available in WebLogic Server: exclusive, database, or optimistic. The standard bean may also employ the caching-between-transactions feature, in some cases, to further reduce the number of database reads required when clients employ the standard entity bean.
Relationships and the Read-Mostly Pattern
A number of problems occur when you use read-only entity beans in CMP-managed relationships.First, you can’t mix relationships between standard beans and read-only beans in the application. Relationships between CMP entity beans are declared in the ejb-jar.xml descriptor file using relationship-role-source elements referring to specific EJB components by name:
<relationships>
<ejb-relation>
<ejb-relationship-role>
...
<relationship-role-source>
<ejb-name>AddressCMPEJB</ejb-name>
</relationship-role-source>
...
Note that the relationships are defined using the EJB names, not their local interfaces, so it is not possible to declare a relationship with both the standard and the read-only version of the other bean. Each relationship must be declared to use one version or the other, so typically the entire graph of related beans will exist in an all-standard form and in an all-read-only form. The single-bean-class technique described previously does not help with this problem. Although the underlying beans are the same in both cases, they are deployed twice using different EJB names, requiring duplicate graphs of related beans.A second problem is that the read-mostly invalidation scheme does not produce reliable invalidation of bidirectional relationships. For example, an update to the standard PersonCMP bean removing a particular Address from its addresses relationship collection would cause the related read-only PersonCMPRO bean to become invalidated, but there is no facility for automatically invalidating the related AddressCMPRO bean to ensure that the now-defunct relationship is not traversed in the other direction.Of course, you can still use read-only beans with relationships if you are not using the read-mostly pattern and do not require cluster-wide invalidation. The choice really depends on your application’s requirements.
Best Practice | Based on the inherent limitations when declaring relationships involving read-only beans and a minor invalidation problem, we don’t recommend that you use read-only beans when relationships are present between beans. Standard beans using cache between transactions are preferable in this situation in most cases. |
The bottom line on the read-mostly pattern is that it still has some value in cases where programmatic invalidation of cached data is important, a feature supported only by read-only beans, but in general it has been superceded by the optimistic concurrency and cache-between-transactions approach for most applications.
Combined Caching Support
WebLogic Server supports the creation of combined, or shared, caches for use by multiple entity beans in place of the standard per-bean cache. All entity beans configured to use a particular combined cache will share the space in the combined cache and be subject to passivation or removal using the same rules. The combined cache eliminates the need to determine an appropriate size for each per-bean cache and may result in more efficient use of overall system memory.The standard per-bean cache was configured in weblogic-ejb-jar.xml using the entity-cache element:
<weblogic-enterprise-bean>
<ejb-name>PersonCMPEJB</ejb-name>
<entity-descriptor>
<entity-cache>
<max-beans-in-cache>1000</max-beans-in-cache>
<concurrency-strategy>Optimistic</concurrency-strategy>
</entity-cache>
...
</entity-descriptor>
...
</weblogic-enterprise-bean>
In this configuration, the PersonCMPEJB bean will be cached in a unique cache containing a maximum of 1,000 bean instances and will use the optimistic concurrency strategy. Other beans in the application that have similar descriptor elements would likewise be cached in their own per-bean caches.To use a combined cache, the entity-cache element is replaced with an entity-cache-ref element referencing the name of the combined cache and specifying the bean’s concurrency strategy:
<entity-descriptor>
<entity-cache-ref>
<entity-cache-name>BigCache</entity-cache-name>
<concurrency-strategy>Optimistic</concurrency-strategy>
</entity-cache-ref>
...
</entity-descriptor>
The combined cache is configured using the weblogic-application.xml descriptor in the META-INF directory of the enterprise application archive (.ear) file or exploded structure. The weblogic-application.xml file shown in Listing 6.7 defines a single large cache for use by entity beans in the application referencing this cache rather than defining a per-bean cache.Listing 6.7: weblogic-application.xml defining a combined cache.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE weblogic-application PUBLIC
"-//BEA Systems, Inc.//DTD WebLogic Application 8.1.0//EN"
"http://www.bea.com/servers/wls810/dtd/weblogic-application_2_0.dtd">
<weblogic-application>
<ejb>
<entity-cache>
<entity-cache-name>BigCache</entity-cache-name>
<max-beans-in-cache>50000</max-beans-in-cache>
<read-timeout-seconds>600</read-timeout-seconds>
<caching-strategy>MultiVersion</caching-strategy>
</entity-cache>
</ejb>
</weblogic-application>
Detailed descriptions of the XML elements in weblogic-ejb-jar.xml and weblogic-application.xml are available in the online documentation at http://edocs.bea.com/wls/docs81/ejb/referencel and http://edocs.bea.com/wls/ docs81/programming/app_xmll, respectively. Some key capabilities and rules include the following:
The combined cache may be configured using a maximum number of bean instances or by limiting the approximate size of the cache. Note that the size option requires that each bean define its average size using the estimated-bean-size tag to estimate the total size of the cache for the purposes of cache management. Beans that do not define their size are assumed to consume 100 bytes of memory for this calculation.
The combined cache may be configured using the exclusive caching strategy, equivalent to the exclusive concurrency strategy on a per-bean cache, or the multiversion caching strategy, which does not lock bean instances. Beans declaring the exclusive concurrency strategy may only use a combined cache using exclusive, but all other bean strategies may be hosted in a multiversion combined cache.
Multiple combined caches may be defined providing that their names are unique.
Combined caching represents a significant improvement in both flexibility and ease of configuration. No longer must you estimate the appropriate cache size on a per-bean basis or resign yourself to oversizing most per-bean caches and wasting memory.
Best Practice | Consider using combined caches for most beans, configuring the size of the combined caches to accommodate expected instance requirements. Use separate bean caches for selected high-use or resource-intensive beans to better control the use of cache space. |
Specific beans can still be configured with per-bean caches, as before, and all beans of a certain type might be placed in a single combined cache (a ReadOnlyCache, for example). Note that read-only beans sharing a single combined cache will have the same read-timeout-seconds value because the value is defined in the combined cache and is not configurable on a per-bean basis in the entity-cache-ref element.Sizing entity-bean caches will be discussed in Chapter 12 as part of performance tuning and management.
Tuning Strategies
This section will describe some entity-bean tuning strategies available in WebLogic Server to improve query and database-read operations.
Loading Beans During Finder Operations
One of the classic problems with entity beans involves the n + 1 query problem. Fetching and displaying the contents of n beans normally requires one SQL query to fetch the list of primary keys and then n additional single-row SQL statements to fetch the bean data. Setting finders-load-bean to true can dramatically improve the performance of finder methods that return multiple beans because WebLogic Server will optimize the finder to fetch all of the bean data during a single SQL statement.
Normal finders-load-bean Behavior
The following client code uses a simple finder method on the PersonCMP entity bean to retrieve and display people having a particular last name:
Collection people = home.findByLastName(“Sm%”);
Iterator i = people.iterator();
while (i.hasNext()) {
PersonCMPLocal person = (PersonCMPLocal) i.next();
out.print(“ID: “ + person.getId() + “ Name: “ +
person.getSalutation() + “ “ + person.getFirstName() +
“ “ + person.getLastName() + “<br>”);
}
Using a standard CMP entity bean with optimistic concurrency and finders-load-bean set to false, inspection of the calls made by the EJB container reveals that the following four SQL statements are required to fetch and display the three matching people in the database:
... : Finder produced statement string
SELECT WL0.ID FROM PERSON WL0 WHERE ( (WL0.LASTNAME LIKE ? ) )
... __WL_loadGroup0 for pk=102
... execute Query: SELECT WL0.FIRSTNAME, WL0.ID, WL0.LASTNAME,
WL0.MIDDLENAME, WL0.SALUTATION FROM PERSON WL0 WHERE WL0.ID = ?
... __WL_loadGroup0 for pk=105
... execute Query: SELECT WL0.FIRSTNAME, WL0.ID, WL0.LASTNAME,
WL0.MIDDLENAME, WL0.SALUTATION FROM PERSON WL0 WHERE WL0.ID = ?
... __WL_loadGroup0 for pk=106
... execute Query: SELECT WL0.FIRSTNAME, WL0.ID, WL0.LASTNAME,
WL0.MIDDLENAME, WL0.SALUTATION FROM PERSON WL0 WHERE WL0.ID = ?
The same code executed on a standard optimistic bean with finders-load-bean set to true results in a single SQL statement:
... : Finder produced statement string
SELECT WL0.ID, WL0.FIRSTNAME, WL0.LASTNAME, WL0.MIDDLENAME, WL0.SALUTATION
FROM PERSON WL0 WHERE ( (WL0.LASTNAME LIKE ? ) )
The result set returned by this query is used by WebLogic Server to preinstantiate and preload all of the beans returned by the finder with data at the time of the finder operation. The beans will be placed in the entity bean cache in the normal manner, and subsequent method invocations using the bean references returned by the finder will use the cached version of the bean. Unless cache between transactions is enabled for this bean, the cached beans are valid only in the context of the transaction used to execute the finder method. The finder and all subsequent bean-method invocations should therefore be in a single transaction to take full advantage of finders-load-bean=true.
Generally speaking, it is best to leave the finders-load-bean parameter set to the default value of true. Preloading retrieved beans avoids the n + 1 query problem and provides better performance and scalability in most circumstances. The only exceptions are cases in which the resulting collection will be used in some manner not requiring a complete iteration and inspection of bean data. Examples might include displaying only the first few matching beans, performing the finder in one transaction but accessing the bean data in a different transaction, or passing the collection to a relationship management method on a bean. The finders-load-bean value is defined for the entity bean as a whole, unfortunately, so there is no way to specify the setting differently for specific finders.
Best Practice | Use the default finders-load-bean value of true for most applications to eliminate the n + 1 query problem and improve system performance. |
The internal operation of finders using finders-load-bean=true becomes more complex when you consider the many options for caching and concurrency strategy in combination with finders-load-bean. We’ll discuss a few of the more common combinations.
finders-load-bean with Read-Only Beans
Entity beans using the read-only concurrency strategy are cached for long periods of time to avoid repeated database reads and improve performance. Finder methods used to retrieve read-only bean references or collections of bean references are affected by the finders-load-bean parameter according to Table 6.7.
FINDER TYPE | FINDERS-LOAD-BEAN=TRUE | FINDERS-LOAD-BEAN=FALSE |
---|---|---|
Find by primary key | The cached bean reference is returned without performing the SQL statement. | The cached bean reference is returned without performing the SQL statement. |
All other finders | The full SQL select statement is performed for all finders, and all bean instances are refreshed with fetched data. The query is performed regardless of cached instances. | The key-only SQL select statement is performed for all finders, and references are returned to the caller. Calls to cached beans avoid subsequent database read operations. |
One interesting side-effect of the behavior with read-only beans is that the use of a findAll() finder method with finders-load-bean set to true will essentially fetch and refresh all cached read-only beans immediately with a single SQL statement. The CachingHome.invalidate() method of invalidation simply marks the beans invalid and defers the retrieval of new data until each bean is used again by a client, potentially requiring many individual queries. Remember, however, that the invalidate() method invalidates beans in all EJB containers in a cluster, but using finders would not affect other servers in the cluster.
Best Practice | Consider using finder methods with read-only beans to force the refresh of all matching beans in the cache immediately as a complement for normal invalidation. |
finders-load-bean with Cache between Transactions
The cache-between-transactions feature caches bean instances in the entity bean cache and makes them available for subsequent client requests, reducing the number of database read operations and improving performance. Finder methods used to retrieve bean references or collections of bean references for optimistic or exclusive concurrency beans using the cache-between-transactions feature are affected by the finders-load-bean parameter, according to Table 6.8.
FINDER TYPE | FINDERS-LOAD-BEAN=TRUE | FINDERS-LOAD-BEAN=FALSE |
---|---|---|
Find by primary key | The cached bean reference is returned without performing the SQL statement. | The key-only SQL select statement is performed to validate the key in database. A cached bean reference is returned. |
All other finders | The full SQL select statement is performed for all finders, and all bean instances are refreshed with fetched data. The query is performed regardless of cached instances. | The key-only SQL select statement is performed for all finders, and references are returned to the caller. Calls to cached beans avoid subsequent database read operations. |
Choosing the right setting for finders-load-bean was easy for beans not employing caching: Always use true. It is not quite as clear-cut for beans using cache between transactions, however, because non-primary-key finders always reread all data for cached beans if finders-load-bean is true. Preloading bean data during the finder will be more efficient than going back to the database for each bean if the matching beans are not yet in the cache, but subsequent finders would be faster without finders-load-bean set to true because the beans are already in the cache. The right setting therefore depends on the number of times the same cached beans might be accessed through the finder: If the same beans are accessed many times, leaving finders-load-bean set to false may give you better performance.
Updating Database after Each Method Invocation
The normal bean life cycle, described earlier in this chapter, includes a database write operation as part of the final transaction completion. Delaying these updates until the end of the transaction makes sense in most cases. In certain circumstances, however, you may need to modify the default behavior and have changes written to the database after every method invocation. This behavior is controlled by the delay-updates-until-end-of-tx element in the weblogic-ejb-jar.xml descriptor file:
<entity-descriptor>
<persistence>
<delay-updates-until-end-of-tx>false</delay-updates-until-end-of-tx>
</persistence>
</entity-descriptor>
Setting this parameter to false will cause changes to be written to the database after every method invocation. Depending on the type of entity bean, CMP or BMP, and the specific implementation of ejbStore(), this may or may not cause a SQL update statement to be executed against the database if no changes in the bean are sensed by the relevant code. If a SQL statement is sent to the database, it will still remain uncommitted until the end of the transaction.The default value, true, is suitable for most applications. Setting it to false can help with finders and other queries performed during the transaction that are not properly reflecting modified beans. For example, a JDBC query occurring after a change to bean data, but within the same transaction, will not reflect the bean changes unless this flag is set to false. If finder methods are the only queries for which you need this behavior, then you should use the include-updates functionality instead of setting delay-updates-until-end-of-tx to false. The next section discusses the include-updates functionality for EJB finder queries.
Best Practice | Leave delay-updates-until-end-of-tx with the default value of true unless specific application needs warrant the change. |
Including Updates in Finder Queries
Finder queries are executed using SQL statements sent to the database in the context of the current transaction. Because the default behavior of entity beans is to delay database writes until the end of the current transaction, queries performed in the transaction will typically not reflect updates to bean data made earlier in the transaction. For example, an application may perform the following steps:
Start a transaction.
Find Person bean #102 by primary key, and update its last name from “Anderson” to “Smith” using setLastName(“Smith”) on the bean reference.
Perform a findByLastName(“Sm%”) finder query, where the finder is defined using a LIKE comparison, and then display the contents of each bean matching the criteria.
End the transaction.
If the include-updates element in the findByLastName() query is set to false, the output in Step 3 will not include bean #102 because the updates to the bean have not been flushed to the database prior to executing the query. Setting include-updates to true in the finder definition causes all beans with unwritten changes in the current transaction to be flushed to the database, without committing the transaction, prior to executing the finder query. Step 3 would then include bean #102 in the output because it now matches the criteria.Note that the default value of include-updates was false in previous versions of Weblogic Server, but it is now true in WebLogic Server 8.1 for beans using the database and exclusive concurrency strategies. The default remains false in optimistic concurrency. This change was made in order to comply with the J2EE licensing model. This means that the default behavior starting in WebLogic Server 8.1 is to flush unwritten changes to the database prior to executing finder queries for beans using database and exclusive concurrency strategies. While this represents the J2EE-specified behavior, it adds overhead for applications that do not require this functionality. If your application does not require this behavior, set the includes-update parameters explicitly to false to improve performance.
Warning | J2EE licensing restrictions have caused the WebLogic Server 8.1 default setting for includes-update to change from false to true for database and exclusive concurrency strategies. If your application doesn’t depend on this behavior, setting the value back to false can improve performance. The default remains false in optimistic concurrency. |
The include-updates element is set in the WebLogic-specific CMP descriptor, weblogic-cmp-rdbms-jar.xml, using a finder definition that matches the definition in the standard ejb-jar.xml file. The findByLastName() finder is defined in ejb-jar.xml using a query element:
<query>
<query-method>
<method-name>findByLastName</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</query-method>
<ejb-ql>
SELECT OBJECT(o) FROM PersonCMPEJB o WHERE o.lastName LIKE ?1
</ejb-ql>
</query>
The WebLogic-specific file contains a matching weblogic-query element:
<weblogic-query>
<query-method>
<method-name>findByLastName</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</query-method>
<include-updates>false</include-updates>
</weblogic-query>
The include-updates behavior is not the default for optimistic concurrency, even in WebLogic Server 8.1, because include-updates tends to defeat some of the benefits of optimistic concurrency—namely, not holding locks on database rows during a transaction. When updates are flushed to the database before a finder call, update locks are held in the database. Worse yet, the finder queries must be run as part of the global transaction to be able to see uncommitted updates, and this causes read locks to be acquired for many databases.WebLogic Server 8.1 currently allows include-updates to be true with optimistic concurrency for only one database type, Oracle, because Oracle does not hold read locks and is less likely to defeat the benefits of optimistic concurrency. The database type must be set at the bottom of the weblogic-cmp-rdbms-jar.xml descriptor file:
<weblogic-rdbms-jar>
...
<database-type>ORACLE</database-type>
</weblogic-rdbms-jar>
Batching Database Operations
WebLogic Server 8.1 provides automatic support for batching database operations involving CMP entity beans. This feature delays all database operations until the end of the enclosing transaction and uses the batch update capability of the JDBC driver to perform many SQL operations with a single call to the database. Batching is enabled using the enable-batch-operations element in the weblogic- cmp-rdbms-jar.xml descriptor file:
<weblogic-rdbms-jar>
<weblogic-rdbms-bean>
<ejb-name>AddressCMPEJB</ejb-name>
...
</weblogic-rdbms-bean>
<enable-batch-operations>
True
</enable-batch-operations>
</weblogic-rdbms-jar>
Note that the enable-batch-operations element is defined once for the entire set of beans, rather than on a per-bean basis. The default value is True, so by default WebLogic Server will attempt to batch operations at the end of the transaction.There are several restrictions related to batching:
The JDBC driver must support the addBatch() and executeBatch() methods. WebLogic Server will disable batching and log an error message if the driver used for CMP operations does not support batching.
Certain types of automatic key generation, a topic discussed later in this chapter, are not compatible with batching. Attempting to use the SQLSERVER technique for key generation will disable batching.
The total number of entity beans involved in the transaction, and therefore the batched database operation, must not exceed the maximum cache size specified by the max-beans-in-cache element.
Entity beans containing OracleClob and OracleBlob data types will not be processed using batched database operations.
One final complication involves the proper ordering of all database operations performed in the batch update. Your beans may have many relationships and dependencies, and these dependencies are likely to be implemented in the database as foreign-key constraints. The database operations must be performed in the proper order to avoid constraint-violation errors. WebLogic Server 8.1 implements a dependency-checking algorithm that uses CMR information in the entity beans to determine the proper order of operations. This feature is enabled by default, but it may be disabled using the order-database-operations element in weblogic-cmp-rdbms-jar.xml.
Best Practice | Batching database operations can provide a significant benefit when many beans are involved in the transaction. Use automatic batching in your applications to eliminate unnecessary database calls and improve performance. |
Controlling Timing of Database Inserts
New CMP entity beans are created using one of the create() methods defined on the bean’s home interface. During the creation process, the EJB container calls the matching ejbCreate() and ejbPostCreate() methods on the bean class to allow the bean to initialize attributes and relationships using the parameters supplied on the create() invocation.
At some point in the creation process, the bean must be inserted in the database. The batching capability outlined in the previous section treats these insert operations like any other SQL operation and postpones them to the end of the transaction, if possible. If batching is disabled, via the enable-batch-operations element, or is not possible for one of the reasons described in the previous section, WebLogic Server provides an alternative technique for controlling the timing of this insert operation. The timing can be set on a per-bean basis using the delay-database-insert-until element in the weblogic-cmp-rdbms-jar.xml descriptor file:
<weblogic-rdbms-bean>
<ejb-name>AddressCMPEJB</ejb-name>
...
<delay-database-insert-until>
ejbPostCreate
</delay-database-insert-until>
</weblogic-rdbms-bean>
The valid options in WebLogic Server 8.1 are ejbCreate and ejbPostCreate, with a default of ejbPostCreate. Previous versions also permitted a value of commit, but this option has been eliminated in version 8.1 in favor of the new batching support configured through the enable-batch-operations element. Generally speaking, there is little reason to modify the default behavior of database insert operations by disabling batching and using the delay-database-insert-until element. Delaying inserts until the end of the transaction and using batch operations provide the best performance. It also avoids unneeded database operations when new beans are created and modified in the same transaction. For example, the following code creates a bean and then updates a CMP field in the same transaction:
PersonCMPLocal person = home.create(pk, “Mr.”, “Joe”, “”, “Smith”);
person.setAge(40);
...
If database inserts take place after ejbCreate() or ejbPostCreate() in this example, a subsequent UPDATE statement will be required at the end of the transaction to update the age value in the database before committing the transaction.In addition, performing the INSERT operation after ejbCreate(), rather than after ejbPostCreate(), may be impossible due to the following limitations:
The database is often configured to disallow null values in foreign key columns. In this case, database INSERT statements will not succeed until the CMR field in the bean has been set to a valid bean.
It is not possible to set CMR fields in the ejbCreate() method because the primary key of the new bean may not be available at that point in the process.
Best Practice | Allow the normal database batching logic to control the timing of database inserts unless there is a strong rationale for disabling batching and controlling inserts on a per-bean basis. |
Controlling Lazy Retrieval Using Field Groups
The data contained in a CMP entity bean is generally retrieved from the database using a lazy-retrieval approach that performs the SQL SELECT statement for some or all bean data when a client requests a data element using a get method. Note that the change to defining only abstract get and set methods in bean classes in EJB 2.0 makes this behavior possible and is a key reason for the vast improvement in CMP entity beans in the 2.0 specification.In the simplest lazy-retrieval case, a client obtaining and calling methods on a bean reference would cause the bean data to be loaded during the first get invocation:
PersonCMPLocal person = home.findByPrimaryKey(pk);
String s = person.getFirstName(); // container fetches bean data here
Note that this simple lazy-retrieval example is not typical, given default parameters in your descriptor files. If finders-load-bean is true, the default setting, finders will load bean data during the finder operation and there will be no need to retrieve the data later when get methods are called by the client. Obviously, if a noncached bean reference is obtained in one transaction and used later in a different transaction, the lazy retrieval scenario would be operative during the second transaction to refetch the bean data in response to get methods.WebLogic Server allows tuning of lazy-retrieval functionality through the definition of field groups containing one or more bean attributes and container-managed relationship fields. Field groups serve two important roles in the control of lazy retrieval:
Requests for any data in the field group cause the whole group to be loaded from the database with a single SQL statement.
Field groups may be specified to control the specific data loaded during finder methods when finders-load-bean is set to true.
Using Field Groups to Tune Data Retrieval
The first role allows retrieval of attributes to be tuned for different types of client requests. For example, there may be a small number of fields in the entity bean used frequently in client requests, with the remaining set of fields used only rarely. By placing these two sets of fields in different field groups, the rarely used fields will be loaded from the database only when a field in that set is actually accessed by a client.Field groups are defined in the weblogic-cmp-rdbms-jar.xml descriptor file in the weblogic-rdbms-bean section:
<weblogic-rdbms-bean>
<ejb-name>PersonCMPEJB</ejb-name>
<data-source-name>MasteringDataSource</data-source-name>
<table-map>
<table-name>PERSON</table-name>
<field-map>
<cmp-field>id</cmp-field>
<dbms-column>ID</dbms-column>
</field-map>
...
<verify-columns>Modified</verify-columns>
</table-map>
<field-group>
<group-name>summary</group-name>
<cmp-field>id</cmp-field>
<cmp-field>lastName</cmp-field>
</field-group>
<field-group>
<group-name>everything</group-name>
<cmp-field>id</cmp-field>
<cmp-field>salutation</cmp-field>
<cmp-field>firstName</cmp-field>
<cmp-field>middleName</cmp-field>
<cmp-field>lastName</cmp-field>
</field-group>
</weblogic-rdbms-bean>
In this case we’ve defined a summary group, containing the primary key field and last name of the person, and an everything group, containing all cmp fields. Note that lastName appears in both groups, a perfectly valid configuration. If a client invokes getLastName() on an empty bean instance, WebLogic Server will use the first field group containing the attribute and load all fields in that group.Note that improper use of field groups can actually increase the number of SQL statements required to populate a bean. If a client called getLastName() first and then getFirstName(), for example, the bean data would be retrieved in two separate SQL statements.
Best Practice | Plan your field groups carefully, according to usage patterns and the normal order of attribute requests by clients. Avoid field groups and usage patterns that require two or more SQL statements to fetch bean data both for performance and data-consistency reasons. |
Also recognize one danger inherent in the use of field groups: Because the bean data might be fetched in multiple queries it is possible to have a bean in memory that represents data from different points in time, potentially representing inconsistent state. If another transaction made changes to the bean between the first and second fetches you make, for example, your copy of the bean might contain some data that reflects the update and some that does not. Optimistic concurrency will avoid corruption of the database itself, but your display or business logic might show inconsistent data.
Warning | Using field groups to support partial loading of a bean’s data can cause the bean to have inconsistent data loaded, with each group loaded at different points in time. Use caution when defining field groups to make sure that all interdependent fields are in the same group. |
Using Field Groups to Tune finders-load-bean
Finder methods provide clients with bean references matching the query parameters provided by the client. If a bean is configured with finders-load-bean set to true, the default value, finder methods will fetch all data necessary to populate the bean instance in the finder query itself. We discussed this feature earlier and stressed its importance in eliminating the n + 1 query problem when using CMP entity beans.Field groups can be used to tune these finder queries to fetch a subset of bean attributes rather than all attributes, potentially improving performance dramatically for large beans or large result sets. In the limiting case, you could define an idonly field group, containing only the primary key attribute, and use that group for certain finder queries to defeat the all-or-nothing nature of finders-load-bean. In practice, however, you will probably define a summary field group, as shown in the previous example, and configure certain finder methods to use this field group in the weblogic-query override section in weblogic-cmp-rdbms-jar.xml:
<weblogic-rdbms-bean>
...
<weblogic-query>
<query-method>
<method-name>findByLastName</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</query-method>
<group-name>summary</group-name>
</weblogic-query>
</weblogic-rdbms-bean>
Calls to findByLastName() now cause the container to execute a shortened version of the finder query, which fetches only the fields in the summary field group:
... : Finder produced statement string
SELECT WL0.ID, WL0.LASTNAME FROM PERSON WL0
WHERE ( (WL0.LASTNAME LIKE ? ) )
Using field groups to tune finder queries represents a powerful technique for improving performance and reducing the memory footprint required to cache beans loaded via finders-load-bean. You can employ this technique without introducing the double-query problem described in the previous section by ensuring that the first field group for the bean includes all fields. This will effectively disable the use of field groups for normal data retrieval caused by get methods, but finder methods that specify other field groups can tune their queries as needed.
Best Practice | Use field groups to tune finder queries for large beans or large result sets to avoid the n + 1 query problem without fetching unnecessary data. |
The default field group, appropriately named default, contains all normal data attributes and any CMR fields mapped to foreign keys in the bean’s database table. You may redefine the default field group to contain a subset of the bean attributes to change the default behavior of finders when finders-load-bean is true.
Relationship Caching with CMP Entity Beans
The finders-load-bean feature improves the performance of CMP entity beans by fetching all of the bean data during the finder SQL SELECT statement, thereby avoiding the n + 1 query problem for most simple beans. If the beans being fetched include container-managed relationship fields, however, the related beans will not be fetched during the initial finder query. Subsequent access of the CMR field will require individual SQL statements to fetch the related bean data, producing a variation of the n + 1 query problem if all relationships are traversed.WebLogic Server provides a mechanism, relationship caching, capable of prefetching related entity beans during the initial finder query in much the same way bean data for simple beans was prefetched by finders-load-bean. When relationship caching is enabled for a specific finder method, WebLogic Server will use an outer join in the generated SQL SELECT statement to fetch the primary bean data and related bean data in a single query. As you’ll see in a moment, options exist to control which relationships in the primary bean are included in this outer join and which fields in the related beans are prefetched by the query.An example will help explain this feature. The PersonCMPEJB bean has a one- to-many relationship with the AddressCMPEJB bean, as we’ve described in previous sections. We’ve added a new finder method to the Person bean, findByLastNameWithAddress(), by modifying the ejb-jar.xml file:
<query>
<query-method>
<method-name>findByLastNameWithAddress</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</query-method>
<ejb-ql>
SELECT OBJECT(o) FROM PersonCMPEJB o WHERE o.lastName LIKE ?1
</ejb-ql>
</query>
So far this query is identical to the findByLastName query discussed earlier. We could have simply used that query for this example, but it seems reasonable to indicate the presence of relationship caching by the choice of finder name, so we’ve chosen to call it findByLastNameWithAddress. Recalling that the cmr field relating a Person to its Address beans was called addresses, we now declare a particular relationship-caching element in the Person section of weblogic-cmp-rdbms-jar.xml defining the relationship we want to prefetch in the finder and any specific field group to load in the related bean:
<weblogic-rdbms-jar>
<weblogic-rdbms-bean>
<ejb-name>PersonCMPEJB</ejb-name>
...
<relationship-caching>
<caching-name>withaddress</caching-name>
<caching-element>
<cmr-field>addresses</cmr-field>
<!-- can specify field group within Address -->
<!-- <group-name>summary</group-name> -->
</caching-element>
</relationship-caching>
The descriptor shown here does not specify a particular field group name in the Address bean, so all fields in the related Address beans will be included in the query. Next, we override the definition of findByLastNameWithAddress in this descriptor to specify both the field group to fetch for the Person bean and the name of the relationship-caching element that specifies the related beans and groups to include in the query:
<weblogic-query>
<query-method>
<method-name>findByLastNameWithAddress</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</query-method>
<group-name>everything</group-name> <!-- group in Person -->
<caching-name>withaddress</caching-name>
</weblogic-query>
Note that omitting the field group for the Person bean is perfectly acceptable, thereby causing the query to include the default field group or all fields if no default group is defined. The everything field group is included in this example only to illustrate the ability to combine both types of elements in a single finder definition.Finally, because the prefetching technique uses an outer join, WebLogic Server must generate the finder query using a database-specific syntax for this outer join. A final element in weblogic-cmp-rdbms-jar.xml defines the specific database technology:
<!-- This element required for relationship caching -->
<!-- Valid: DB2 INFORMIX ORACLE SQL_SERVER SYBASE POINTBASE -->
<database-type>ORACLE</database-type>
</weblogic-rdbms-jar>
That’s it! When a client invokes the findByLastNameWithAddress() method, the EJB container will execute a database-specific query similar to the following:
SELECT WL0.ID, WL0.FIRSTNAME, WL0.LASTNAME, WL0.MIDDLENAME,
WL0.SALUTATION, WL1.ID, WL1.CITY, WL1.POSTAL_CODE, WL1.STATE,
WL1.STREET, WL1.PERSON_ID FROM PERSON WL0, ADDRESS WL1
WHERE ( (WL0.LASTNAME LIKE ? ) ) AND WL1.PERSON_ID (+) = WL0.ID
As indicated by this listing, the query joins the PERSON and ADDRESS table using an outer join between the PERSON_ID foreign key in ADDRESS and the ID primary key in PERSON to automatically fetch all addresses for each Person bean matching the driving criteria. Note that Oracle-specific outer-join syntax was employed in this case. After fetching this Cartesian set of rows, the container parses through the results and constructs the individual Person and Address beans, establishes their relationship fields, and caches the beans in their appropriate entity-bean caches.Viola! In one query you’ve populated the cache with an entire graph of Person beans and related Address beans. A few nuances worth mentioning are these:
Make sure finders-load-bean is set to true, the default value, for the parent bean of the relationship. If this parameter is false, the finder methods are not responsible for loading bean data or relationships.
You may place multiple caching-element definitions in a single relationship-caching element, thereby specifying multiple relationships to traverse and prefetch during the finder query. Be careful that the query doesn’t become a monster Cartesian product of all related bean data.
You may also nest caching-element definitions to prefetch children of the related beans. See the online documentation for an example of this. Be careful here as well to avoid huge Cartesian sets.
Because the result set includes duplicate rows caused by the outer join, it is not possible for WebLogic Server to distinguish between a duplicate related bean caused by the join and a true duplicate related bean in the relationship. WebLogic Server assumes the duplicates are artifacts of the join and removes them, meaning that CMR relationships will always appear to contain sets of distinct beans. Don’t use this feature if you allow duplicate related beans in the relationship.
Relationship caching does not work with many-to-many relationships.
Relationship caching may also be used on ejbSelect() methods that return entity beans.
Best Practice | Use relationship caching to create alternate finders that automatically fetch related beans. Consider naming these finders in a manner that indicates the presence of prefetching behavior to avoid their use by clients not requiring the related beans. |
Additional CMP Features
A number of additional CMP-related features are available in WebLogic Server related to primary key generation, mapping to multiple tables, EJB-QL extensions, dynamic queries, and cascading deletes.
Automatic Primary Key Generation
WebLogic Server provides a mechanism to generate a numeric primary key for CMP entity beans automatically. Automatic key generation eliminates the need to set the primary key in the ejbCreate() method and removes the key from the parameter list for create(), ejbCreate(), and ejbPostCreate().For example, the PersonCMPBean class defined in Listing 6.1 included the primary key in the ejbCreate() and ejbPostCreate() methods:
public java.lang.Integer ejbCreate(Integer pId, String pSalutation,
String pFirstName,
String pMiddleName,
String pLastName)
{
LOG.info("ejbCreate()");
setId(pId);
setSalutation(pSalutation);
setFirstName(pFirstName);
setMiddleName(pMiddleName);
setLastName(pLastName);
return null;
}
A version using automatic key generation, however, eliminates this parameter and the setId() invocation:
public java.lang.Integer ejbCreate(String pSalutation,
String pFirstName,
String pMiddleName,
String pLastName)
{
LOG.info(“ejbCreate()”);
setSalutation(pSalutation);
setFirstName(pFirstName);
setMiddleName(pMiddleName);
setLastName(pLastName);
return null;
}
Clients no longer provide the key value when calling create() on the home interface:
PersonCMPLocal person = home.create(“Mr.”, “John”, “”, “Smith”);
Automatic key generation is configured on a per-bean basis by including an automatic-key-generation section in the weblogic-cmp-rdbms-jar.xml descriptor using one of three different techniques: named sequence table, Oracle sequence, or SQL*Server identity column.
The named sequence table technique is the most portable approach. The descriptor elements identify a table in the database to use for automatic key generation:
<automatic-key-generation>
<generator-type>NAMED_SEQUENCE_TABLE</generator-type>
<generator-name>PERSON_SEQ_TBL</generator-name>
<key-cache-size>100</key-cache-size>
</automatic-key-generation>
The specified table must contain a single numeric column, SEQUENCE, and a single row containing the next primary-key value to be assigned for any bean using this table. Note that multiple beans may use the same table.The key-cache-size element defines the number of primary keys to cache in each EJB container. When the server boots, or whenever this in-memory cache of keys is exhausted, the container will access the named sequence table, retrieve the next key value, and increment the database value by the cache size. Caching keys is important for good performance, but it results in wasted key values whenever servers are shut down with unused keys in their cache.
Best Practice | Using a reasonable key-cache-size is very important in reducing the number of database requests when using the named-sequence-table technique. A value of 10 should be considered a minimum, and a value of 100 or more should be used for production systems. |
The Oracle-sequence technique uses an Oracle sequence to provide the generated key values:
<automatic-key-generation>
<generator-type>ORACLE</generator-type>
<generator-name>PERSON_SEQ</generator-name>
<key-cache-size>10</key-cache-size>
</automatic-key-generation>
Again, multiple beans can employ the same sequence if desired, and the key-cache-size will be used to cache key values in each container. If keys are cached in the container, the Oracle sequence must be created with the INCREMENT BY option set to the same value:
CREATE SEQUENCE PERSON_SEQ START WITH 200 INCREMENT BY 10;
Failure to follow this rule will cause the container to use key values from the internal cache that are higher than the next value stored in the sequence. This causes duplicate key exceptions once the container exhausts the cached key values and queries the sequence for the next valid value.
Best Practice | Use a reasonable key-cache-size with the Oracle-sequence technique, and ensure that the cache size in WebLogic equals the INCREMENT BY value used in Oracle. |
Finally, the SQL*Server-identity-column technique utilizes an identity column in the database schema to assign the primary key automatically when the row is inserted in the database:
<automatic-key-generation>
<generator-type>SQLSERVER</generator-type>
</automatic-key-generation>
During the database insert, SQL*Server assigns the next primary key value to the database row and WebLogic Server retrieves this key value and places it in the bean instance. Client code may retrieve the assigned key value immediately after the create() invocation using the appropriate get method:
PersonCMPLocal person = home.create(“Mr.”, “Gregory”, “A.”, “Nyberg”);
Integer pk = person.getId();
This assumes that database inserts have not been delayed until commit, in which case the key will not be available until the transaction commits.
Best Practice | When using automatic key generation with Oracle or SQL*Server, favor the database-specific key-generation technique over the named-sequence table to optimize performance. |
EJB CMP Multiple-Table Mapping Support
Multiple-table mapping allows a single CMP entity bean to map its attributes and relationship foreign keys to multiple database tables. Mapping is configured in the weblogic-cmp-rdbms-jar.xml using appropriate table-map and field-map elements:
<weblogic-rdbms-bean>
<ejb-name>PersonCMPEJB</ejb-name>
<data-source-name>MasteringDataSource</data-source-name>
<table-map>
<table-name>PERSON</table-name>
<field-map>
<cmp-field>id</cmp-field>
<dbms-column>ID</dbms-column>
</field-map>
...
</table-map>
<table-map>
<table-name>PERSONEXTRA</table-name>
<field-map>
<cmp-field>id</cmp-field>
<dbms-column>ID</dbms-column>
</field-map>
<field-map>
<cmp-field>height</cmp-field>
<dbms-column>HEIGHT</dbms-column>
</field-map>
<field-map>
<cmp-field>weight</cmp-field>
<dbms-column>WEIGHT</dbms-column>
</field-map>
</table-map>
</weblogic-rdbms-bean>
In this example, we’ve mapped two additional bean attributes, height and weight, to database columns in a new PERSONEXTRA table. WebLogic Server requires that both tables have the same number and type of primary key columns, although the dbms-column names can differ in each table.When the EJB container retrieves the Person bean, it will execute a single SQL statement joining the mapped tables to retrieve all attributes:
... : Finder produced statement string
SELECT WL0.ID, WL0.FIRSTNAME, WL1.HEIGHT, WL0.LASTNAME, WL0.MIDDLENAME,
WL0.SALUTATION, WL1.WEIGHT FROM PERSON WL0, PERSONEXTRA WL1
WHERE ( (WL0.ID = ?) ) AND WL0.ID = WL1.ID
Updates are stored using a separate SQL statement for each mapped table.
Best Practice | Mapping CMP beans to multiple tables provides a clean, declarative solution. Consider creating a composite entity bean and mapping it to multiple tables as an alternative to one-to-one relationships in your design. |
Additional configuration is required for CMP entity beans containing CMR fields if the beans are mapped to multiple tables and one of the tables contains the foreign key implementing the container-managed relationship. In the simple case shown here, the Person bean participates in a one-to-many relationship with Address beans, but the foreign key is not part of the PERSON table, so no additional configuration is required. If the Address bean was mapped to multiple tables, however, the simple relationship-mapping section in weblogic-cmp-rdbms-jar.xml would have to be modified to indicate the location of the foreign-key column.See the WebLogic Server documentation at http://edocs.bea.com/wls/docs81/ejb /cmp_advancedl for more information on mapping relationships with multiple tables.
WebLogic EJB-QL Enhancements
One of the limitations of EJB 2.0 is the lack of support for important SQL modifiers and capabilities in the standard EJB Query Language specification. WebLogic Server extends the functionality of EJB-QL to include a number of additional features and enhancements, including the following:
DISTINCT and ORDERBY capabilities
Aggregate functions such as COUNT, AVG, SUM, MIN, and MAX
Subqueries, including correlated and uncorrelated subqueries
Queries that return ResultSet objects containing the selected columns (ejbSelect only)
SELECT hints for Oracle RDBMS queries
Although a complete discussion of all of these extensions and features is beyond the scope of this book, a simple example using the findByLastName() finder method will help you understand the configuration steps involved.As discussed earlier, finder and ejbSelect queries must first be declared in the standard ejb-jar.xml descriptor file:
<query>
<query-method>
<method-name>findByLastName</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</query-method>
<ejb-ql>
SELECT OBJECT(o) FROM PersonCMPEJB o WHERE o.lastName LIKE ?1
</ejb-ql>
</query>
To order the results returned by the finder by lastName and firstName, override the definition of the query in weblogic-cmp-rdbms-jar.xml to include the ORDERBY clause in the weblogic-ql contents:
<weblogic-query>
<query-method>
<method-name>findByLastName</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</query-method>
<weblogic-ql>
SELECT OBJECT(o) FROM PersonCMPEJB o WHERE o.lastName LIKE ?1
ORDERBY o.lastName, o.firstName
</weblogic-ql>
</weblogic-query>
The collection returned by the findByLastName() finder method will now be ordered by the desired columns. Many of the WebLogic Server EJB-QL enhancements operate this way: The query is declared in ejb-jar.xml and then redefined in weblogic-cmp-rdbms-jar.xml to include the desired aggregate functions, subqueries, or other features. See the WebLogic Server documentation at http://edocs .bea.com/wls/docs81/ejb/cmp_basicl for additional examples and information.
Best Practice | WebLogic Server EJB-QL enhancements provide extremely useful extensions to the basic EJB-QL query language. Use these extensions to reduce manual result set manipulation performed in the EJB container (sorting items, eliminating duplicates, producing averages, and so on). |
Dynamic Query Support
Queries used for CMP finder and ejbSelect methods are normally declared and defined in the EJB descriptor files. Although this technique provides a standard, declarative mechanism, it suffers from the requirement to redeploy the entire EJB archive file to modify a query, and it lacks flexibility for certain types of queries. For example, it is impossible to create queries with a variable number of parameters in the WHERE clause or a dynamic set of values in an IN operator using the standard EJB-QL syntax.WebLogic Server provides a mechanism for programmatically defining and executing queries in application code to address the limitations of declarative queries. This Dynamic Query Support, added to WebLogic Server in version 7.0, provides a special-purpose Query object used to define and execute dynamic queries in the context of the EJB container and the desired transaction. Using dynamic queries requires a few straightforward changes to your application.First, enable dynamic queries for a given CMP entity bean home interface using the enable-dynamic-queries element in the weblogic-ejb-jar.xml descriptor for the entity bean:
<enable-dynamic-queries>True</enable-dynamic-queries>
Next, obtain a reference to the home interface, and cast it to the QueryHome or QueryLocalHome interface as appropriate:
PersonCMPHomeLocal home = (PersonCMPHomeLocal)ctx.lookup(“...”);
QueryLocalHome qhome = (QueryLocalHome)home;
The QueryHome and QueryLocalHome interfaces define two methods: createQuery() to create a Query helper object and nativeQuery(String) to return the SQL statement generated for a specific dynamic query. Use the createQuery() method to obtain a reference to a Query object:
Query myQuery = qhome.createQuery();
The Query interface defines a number of configuration methods for setting transaction policy, maximum result set size, and whether CMP updates should be flushed before the query is executed. See the WebLogic Server documentation for more information on these methods. The Query interface also defines methods for defining and executing finder and ejbSelect queries:
public java.sql.ResultSet execute(String)
public java.sql.ResultSet execute(String, java.util.Properties)
public java.util.Collection find(String)
public java.util.Collection find(String, java.util.Properties)
Use the simple versions of the execute() or find() method to perform the desired query:
Collection people = myQuery.find(
“SELECT OBJECT(o) FROM PersonCMPEJB o WHERE o.lastName = ‘Smith’”);
This query will be executed using the DataSource defined for the entity bean and a collection of entity-bean references will be returned in the same manner as a normal finder method. The dynamic-query processing will be affected by the value of finders-load-bean and other parameters in the same manner as well. Note that the current version of WebLogic Server does not support parameters or bound variables in the query, so all parameters must be placed in the string, as shown in the example.The alternate versions of execute() and find() allow you to specify configuration parameters during the query execution:
Properties p = new Properties();
p.setProperty(“GROUP_NAME”, “fieldgroup”);
p.setProperty(“INCLUDE_UPDATES”, “true”);
Collection people =
myQuery.find(“SELECT OBJECT(o) FROM PersonCMPEJB o WHERE “ +
“o.lastName = ‘Smith’”, p);
Note that the query string supplied to these methods follows the EJB-QL syntax rather than actual SQL syntax. Attribute names are used rather than column names, relationships are traversed using EJB-QL syntax rather than manual joins, and so on. Use the nativeQuery() method on the QueryHome interface to view the actual SQL generated by the container for a given query string.
Best Practice | Use dynamic queries for finder and ejbSelect queries that are difficult or impossible to implement in standard declarative EJB-QL. |
Cascade Delete Support
Cascade delete refers to the automatic deletion of dependent children objects and data whenever parent objects are deleted from the persistent store. WebLogic Server supports two different versions of the cascading delete functionality: container-driven and database-driven cascade deletion.
In the container-driven version, the EJB container is responsible for deleting child objects when the parent object is removed using the remove() method, either on the bean or the bean’s home object. To configure container-driven cascade delete, include the cascade-delete element in the child object’s ejb-relationship-role element in ejb-jar.xml:
<relationships>
<ejb-relation>
<ejb-relation-name>Person-Addresses</ejb-relation-name>
<ejb-relationship-role>
<ejb-relationship-role-name>
Many-Addresses-Have-One-Person
</ejb-relationship-role-name>
<multiplicity>many</multiplicity>
<cascade-delete/>
<relationship-role-source>
<ejb-name>AddressCMPEJB</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>person</cmr-field-name>
</cmr-field>
</ejb-relationship-role>
...
</ejb-relation>
cascade-delete is configured on the child object, not the parent, and the multiplicity on the parent side of the relationship must be equal to 1.When the parent object is removed, the container will automatically retrieve a collection of all related children objects, issue individual SQL statements to delete these children, and eliminate the children from the entity-bean cache if cache-between-transactions is true. Recursion is possible, meaning that child objects can have their own collections of dependent children configured for cascade delete as well, and the container will traverse all appropriate collections and perform individual SQL statements for all objects requiring deletion. Container-driven cascade-delete operations can be relatively costly to perform if the collections are large or if there are many levels of dependent objects to traverse.An alternative approach, database-driven cascade deletion, makes use of the automatic cascading delete capability of most RDBMS systems by configuring the database to perform the operation behind the scenes. This technique is configured by first including the cascade-delete element in ejb-jar.xml for all dependent children, as in the container-driven version, but then by adding a WebLogic Server-specific db-cascade-delete element in the weblogic-cmp-rdbms-jar.xml file as well:
<weblogic-rdbms-relation>
<relation-name>Person-Addresses</relation-name>
<weblogic-relationship-role>
<relationship-role-name>
Many-Addresses-Have-One-Person
</relationship-role-name>
<relationship-role-map>
<column-map>
<foreign-key-column>PERSON_ID</foreign-key-column>
<key-column>ID</key-column>
</column-map>
</relationship-role-map>
<db-cascade-delete/>
</weblogic-relationship-role>
</weblogic-rdbms-relation>
When the parent object is removed, the container will traverse relationships and invalidate all appropriate dependent children in the entity bean cache, but it will not issue individual SQL statements to delete the underlying database information. The database must be configured with cascading delete enabled in the foreign-key constraints connecting parent and child tables for this technique to operate properly.
Best Practice | Use database-driven cascade delete for most applications to improve performance. Container-driven cascade delete may be suitable for applications with a small number of child objects that require manual deletion. |
Checking for Bean Existence
When using clusters, or employing concurrency strategies other than exclusive concurrency, it is possible to call methods on an entity bean that has been removed from the database by a different transaction. Normally this condition will be caught during the load operation in response to a get method invocation, or at commit time when the container attempts to update the bean’s data in the database. WebLogic Server also provides a mechanism to check the existence of a CMP bean at the conclusion of every business method for added safety. Note that once a bean’s data has been loaded from the database, no additional checks are made during that transaction. Enable this per-method check using the check-exists-on-method element in the weblogic-cmp-rdbms-jar.xml file:
<weblogic-rdbms-bean>
<ejb-name>PersonCMPEJB</ejb-name>
...
<check-exists-on-method>true</check-exists-on-method>
</weblogic-rdbms-bean>
In previous versions of WebLogic Server, the default value for check-exists-on-method was false, meaning that no additional method-level checks for existence took place by default. This setting now defaults to true in WebLogic Server 8.1, again due to J2EE 1.3 licensing requirements. Consider setting check-exists-on-method to false in all beans to avoid unnecessary overhead in business methods.
Warning | J2EE licensing restrictions have caused the WebLogic Server 8.1 default setting for check-exists-on-method to change from false to true. If your application doesn’t require existence checks after every business method, setting the value back to false will improve performance. |