Typically , I use Hibernate's @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) to cache an @Entity class , and it works well.
In JPA2 , there's another @Cacheable annotation that seems to be the same functionality with Hibernate's @Cache. To make my entity class independent of hibernate's package , I want to give it a try. But I cannot make it work. Each time a simple id query still hits the DB.
Can anybody tell me where goes wrong ? Thanks.
Entity class :
@Entity //@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) @Cacheable(true) public class User implements Serializable { // properties }
Test class :
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations={"classpath:app.xml"}) @TransactionConfiguration(transactionManager="transactionManager") public class UserCacheTest { @Inject protected UserDao userDao; @Transactional @Test public void testGet1() { assertNotNull(userDao.get(2L)); } @Transactional @Test public void testGet2() { assertNotNull(userDao.get(2L)); } @Transactional @Test public void testGet3() { assertNotNull(userDao.get(2L)); } }
The test result shows each "get" hits DB layer (with hibernate.show_sql=true).
Persistence.xml :
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.format_sql" value="true" /> <property name="hibernate.use_outer_join" value="true"/> <property name="hibernate.cache.provider_class" value="org.hibernate.cache.SingletonEhCacheProvider"/> <property name="hibernate.cache.use_second_level_cache" value="true"/> <property name="hibernate.cache.use_query_cache" value="true"/>
JPA code :
@Override public T get(Serializable id) { return em.find(clazz, id); }
First Level Cache: Hibernate first level cache is associated with the Session object. Hibernate first level cache is enabled by default and there is no way to disable it. However hibernate provides methods through which we can delete selected objects from the cache or clear the cache completely.
We can use session clear() method to clear the cache i.e delete all the objects from the cache. We can use session contains() method to check if an object is present in the hibernate cache or not, if the object is found in cache, it returns true or else it returns false.
Interface CacheConcurrencyStrategy. All Known Implementing Classes: NonstrictReadWriteCache, ReadOnlyCache, ReadWriteCache, TransactionalCache public interface CacheConcurrencyStrategy. Implementors manage transactional access to cached data. Transactions pass in a timestamp indicating transaction start time.
According to the JPA 2.0 specification, if you want to selectively cache entities using the @Cacheable
annotation, you're supposed to specify a <shared-cache-mode>
in the persistence.xml
(or the equivalent javax.persistence.sharedCache.mode
when creating the EntityManagerFactory
).
Below, a sample persistence.xml
with the relevant element and properties:
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0"> <persistence-unit name="FooPu" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> ... <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode> <properties> ... <property name="hibernate.cache.provider_class" value="org.hibernate.cache.SingletonEhCacheProvider"/> <property name="hibernate.cache.use_second_level_cache" value="true"/> <property name="hibernate.cache.use_query_cache" value="true"/> </properties> </persistence-unit> </persistence>
Note that I've seen at least one issue HHH-5303 related to caching. So the above is not guaranteed :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With