JBoss.org Community Documentation
This tutorial shows you how to cache your entities using EJB 3.0 for JBoss
To avoid roundtrips to the database, you can use a cache for your entities. JBoss EJB3 uses Hibernate as the JPA implementation which has support for a second-level cache. The Hibernate setup used for our JBoss EJB 3.0 implementation uses JBossCache as its underlying cache implementation. With caching enabled:
JBossCache allows you to specify timeouts to cached entities. Entities not accessed within a certain amount of time are dropped from the cache in order to save memory.
Furthermore, JBossCache supports clustering. If running within a cluster, and the cache is updated, changes to the entries in one node will be replicated to the corresponding entries in the other nodes in the cluster.
Take a look at META-INF/persistence.xml
which sets up the caching for this deployment:
<property name="hibernate.cache.use_second_level_cache" value="true"/> <property name="hibernate.cache.use_query_cache" value="true"/> <property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.jbc2.JndiMultiplexedJBossCacheRegionFactory"/> <property name="hibernate.cache.region.jbc2.cachefactory" value="java:CacheManager"/> <property name="hibernate.cache.region.jbc2.cfg.entity" value="mvcc-entity"/> <property name="hibernate.cache.region.jbc2.cfg.query" value="local-query"/> <property name="hibernate.show_sql" value="true"/>
hibernate.show_sql
property in the persistence.xml to true.
When this property is set, Hibernate will print to STDOUT the sql queries that are fired to the database, when
the entity is being operated upon. This will, later on, help us in verifying whether the entity is being picked up
from cache or is being loaded from database through a SQL query.
You define your entities org.jboss.tutorial.cachedentity.bean.Customer
and org.jboss.tutorial.cachedentity.bean.Contact
the normal way.
The default behaviour is to not cache anything, even with the settings shown above, in the persistence.xml.
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. We also annotate the classes with the @Cache
annotation to
indicate that the entities should be cached.
@Entity @Cache (usage=CacheConcurrencyStrategy.TRANSACTIONAL) public class Customer implements java.io.Serializable { ...
@Entity @Cache (usage=CacheConcurrencyStrategy.TRANSACTIONAL) public class Contact implements Serializable { ...
This defines that the Customer and the Contact entities need to be cached. Any attempt to look up
Customer
or Contact
by their primary key, will first attempt
to read the entry from the cache. If it cannot be found it will try and load it up from the database.
You can also cache relationship collections. Any attempt to access the contacts
collection of
Customer
will attempt to load the data from the cache before hitting the database:
@Entity @Cache (usage=CacheConcurrencyStrategy.TRANSACTIONAL) public class Customer implements java.io.Serializable { ... @Cache (usage=CacheConcurrencyStrategy.TRANSACTIONAL) @OneToMany(mappedBy="customer", fetch=FetchType.EAGER, cascade=CascadeType.ALL) public Set<Contact> getContacts() { return contacts; } ... }
Open org.jboss.tutorial.cachedentity.client.CachedEntityRun
. It takes two arguments, they are the
server:jndiport of the two nodes to use. If you look at the 'run' target of META-INF/build.xml
you will see that they both default to localhost:1099, so in this case node1 and node2 will be the same,
i.e. no clustering takes place.
To build and run the example, make sure you have installed JBoss 5.x. See the Section 1.1, “JBoss Application Server 5.x” for details.
From the command prompt, move to the "cachedentity" folder under the Section 1.3, “Set the EJB3_TUTORIAL_HOME”
Make sure the "all" server configuration of JBossAS-5.x is running
$ ant $ ant run run: [java] Saving customer to node1 = localhost:1099 [java] Looking for customer on node2 = localhost:1099 (should be available in cache) [java] Found customer on node2 (cache). Customer details follow: [java] Customer: id=1; name=JBoss [java] Contact: id=2; name=Kabir [java] Contact: id=1; name=Bill
$ mvn clean install -PRunSingleTutorial
On the server you will notice these logs:
02:14:04,528 INFO [STDOUT] Hibernate: insert into Customer (id, name) values (null, ?) 02:14:04,529 INFO [STDOUT] Hibernate: call identity() 02:14:04,542 INFO [STDOUT] Hibernate: insert into Contact (id, CUST_ID, name, tlf) values (null, ?, ?, ?) 02:14:04,543 INFO [STDOUT] Hibernate: call identity() 02:14:04,544 INFO [STDOUT] Hibernate: insert into Contact (id, CUST_ID, name, tlf) values (null, ?, ?, ?) 02:14:04,545 INFO [STDOUT] Hibernate: call identity() 02:14:04,545 INFO [EntityTestBean] Created customer named JBoss with 2 contacts 02:14:04,634 INFO [EntityTestBean] Find customer with id = 1 02:14:04,645 INFO [STDOUT] Hibernate: select customer0_.id as id0_1_, customer0_.name as name0_1_, contacts1_.CUST_ID as CUST4_3_, contacts1_.id as id3_, contacts1_.id as id1_0_, contacts1_.CUST_ID as CUST4_1_0_, contacts1_.name as name1_0_, contacts1_.tlf as tlf1_0_ from Customer customer0_ left outer join Contact contacts1_ on customer0_.id=contacts1_.CUST_ID where customer0_.id=? 02:14:04,682 INFO [EntityTestBean] Customer with id = 1 found 02:14:04,756 INFO [EntityTestBean] Find customer with id = 1 02:14:04,801 INFO [EntityTestBean] Customer with id = 1 found
As you can see, the first time the customer with id = 1 was being requested through the entity manager, a database query was fired to fetch it, since it was not available in cache. The Customer object was then loaded from the database and stored in the cache. As can be seen in the next request to fetch the customer with the same id. This request to the entity manager, pulls up the entity from the cache instead of firing a database query.