How to mark entites as cacheable - java

I have java jar library with JPA entities, spring repositories and spring services and now i included this library in project where i want some entities to be cacheable (cached in second level cache) and don't want to change library.
Is is possible to configure Cachemanager to include entities which are not market as #Cacheable?

If you have control over the persistence.xml, you can add
<shared-cache-mode>ALL</shared-cache-mode>
for a persistence unit to cache alle entities or
<shared-cache-mode>DISABLE_SELECTIVE</shared-cache-mode>
to enable caching for all entities but the ones you marked as #Cacheable(false) (see also https://docs.oracle.com/javaee/7/api/javax/persistence/SharedCacheMode.html)

If you want to use Hibernate's session-independent second level cache which can be used by multiple Hibernate Session you must first activate it.
First modify persistence.xml, so add:
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
and property:
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
You can choose between ALL to cache all entities, NONE to don't cache any entities, ENABLE SELECTIVE to select the entities that will be cached and DISABLE SELECTIVE to select the entities that will not be cached. Prefer ENABLE SELECTIVE because it requires you to explicitly decide which entities are cached.
Annotate your entity with JPA #Cacheable or Hibernate #Cache annotation.
You can also use the #Cacheable (false) annotation to exclude an entity from caching if you use shared cache mode DISABLE SELECTIVE.
Hibernate doesn't use the second-level cache with JPQL or criteria queries.
If you want to use query caching modify persistence.xml, so add properties:
<property name="hibernate.cache.use_query_cache" value="true"/>
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
and activate caching for query with setCacheable(boolean b); method, (eg.) :
Session s = (Session) em.getDelegate();
Query q = s.createQuery("SELECT b FROM Book b WHERE id = :id");
q.setParameter("id", 1L);
q.setCacheable(true);
Book b = q.uniqueResult();
Hibernate now stores the result of this query in the query cache. When your next use case executes this query, Hibernate checks if the query cache contains a result for the given parameter values. If the cache contains a matching record, Hibernate gets the query result from there. Otherwise, it executes the query and store its result in the cache.

Related

Why do we have to use #Cacheable as well as #Cache in Hibernate for second-level of cache?

I want to know why we have to use 2 annotations to use the second-level of cache in Hibernate.
We declare :
#Cacheable
#Cache
Why don't we declare directly #Cache with the options?
I want to know why we have to use 2 annotations to use the
second-level of cache in Hibernate.
You don't need to use both, but you can. To enable the second level cache on an entity, you have 2 options:
(1) Use #Cacheable (more below)
(2) Use #Cache(more below)
#Cacheable is the JPA cache interface(used on an entity) that together with shared cache mode property decides whether an entity will be cached or not.
By default, entities are not part of the second level cache and we
recommend you to stick to this setting. However, you can override this
by setting the shared-cache-mode element in your persistence.xml file
or by using the jakarta.persistence.sharedCache.mode property in your
configuration file. The following values are possible:
ENABLE_SELECTIVE (Default and recommended value) Entities are not
cached unless explicitly marked as cacheable (with the #Cacheable
annotation).
DISABLE_SELECTIVE Entities are cached unless explicitly marked as
non-cacheable.
ALL Entities are always cached even if marked as non-cacheable.
NONE No entity is cached even if marked as cacheable. This option can
make sense to disable second-level cache altogether.
As per this:
Some developers consider that it is a good convention to add the
standard #javax.persistence.Cacheable annotation as well (although not
required by Hibernate).
#Cache is the Hibernate cache interface, recommended to specify the CacheConcurrencyStrategy on a per entity basis instead of the global setting hibernate.cache.default_cache_concurrency_strategy.
Why don't we declare directly #Cache with the options ?
That's exactly what is recommended, so that besides telling Hibernate that you want to enable second level caching for this entity, you also keep the concurrency strategy on a per entity basis, instead of global, as explained above. Remember to enable the second level cache(as by default, it' not), by using hibernate.cache.use_second_level_cache. A basic configuration of second level cache(I am using Spring Boot):
//In build.gradle:
implementation 'org.hibernate:hibernate-ehcache' //IMPORTANT: If you are specifying a version, make sure that it is the same version as your Hibernate version that you are using.
//Hibernate properties(can also be externalized to application.properties):
properties.put("hibernate.cache.use_second_level_cache", "true"); //hibernate.cache.use_second_level will also work
properties.put("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.EhCacheRegionFactory"); //act as a bridge between Hibernate and the caching provider
emf.setJpaProperties(hibernateProperties());
//In the entity class:
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
#Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class MyClass implements Serializable {
Note 1: The above is using Ehcache 2.x
Note 2: To use Ehcache 3.x, use the following properties:
properties.put("hibernate.javax.cache.provider", "org.ehcache.jsr107.EhcacheCachingProvider");
properties.put("hibernate.cache.region.factory_class", "jcache");
Note 3: Collections are not cached by default, you need to explicitly mark them with #Cache.
BONUS:
If you want to check the statistics:
//Add this property:
properties.put("hibernate.generate_statistics", "true");
//And somewhere in your code:
sessionFactory.getStatistics();

OpenJpa query caching is not refreshing in case of null value

I am facing some problem in OpenJpa second level caching. Most of times caching is working but in one particular case it is not working. Here is a scenario when it is not working,
When your code result null value then it store it into cache and then it never clear that value. Although it clears values only when query returns a value.
Here is code which I had written to get value from database,
List<PartnerapiworkflowEntity> partnerapiworkflowEntityList = null;
try {
partnerapiworkflowEntityList = entityManager.createQuery("select p from someentity p where p.id = :Id and p.name = :name and " +
"p.code = :Code and p.operationname = :operationName")
.setParameter("Id", Id)
.setParameter("name", name)
.setParameter("code", Code)
.setParameter("operationName", operationName).getResultList();//.getSingleResult();
if(partnerapiworkflowEntityList != null && partnerapiworkflowEntityList.size() > 0){
return Boolean.TRUE;
}
} catch (NoResultException ne) {
logger.severe("some logging info.");
}
finally {
// entityManager.detach(partnerapiworkflowEntity);
}
And here is a code which refresh cache.
try{
entityManager.flush();
entityManager.clear();
entityManager.getEntityManagerFactory().getCache().evictAll();
//((JpaEntityManager)entityManager.getDelegate()).getServerSession().getIdentityMapAccessor().invalidateAll();
entityManager.flush();
} catch (Exception e){
throw e;
}
And this is persistence.xml code
<property name="openjpa.jdbc.DBDictionary" value="mysql"/>
<property name="openjpa.DataCache" value="true(EnableStatistics=true, CacheSize=10000, SoftReferenceSize=0, EvictionSchedule='+10')"/>
<property name="openjpa.QueryCache" value="true(EvictPolicy='timestamp')"/>
<!--<property name="openjpa.jdbc.QuerySQLCache" value="true(EnableStatistics=true)"/>-->
<property name="javax.persistence.sharedCache.mode" value="ENABLE_SELECTIVE"/>
<property name="openjpa.Instrumentation" value="jmx(Instrument='DataCache,QueryCache,QuerySQLCache')"/>
<property name="openjpa.MetaDataRepository" value="Preload=true"/>
<property name="openjpa.Log" value="SQL=Trace" />
<property name="openjpa.ConnectionFactoryProperties" value="PrintParameters=true" />
Everything working fine when query always returns value. The problem is start when it return null value. Then first time is store in cache and then it never refresh.
I am using OpenJpa2 and Hibernate.
This issue was first observed with OpenJPA 2.2.2. Looking up online revealed that there was a defect that was fixed on trunk that was related to L2 cache (https://issues.apache.org/jira/browse/OPENJPA-2285)
But this problem is again found later in https://issues.apache.org/jira/browse/OPENJPA-2522
Solution:
So far it is not fixed yet. But they have given some bypassing solution.
Disable query cache
To disable the query cache (default), set the openjpa.QueryCache property to false:
<property name="openjpa.QueryCache" value="false"/>
By configuring sql query cache to false
To specify a custom cache class:
<property name="openjpa.jdbc.QuerySQLCache" value="com.mycompany.MyCustomCache"/>
To use an unmanaged cache:
<property name="openjpa.jdbc.QuerySQLCache" value="false"/>
OR
To use an unmanaged cache:
<property name="openjpa.jdbc.QuerySQLCache" value="all"/>
Open JPA - L2 Cache Issue and Workaround
This tutorial depicts your problem same to same. Here you can get the
clear conception of occuring this error.
It gives a solution that you must have to keep related data. So that NullPointerException will not arise. Data must be consistent until OpenJPA not solve the issue. :D
Several mechanisms are available to the application to bypass SQL
caching for a JPQL query.
A user application can disable Prepared SQL Cache for entire lifetime of a persistence context by invoking the following method on OpenJPA's EntityManager SPI interface:
OpenJPAEntityManagerSPI.setQuerySQLCache(boolean)
Plug-in property openjpa.jdbc.QuerySQLCache can be configured to
exclude certain JPQL queries as shown below.
<property name="openjpa.jdbc.QuerySQLCache" value="true(excludes='select c from Company c;select d from Department d')"/>
will never cache JPQL queries select c from Company c and select d from Department d.
Root Cause Analysis:
The query cache stores the object IDs that are returned by query executions. When you run a query, JPA assembles a key that is based on the query properties and the parameters that are used at launch time and checks for a cached query result. If one is found, the object IDs in the cached result are looked up, and the resulting persistence-capable objects are returned. Otherwise, the query is launched against the database and the object IDs that are loaded by the query are placed into the cache. The object ID list is not cached until the list that is returned at query launch time is fully traversed.
IBM Recommendation:
L2 caching increases the memory consumption of the application,
therefore, it is important to limit the size of the L2 cache. There is
also a possibility of stale data for updated objects in a clustered
environment. Configure L2 caching for read-mostly, infrequently
modified entities. L2 caches are not recommended for frequently and
concurrently updated entities.
Resource Link:
Open JPA 2.4.0 Caching Reference Guide
You are evicting entries, but what about query cache? It can be that in normal case evicting is noticed by query case, so the result is invalidated... It can explain why null fails here. Can you please confirm?
EDIT:
<property name="openjpa.QueryCache" value="false"/>
Means no query cache. My bad.
Other try - NULL check? You have query, with params - can you execute it directly on the database?

How to load data from database at the time of class loading by JVM and store it in List or Map for further use?

I am using spring and hibernate application, In my requirement I have to load data from database at the time of project deployement and store data in Map or List for further use in whole application. I have googled a lot but not found any solution.
try using a ServletContainerInitializer
https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Application_Platform/6/html/API_Documentation/files/javadoc/javax/servlet/ServletContainerInitializer.html
If this ServletContainerInitializer is bundled in a JAR file inside the WEB-INF/lib directory of an application, its onStartup method will be invoked only once during the startup of the bundling application.
You can use
<prop key="hibernate.cache.use_query_cache">true</prop>
In hibernate configuration.xml
to cache the data that you access from data base and store in object ... now if you use the same object anywhere in your application it will use the same cache and wil not hit the database again.
Another way is
you can configure the cache using the xml file, see the spring reference manual:
http://static.springsource.org/spring/docs/current/spring-framework-reference/html/cache.html#cache-declarative-xml
<!-- the service we want to make cacheable -->
<bean id="service" class="x.y.service.MyService"/>
<!-- cache definitions -->
<cache:advice id="cacheAdvice" cache-manager="cacheManager">
<cache:caching cache="books">
<cache:cacheable method="getData" key="#id"/>
<cache:cache-evict method="loadData" all-entries="true"/>
</cache:caching>
</cache:advice>
<!-- apply the cacheable behaviour to all dataService interfaces -->
<aop:config>
<aop:advisor advice-ref="cacheAdvice" pointcut="execution(* x.y.myService.*(..))"/>
</aop:config>
Better approach is to use Hibernate, In hibernate we have First Level cache.
First-level cache always Associates with the Session object. Hibernate uses this cache by default. Here, it processes one transaction after another one, means wont process one transaction many times. Mainly it reduces the number of SQL queries it needs to generate within a given transaction. That is instead of updating after every modification done in the transaction, it updates the transaction only at the end of the transaction.
First level cache retrieval example
In this example, I am retrieving DepartmentEntity object from database using hibernate session. I will retrieve it multiple times, and will observe the sql logs to see the differences.
//Open the hibernate session
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
//fetch the department entity from database first time
DepartmentEntity department = (DepartmentEntity) session.load(DepartmentEntity.class, new Integer(1));
System.out.println(department.getName());
//fetch the department entity again
department = (DepartmentEntity) session.load(DepartmentEntity.class, new Integer(1));
System.out.println(department.getName());
session.getTransaction().commit();
HibernateUtil.shutdown();
Output:
Hibernate: select department0_.ID as ID0_0_, department0_.NAME as NAME0_0_ from DEPARTMENT department0_ where department0_.ID=?
Human Resource
Human Resource
As you can see that second “session.load()” statement does not execute select query again and load the department entity directly.
If you need to load the collection at time of class loading you can make use of immediate fetching or eager loading strategy in Hibernate. According to eager loading, when you make any collection as eagerly loaded lets suppose-->
If Foo has a Collection, and you set it to lazy, then only when you need the contents fo that collection are the selected, loaded, etc. whereas if it is eager, it will load the Bars at the time it loads Foo. This can be problematic if you eagerly load a collection of entities that eagerly load a collection of entities, and so on.
But I suppose in your case the Fetch Type = eager strategy will work and it will load at the time the class is loaded.
An Example :
#OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JoinColumn(name="countryId")
private Set states;
First of all you can't use any List or Map as Cache replacement. You can use Hibernate Second Level Cache, or even an external caching solution as long as your data is not going to be modified, which might cause consistency issues.
So if teh cached data is read-only then you can safely cache it. Otherwise you need a read-write cache, and the second Level Cache is much more appropriate in this scenario.

Configure query cache without second level cache

i want to configure query cache without second level cache for some learning purpose as i have read it can be done but somehow for me query cache works only with 2nd level cache.
Here is my configuration
<property name="cache.use_query_cache">true</property>
Entity class is
#Entity
public class Company {}
but when i do
session = factory.openSession();
Query getQuery=session.createQuery("from Company where companyId=1");
getQuery.setCacheable(true);
Object company2 = getQuery.uniqueResult();
session.close();
session = factory.openSession();
getQuery=session.createQuery("from Company where companyId=1");
getQuery.setCacheable(true);
company2 = getQuery.uniqueResult();
session.close();
Two separate queries are fired .
As soon as i do
#Entity
#Cacheable
#Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
public class Company {}
Query cache work but it enables the second level cache also. So how to configure query
cache without enabling second level cache?
Query cache needs the 2nd level cache to do its job, since the results of the queries are then retrieved from the 2nd level cache. The query cache just keeps track of a query and the set of ids that the query returned. These ids are then retrieved from 2nd level cache from further speed increase. Where did you read you could have query cache without 2nd level cache?

how to disable cache in eclipselink

I have tried disabling L2 cache in EclipseLink with Eclipse indigo by using following properties in persistence.xml:-
<property name="eclipselink.cache.shared.default" value="false"/>
<shared-cache-mode>NONE</shared-cache-mode>
Basically I am testing one scenario whether same object created in two different sessions is hitting database twice or both sessions are referring to same object created in earlier session in memory cache. It should not because L2 cache is disabled by mentioning above properties in persistence.xml
My code is as below:-
Session session = DataAccessManager.getManager().openSession();
ReferenceObjectRepository referenceObjectRepository = ReferenceObjectRepository.getInstance();
ReferenceObjectKey referenceObjectKey = new ReferenceObjectKey(getStringValue("testCacheByPass.input"));
//load object first time.
ReferenceObject referenceObject = referenceObjectRepository.load(ReferenceObject.class, referenceObjectKey);
logger.log(Level.SEVERE, "Cache ReferenceObject: " + referenceObject);
//load object in another session
Session sessionNew = DataAccessManager.getManager().openNewSession();
Object dbObject = referenceObjectRepository.load(ReferenceObject.class, referenceObjectKey);
logger.log(Level.SEVERE, "DB loaded ReferenceObject: " + dbObject);
Please help me whether I have missed something? or do I need to do it some other way??
Add this line in each function where the call is made. I use in the find function when consulted a view.
((JpaEntityManager)em.getDelegate()).getServerSession().getIdentityMapAccessor().invalidateAll();
This line clear the cache before run de query.
public Entity find(Object id) {
((JpaEntityManager)em.getDelegate()).getServerSession().getIdentityMapAccessor().invalidateAll();
return em.find(Entity.class, id);
}
You have disabled the object cache, but I think you still have query cache in play. You should be able to disable query cache too with
<property name="eclipselink.query-results-cache" value="false"/>
<property name="eclipselink.refresh" value="true"/>
Same thing can be set with query hints, too. You could also try using query hints if persistence.xml configuration doesn't seem to be working.
Also note that essentially, even without the caching, you'd be comparing the same object, so unless it is detached it should be the same.
Related questions:
Disable eclipselink caching and query caching - not working?
Disable caching in JPA (eclipselink)

Categories