In a JBoss Enterprise Application Platform cluster, entity bean instance caches need to be kept in sync across all nodes. If an entity bean provides remote services, the service methods need to be load balanced as well.
In EJB 3.0, entity beans primarily serve as a persistence data model. They do not provide remote services. Hence, the entity bean clustering service in EJB 3.0 primarily deals with distributed caching and replication, instead of load balancing.
To avoid round trips to the database, you can use a cache for your entities. JBoss EJB 3.0 entity beans are implemented by Hibernate, which has support for a second-level cache. The second-level cache provides the following functionalities:
If you persist a cache-enabled entity bean instance to the database via the entity manager, the entity will be inserted into the cache.
If you update an entity bean instance, and save the changes to the database via the entity manager, the entity will be updated in the cache.
If you remove an entity bean instance from the database via the entity manager, the entity will be removed from the cache.
If loading a cached entity from the database via the entity manager, and that entity does not exist in the database, it will be inserted into the cache.
As well as a region for caching entities, the second-level cache also contains regions for caching collections, queries, and timestamps. The Hibernate setup used for the JBoss EJB 3.0 implementation uses JBoss Cache as its underlying second-level cache implementation.
Configuration of a the second-level cache is done via your EJB3 deployment's persistence.xml, like so:
Copy to ClipboardCopied!Toggle word wrapToggle overflow
hibernate.cache.use_second_level_cache
Enables second-level caching of entities and collections.
hibernate.cache.use_query_cache
Enables second-level caching of queries.
hibernate.cache.region.factory_class
Defines the RegionFactory implementation that dictates region-specific caching behavior. Hibernate ships with 2 types of JBoss Cache-based second-level caches: shared and multiplexed.
A shared region factory uses the same Cache for all cache regions - much like the legacy CacheProvider implementation in older Hibernate versions.
Hibernate ships with 2 shared region factory implementations:
Uses a single JBoss Cache configuration, from an existing CacheManager bound to JNDI, for all cache regions.
Expand
Table 24.2. Additional properties for JndiSharedJBossCacheRegionFactory
Property
Default
Description
hibernate.cache.region.jbc2.cfg.shared
Required
JNDI name to which the shared Cache instance is bound.
A multiplexed region factory uses separate Cache instances, using optimized configurations for each cache region.
Expand
Table 24.3. Common properties for multiplexed region factory implementations
Property
Default
Description
hibernate.cache.region.jbc2.cfg.entity
optimistic-entity
The JBoss Cache configuration used for the entity cache region. Alternative configurations: mvcc-entity, pessimistic-entity, mvcc-entity-repeatable, optimistic-entity-repeatable, pessimistic-entity-repeatable
hibernate.cache.region.jbc2.cfg.collection
optimistic-entity
The JBoss Cache configuration used for the collection cache region. The collection cache region typically uses the same configuration as the entity cache region.
hibernate.cache.region.jbc2.cfg.query
local-query
The JBoss Cache configuration used for the query cache region. By default, cached query results are not replicated. Alternative configurations: replicated-query
hibernate.cache.region.jbc2.cfg.ts
timestamps-cache
The JBoss Cache configuration used for the timestamp cache region. If query caching is used, the corresponding timestamp cache must be replicating, even if the query cache is non-replicating. The timestamp cache region must never share the same cache as the query cache.
Hibernate ships with 2 shared region factory implementations:
Table 24.5. Additional properties for JndiMultiplexedJBossCacheRegionFactory
Property
Default
Description
hibernate.cache.region.jbc2.cachefactory
Required
JNDI name to which the CacheManager instance is bound.
Now, we have JBoss Cache configured to support distributed caching of EJB 3.0 entity beans. We still have to configure individual entity beans to use the cache service.
Next we need to configure which entities to cache. The default is to not cache anything, even with the settings shown above. We use the @org.hibernate.annotations.Cache annotation to tag entity beans that needs to be cached.
@Entity
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
public class Account implements Serializable
{
// ... ...
}
@Entity
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
public class Account implements Serializable
{
// ... ...
}
Copy to ClipboardCopied!Toggle word wrapToggle overflow
A very simplified rule of thumb is that you will typically want to do caching for objects that rarely change, and which are frequently read. You can fine tune the cache for each entity bean in the appropriate JBoss Cache configuration file, e.g. jboss-cache-manager-jboss-beans.xml. For instance, you can specify the size of the cache. If there are too many objects in the cache, the cache can evict the oldest or least used objects, depending on configuration, to make room for new objects. Assuming the region_prefix specified in persistence.xml was myprefix, the default name of the cache region for the com.mycompany.entities.Account entity bean would be /myprefix/com/mycompany/entities/Account.
<bean name="..." class="org.jboss.cache.config.Configuration">
... ...
<property name="evictionConfig">
<bean class="org.jboss.cache.config.EvictionConfig">
<property name="wakeupInterval">5000</property>
<!-- Overall default -->
<property name="defaultEvictionRegionConfig">
<bean class="org.jboss.cache.config.EvictionRegionConfig">
<property name="regionName">/</property>
<property name="evictionAlgorithmConfig">
<bean class="org.jboss.cache.eviction.LRUAlgorithmConfig">
<!-- Evict LRU node once we have more than this number of nodes -->
<property name="maxNodes">10000</property>
<!-- And, evict any node that has not been accessed in this many seconds -->
<property name="timeToLiveSeconds">1000</property>
<!-- Do not evict a node that's been accessed within this many seconds.
Set this to a value greater than your max expected transaction length. -->
<property name="minTimeToLiveSeconds">120</property>
</bean>
</property>
</bean>
</property>
<property name="evictionRegionConfigs">
<list>
<bean class="org.jboss.cache.config.EvictionRegionConfig">
<property name="regionName">/myprefix/com/mycompany/entities/Account</property>
<property name="evictionAlgorithmConfig">
<bean class="org.jboss.cache.eviction.LRUAlgorithmConfig">
<property name="maxNodes">10000</property>
<property name="timeToLiveSeconds">5000</property>
<property name="minTimeToLiveSeconds">120</property>
</bean>
</property>
</bean>
... ...
</list>
</property>
</bean>
</property>
</bean>
<bean name="..." class="org.jboss.cache.config.Configuration">
... ...
<property name="evictionConfig">
<bean class="org.jboss.cache.config.EvictionConfig">
<property name="wakeupInterval">5000</property>
<!-- Overall default -->
<property name="defaultEvictionRegionConfig">
<bean class="org.jboss.cache.config.EvictionRegionConfig">
<property name="regionName">/</property>
<property name="evictionAlgorithmConfig">
<bean class="org.jboss.cache.eviction.LRUAlgorithmConfig">
<!-- Evict LRU node once we have more than this number of nodes -->
<property name="maxNodes">10000</property>
<!-- And, evict any node that has not been accessed in this many seconds -->
<property name="timeToLiveSeconds">1000</property>
<!-- Do not evict a node that's been accessed within this many seconds.
Set this to a value greater than your max expected transaction length. -->
<property name="minTimeToLiveSeconds">120</property>
</bean>
</property>
</bean>
</property>
<property name="evictionRegionConfigs">
<list>
<bean class="org.jboss.cache.config.EvictionRegionConfig">
<property name="regionName">/myprefix/com/mycompany/entities/Account</property>
<property name="evictionAlgorithmConfig">
<bean class="org.jboss.cache.eviction.LRUAlgorithmConfig">
<property name="maxNodes">10000</property>
<property name="timeToLiveSeconds">5000</property>
<property name="minTimeToLiveSeconds">120</property>
</bean>
</property>
</bean>
... ...
</list>
</property>
</bean>
</property>
</bean>
Copy to ClipboardCopied!Toggle word wrapToggle overflow
If you do not specify a cache region for an entity bean class, all instances of this class will be cached using the defaultEvictionRegionConfig as defined above. The @Cache annotation exposes an optional attribute "region" that lets you specify the cache region where an entity is to be stored, rather than having it be automatically created from the fully-qualified class name of the entity class.
@Entity
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL, region = "Account")
public class Account implements Serializable
{
// ... ...
}
@Entity
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL, region = "Account")
public class Account implements Serializable
{
// ... ...
}
Copy to ClipboardCopied!Toggle word wrapToggle overflow
The EJB3 Query API also provides means for you to save the results (i.e., collections of primary keys of entity beans, or collections of scalar values) of specified queries in the second-level cache. Here we show a simple example of annotating a bean with a named query, also providing the Hibernate-specific hints that tells Hibernate to cache the query.
First, in persistence.xml you need to tell Hibernate to enable query caching:
Copy to ClipboardCopied!Toggle word wrapToggle overflow
Next, you create a named query associated with an entity, and tell Hibernate you want to cache the results of that query:
@Entity
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL, region = "Account")
@NamedQueries(
{
@NamedQuery(
name = "account.bybranch",
query = "select acct from Account as acct where acct.branch = ?1",
hints = { @QueryHint(name = "org.hibernate.cacheable", value = "true") }
)
})
public class Account implements Serializable
{
// ... ...
}
@Entity
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL, region = "Account")
@NamedQueries(
{
@NamedQuery(
name = "account.bybranch",
query = "select acct from Account as acct where acct.branch = ?1",
hints = { @QueryHint(name = "org.hibernate.cacheable", value = "true") }
)
})
public class Account implements Serializable
{
// ... ...
}
Copy to ClipboardCopied!Toggle word wrapToggle overflow
The @NamedQueries, @NamedQuery and @QueryHint annotations are all in the javax.persistence package. See the Hibernate and EJB3 documentation for more on how to use EJB3 queries and on how to instruct EJB3 to cache queries.
By default, Hibernate stores query results in JBoss Cache in a region named <region_prefix>/org/hibernate/cache/StandardQueryCache. Based on this, you can set up separate eviction handling for your query results. So, if the region prefix were set to myprefix in persistence.xml, you could, for example, create this sort of eviction handling:
Copy to ClipboardCopied!Toggle word wrapToggle overflow
The @NamedQuery.hints attribute shown above takes an array of vendor-specific @QueryHints as a value. Hibernate accepts the "org.hibernate.cacheRegion" query hint, where the value is the name of a cache region to use instead of the default /org/hibernate/cache/StandardQueryCache. For example:
@Entity
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL, region = "Account")
@NamedQueries(
{
@NamedQuery(
name = "account.bybranch",
query = "select acct from Account as acct where acct.branch = ?1",
hints =
{
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")
}
)
})
public class Account implements Serializable
{
// ... ...
}
@Entity
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL, region = "Account")
@NamedQueries(
{
@NamedQuery(
name = "account.bybranch",
query = "select acct from Account as acct where acct.branch = ?1",
hints =
{
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Queries")
}
)
})
public class Account implements Serializable
{
// ... ...
}
Copy to ClipboardCopied!Toggle word wrapToggle overflow