WebLogic platform provides a number of features to reduce data access latency for CMP entity beans. This topic discusses the following techniques:
Note. This topic is meant to specifically point out a number of Weblogic-specific features that speed up data access. Design patterns, the use of local interfaces, and other J2EE design and architecture issues that affect performance are not addressed here. For more information on those topics, please consult your favorite J2EE documentation.
A field-group represents a subset of container-managed persistence (cmp) and container-managed relation (cmr) fields of a single entity bean. You can associate a group with a query method (or relationship; see below), so that when a bean is loaded as the result of executing a query, only the fields mentioned in the group are loaded from the database when the query method returns, a concept known as eager loading. An entity bean's field that is not part of that group is not loaded at that point, but will be loaded if you subsequently access this field (through a get method), a concept known as lazy loading. Data access will be improved if you take advantage of eager loading while avoiding lazy loading, that is, by making sure that all and only the fields that you need are returned by the query method.
Note. Eager loading is way of specifying what data are loaded and cached for the duration of a transaction. If a finder method is used in one transaction, and in a subsequent transaction the bean returned by that finder method is accessed (using a get method), the accessed field is loaded anew (as the data might have changed in the meantime). If caching between transactions is enabled, then eager loading will be used the first time to cache the data, but subsequently the cached data is used instead. For more information about caching, see the various caching sections below.
To enable eager loading of field groups, you must do the following:
In the following code snippet two field groups are defined, as well as a findAll method that uses eager loading of one of these groups. Particularly relevant lines of code are shown in bold:
/** * @ejbgen:entity prim-key-class="java.lang.Integer" ... * * ... * * @ejbgen:finder group-name="queryGroup" * ejb-ql="SELECT OBJECT(o) from CachedOne as o" * generate-on="Local" * signature="Collection findAll()" */ abstract public class CachedOne extends GenericEntityBean implements EntityBean { /** * @ejbgen:cmp-field group-names="queryGroup" primkey-field="true" column="Index" * @ejbgen:local-method */ public abstract Integer getIndex(); /** * @ejbgen:local-method */ public abstract void setIndex(Integer arg); /** * @ejbgen:cmp-field group-names="queryGroup" column="Name" * @ejbgen:local-method */ public abstract String getFieldOne(); /** * @ejbgen:local-method */ public abstract void setFieldOne(String arg); /** * @ejbgen:cmp-field group-names="nonQueryGroup" column="FieldTwo" * @ejbgen:local-method */ public abstract String getFieldTwo(); /** * @ejbgen:local-method */ public abstract void setFieldTwo(String arg); ...
If a query method returns (one or) a set of entity bean object(s), the cmp and cmr fields of each object are loaded at this point (subject perhaps to eager loading of field groups, as discussed above). If subsequently a cmr field (relationship field) accessor method is used to, for instance, get a set of related entity beans referenced via the field, those beans are loaded at this point, requiring a separate database read. To reduce the number of database queries and enhance performance, you can enable eager loading of relationships, such that in the results returned by a query method, related entity beans have also been loaded.
Eager loading of relationships is possible for all entity relationships, with the exception of many-to-many relationships. For more information about relationships, see Entity Relationships. It is also possible to nest eager loading of relationships, that is, for the beans that are loaded as part of a relationship, you can in turn load their entity relationships (see below).
Note. Eager loading is way of specifying what data are loaded and cached for the duration of a transaction. If a finder method is used in one transaction, and in a subsequent transaction the bean returned by that finder method is accessed (using a get method), the accessed field is loaded anew (as the data might have changed in the meantime). If caching between transactions is enabled, then eager loading will be used the first time to cache the data, but subsequently the cached data is used instead. For more information about caching, see the various caching sections below.
To enable eager loading of relationships, you must do the following:
In the following example a findByName method takes a String argument and returns the band(s) matching that name. For the returned band(s), its recordings, represented by RecordingBean instances and referenced through the cmr field recordings, are eagerly loaded at the same time. (The BandBean and RecordingBean are engaged in a one-to-many bidirectional entity relationship.) The code snippet shows the definition of the finder method, the entity relationship, and the eager relationship loading specification:
* @ejbgen:finder caching-name="LoadRecordings" * ejb-ql="SELECT DISTINCT OBJECT(a) FROM BandEJB AS a WHERE a.name = ?1" * signature="java.util.Collection findByName(java.lang.String n0)" * @ejbgen:relation role-name="BandEJB-has-Recordings" cmr-field="recordings" * target-ejb="Recording" * multiplicity="One" name="Recording-BandEJB"
* @ejbgen:relationship-caching-element caching-name="LoadRecordings" cmr-field="recordings" */ abstract public class BandBean extends GenericEntityBean implements EntityBean { ...
For entity bean instances that are loaded as the result of eager loading of relationships, it is possible to in turn load their related beans. You can nest eager loading of relationships by using the id attribute on the top-level ejbgen:relationship-caching-element tag, and using the parent-id attribute on the nested tag. There is no limit to the number of levels you can nest, although in practice using multiple levels is likely going to negatively affect performance, due to the large number of beans that are being loaded.
Building on the example used above, recording beans also have a many-to-one bidirectional relationship with producer beans. That is, for each recording there is one producer (and for each producer there are many recordings). The following sample shows how to use the id and parent-id attributes to also load the producer for each recording that is returned by the BandBean's findByName query method. The BandBean definition is shown first:
* @ejbgen:finder caching-name="LoadRecordings" * ejb-ql="SELECT DISTINCT OBJECT(a) FROM BandEJB AS a WHERE a.name = ?1" * signature="java.util.Collection findByName(java.lang.String n0)" * @ejbgen:relation role-name="BandEJB-has-Recordings" cmr-field="recordings" * target-ejb="Recording" * multiplicity="One" name="Recording-BandEJB"
* @ejbgen:relationship-caching-element id="LR_ID" caching-name="LoadRecordings" * cmr-field="recordings" */ abstract public class BandBean extends GenericEntityBean implements EntityBean { ...
The RecordingBean defines the entity relationship with the ProducerBean, and an eager loading definition that is nested inside the BandBean's LoadRecordings eager loading definition, using the parent-id attribute:
* @ejbgen:relation role-name="Recordings-have-Producer" fk-column="newColumn " * cmr-field="producer" target-ejb="Producer" * multiplicity="Many" name="Recording-Producer" * @ejbgen:relationship-caching-element caching-name="LoadProducer" * cmr-field="producer" parent-id="LR_ID" */ abstract public class RecordingBean extends GenericEntityBean implements EntityBean { ...
In addition to eager loading of group-names and relationships, there is a third way to increase performance of query methods. In line with the EJB 2.0 specification, updates made by a transaction are by default reflected in the results of query methods that are subsequently invoked during the same transaction. This behavior is set by the include-updates attribute on the query method, that is, its ejbgen:finder or ejbgen:select tag.
If the include-updates attribute is set to true (or left unspecified), the EJB container saves all changes made at this point in the transaction to the database before executing the query. This guarantees that the changes show up in the query results but it also slows down performance. When this attribute is set to false, the updates of the current transaction are not reflected in the query. This results in better performance, although it does not necessarily guarantee the most current data to be returned by a query. However, you can safely set this attribute to false if within a transaction you do not re-query the data you just modified in that transaction, which is a common scenario.
Note. If the include-updates attribute is set to true (or left unspecified), changes are always stored before executing a query, thereby overriding the setting to wait until the end of a transaction before data is stored. However, if the transaction subsequently fails, any changes made during that transaction are still rolled back. For more information, see EJBs and Transactions.
When you need to frequently read data from a table but you do not, or only very occasionally, have to write to this table, you can improve performance by creating a read-only bean. This read-only entity bean is used for all read operations, while the occasional write operations to the table is performed by another EJB. Make sure that the following attributes are set in the Property Editor for a read-only bean:
All these attributes are part of the @ejbgen:entity Annotation. To learn more about caching between transactions, read the section below.
When the data that a read-only entity bean accesses is changed by the companion EJB with write operations, you might want to invalidate the read-only entity bean, thus forcing a refresh of the cache and ensuring that the read-only bean always returns the most current data.
To invalidate a read-only bean, you must set the 'write' entity bean's attribute invalidation-target, located in the Property Editor under Deployment, to the ejb-name of the read-only entity bean. This attribute is part of the @ejbgen:entity Annotation.
The Optimistic concurrency strategy for an entity bean entails that, unlike the Database or Exclusive strategy, no locks in the EJB container or database are placed during a transaction (also see the Bean Caching and Concurrency Options Summarized section below. To learn more about transactions, see EJBs and Transactions). The EJB container verifies that none of the data used by the transaction has been changed (by another source during another transaction) before committing the transaction. If the verified data has changed, the EJB container rolls back the transaction. The EJB container can verify that the used data has not changed in various ways, as specified by the verify-columns and verify-rows attributes on the ejbgen:entity tag. The options for the verify-columns attribute are as follows:
Note. The EJB container does not check Oracle Blob/Clob fields for optimistic concurrency when using the Read or Modified option. Use version number or timestamp checking instead.
The options for the verify-rows attribute are as follows:
Read. The EJB container checks any row that is read by the transaction. This most stringent option typically increases the amount of optimistic checking the EJB container must perform and lowers performance. However, it ensures that none of the data used in this transaction has been changed in the meantime.
Note. If verify-rows is set to Read, verify-columns cannot be set to Modified.
Modified. The EJB container checks only the rows that have been updated by the current transaction. This option typically increases performance, but it allows that rows that were not changed in the current transaction were changed in the meantime by another transaction. This interleaving of updates may or may not be desirable in all circumstances
Apart from the one noted exception, verify-rows and verify-columns work in conjunction. For instance, setting verify-columns to Read and verify-rows to Modified ensures that all columns of only modified rows are checked; Setting verify-columns to Version and verify-rows to Read means that all rows are checked via the version number; and so forth.
Note. If the EJB is mapped to multiple tables, optimistic checking is only performed on the tables that are updated during the transaction. In a clustered server environment, notifications for updates of optimistic data are broadcast to other cluster members to help avoid optimistic conflicts.
When you specify the Version option on verify-columns, the EJB container checks via a version number that a record has not changed in the meantime by another transaction. The EJB container manages this version number, that is, it updates the version number of a record when its data has changed. However, you must define a version column in the database table to hold that version number. The column can be of type Integer or Long. In addition, you must indicate that this column is to be used for the version number by the EJB container, by specifying the column name in the optimistic-column attribute of the ejbgen:entity tag. Mapping this column to a cmp field in the entity bean is optional.
When you specify the Timestamp option on verify-columns, the EJB container checks via a time stamp that a record has not changed in the meantime by another transaction. The EJB container manages this time stamp, that is, it updates the time stamp of a record when its data has changed. However, you must define a column in the database table to hold that timestamp, which is an instance of java.sql.Timestamp. (The exact type of this column is database-dependent). In addition, you must indicate that this column is to be used for the timestamp by the EJB container, by specifying the column name in the optimistic-column attribute of the ejbgen:entity tag. Mapping this column to a cmp field in the entity bean is optional.
Note. When the EJB container updates a timestamp, it enforces at least a one-second difference between the old and new timestamp value.
The EJB 2.0 architecture specifies that for entity beans, the CMP entity bean's are synchronized with the database before business methods are invoked. In WebLogic, this synchronization is specifically performed at the start of each transaction (directly after which the ejbLoad callback is invoked to notify the beans of the completed synchronization). When multiple sources may update the same data, synchronizing at this point ensures that the bean has the most current version of the data.
When only the EJB instance is guaranteed to ever access the data it represents, synchronizing repeatedly is unnecessary. Because no other clients or systems update the EJB's underlying data, the cached version of the EJB data is always up to date. To avoid these unnecessary synchronizations, you can set the ejbgen:entity cache-between-transactions attribute to true. When you set this attribute, synchronizations are only performed when:
Note. If you incorrectly set cache-between-transactions set to true, that is, if the data that the EJB maps can be changed by multiple sources, data integrity violations may result.
To set the maximum number of beans in the cache, you can set the max-beans-in-cache attribute. These and the other caching attributes are part of the @ejbgen:entity Annotation.
The concurrency strategy for an entity bean is set using the ejbgen:entity concurrency-strategy attribute, which can be done through the Property Editor. Similarly, in the Property Editor you can enable caching between transactions using the ejbgen:entity cache-between-transactions attribute, but enabling caching is not possible for all concurrency strategies. The following list gives an overview of the various valid combinations:
Using this concurrency strategy, the EJB container loads the latest data from the database at the beginning of the transaction. When the data is to be committed (just prior to calling the ejbStore callback), this request to store data is passed along to the database. The database handles all required lock management and provides deadlock detection.
Deferring locks to the underlying database enables concurrent access to entity EJB data. However, using database locking requires more detailed knowledge of the underlying datastore's lock policies, which might reduce portability among different database systems. Also, the container never caches the intermediate state of the EJB instance between transactions. That is, setting the ejbgen:entity cache-between-transactions attribute to True is not a valid option for this concurrency strategy.
Using this concurrency strategy, you can either enable or disable caching between transactions. However, caching is not allowed for this concurrency strategy in a clustered server environment.
Note. Concurrency settings may affect transaction isolation levels. For more information, see EJBs and Transactions.
Application-level caching allows multiple entity beans that are part of the same application to share a single runtime cache. Application-level caching offers the following advantages:
Application-level caching is not the best choice, however, for applications that experience high throughput. Because one thread of control exists per cache at a time, high throughput can create a bottleneck situation as tasks compete for control of the thread.
To create the application-level cache, you must manually edit weblogic-application.xml, located in the META-INF directory of your WebLogic application. Locate the ejb section of the file, and add the entity-cache within this section, as shown in the following example:
<weblogic-application> <ejb> <entity-cache> <entity-cache-name>large_account</entity-cache-name> <max-cache-size> <megabytes>1</megabytes> </max-cache-size> </entity-cache> </ejb> </weblogic_application>
The most common xml elements for entity caches are:
For more information, see Enterprise Application Deployment Descriptor Elements.
To use the application-level entity cache for an entity bean, you must add an @ejbgen:entity-cache-ref tag to its definition, and use the name attribute to refer to the application-level entity cache name. If you have specified the application-level cache maximum using the max-cache-size, instead of max-beans-in-cache, you should also use the tag's estimated-bean-size attribute to specify an estimated bean size. These estimates are primarily used to estimate the relative amounts of different entity bean instances that can be held in the application-level cache.
If you don't reference the application-level cache, a separate cache is used for the entity bean.
When you invoke entity beans through their remote interfaces, WebLogic must pass method arguments by value (due to classloading requirements). When EJBs are invoked through their local interface, WebLogic can pass method arguments by reference, improving the performance of method invocation (because parameters are not copied).
In WebLogic passing arguments by value is done by default. To enable passing arguments by reference, you must set the ejbgen:entity enable-call-by-reference to True.
When an entity bean is part of a transaction, the EJB container can take advantage of this by executing the SQL commands on the database table at the end of the transaction when the transaction commits instead of at various points during the transaction. (To learn more about transactions, see EJBs and Transactions). In addition, the EJB container can execute related changes in a single SQL instead of executing separate SQL commands for each individual change. The ejbgen:entity order-database-operations and ejbgen:entity enable-batch-operations attributes are used to enable this. By default, both attributes are set to True.
When database operations are ordered, insert, update and delete commands are sorted to ensure data consistency. These sorted database operations are executed at commit time instead of during the transaction. For instance, if during a transaction a customer receives a new credit card and the old credit card is destroyed, a reference is first made from the customer record to the new credit card record before the old credit card record is deleted. Reversing the order of the SQL commands would violate referential integrity.
If multiple instances of an entity bean are changed in the transaction, enabling batch operations increases performance by allowing the EJB container to update/insert/delete multiple records in the database at the same time instead of issuing separate SQL commands for each entity bean instance change.
Note. Batch operations are disabled if the transaction involves Automatic Primary Key Generation with SQL Server or an update to an OracleBlob or OracleClob CMP field (see the @ejbgen:cmp-field Annotation). The total number of instances inserted in a batch operation cannot exceed the entity bean's max-beans-in-cache attribute.