I'm not sure if it's a real problem or it's only a matter of configuration but I can see on my log console how hibernate hits (or at least throws the select query) even on cache hits.
I've checked the cache is properly working on Ehcache monitor and it registers 100% of hits for a certain request. But I always see the queries in the log.
All entities are anotated as shown:
#Entity
#Cacheable
#Cache(usage = READ_WRITE)
#Table(name = "city")
//#NamedQuery(name = "city.findById", query = "from City where ID = :id")
public class City extends Audit implements Serializable {
My ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
dynamicConfig="true" monitoring="autodetect">
<!-- Location of persistent caches on disk -->
<diskStore path="java.io.tmpdir/MxlServiceLayer" />
<cacheManagerPeerListenerFactory
class="org.terracotta.ehcachedx.monitor.probe.ProbePeerListenerFactory"
properties="monitorAddress=localhost, monitorPort=9889, memoryMeasurement=true" />
<defaultCache eternal="false" maxElementsInMemory="1000"
overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0"
timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU" statistics="true" />
<cache name="authToken" eternal="false" maxElementsInMemory="100"
overflowToDisk="false" diskPersistent="true" timeToIdleSeconds="0"
timeToLiveSeconds="31536000" memoryStoreEvictionPolicy="LRU"
statistics="true" />
</ehcache>
And what I see in my over and over again...
Hibernate: select city0_.ID as ID2_, city0_.CREATED as CREATED2_,
city0_.CREATOR as CREATOR2_, city0_.MODIFIED as MODIFIED2_,
city0_.MODIFIER as MODIFIER2_, city0_.NAME as NAME2_, city0_.state_fk
as state7_2_ from city city0_ where State_fk=?
Is hibernate really hitting the DB? Can anyone explain this to me, please?
I'm using:
JPA 2.0 in Spring Data JPA 1.2.0
Ehcache 2.6.0
Spring 3.2.1
What you enabled is the second-level cache. This cache caches the state of the entities, and indexes them by their ID. It's like a Map<ID, EntityState>. This cache is only used if you get the entity by ID:
by calling session.get()
by calling session.load()
by navigating through a XxxToOne association targeting the entity
Your query doesn't fall into this category: it looks for your entity by one of its fields, and not by its ID.
For the rest, Hibernate can't execute arbitrary SQL queries against the cache, and even if it could, the cache only contains a subset of the table, so it has to query the database.
You can however, cache the result of queries as well. You'll need to enable the query cache to do so, and to mark your query as cacheable. You can also cache an association (which seems like the reason for this query: it's looking for all the cities of a given state). You'll have to annotate the association with #Cache.
Read the documentation.
Related
I have a Spring Batch with Spring Boot application with an Oracle Wallet to connect and open JPA to handle persistence. I have some entities the primary keys of which are managed with sequences in the database, like this one:
CREATED 11/11/11
LAST_DDL_TIME 11/11/11
SEQUENCE_OWNER EXAMPLE
SEQUENCE_NAME EXAMPLE_SEQ
MIN_VALUE 1
MAX_VALUE 9999999999999999999999999999
INCREMENT_BY 1
CYCLE_FLAG N
ORDER_FLAG N
CACHE_SIZE 20
LAST_NUMBER 1111
and the corresponding entity annotation:
#Id
#SequenceGenerator(name="seq_examples", sequenceName="EXAMPLE_SEQ", allocationSize = 1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="seq_examples")
#Column (name="ID_EXAMPLE", nullable=false)
private Integer id_example;
With this code JPA is trying constantly to execute an ALTER SEQUENCE, reading a lot about this, I have got two ways to solve the problem, with Spring Boot config file or with the persistence.xml config file.
I have tried both of them: config ->
#Configuration
public class ContextConfiguration {
#Bean(name="springtest_entitymanager")
public LocalContainerEntityManagerFactoryBean getEntityManagerFactoryBean(
#Qualifier("vendorAdapter") JpaVendorAdapter jpaVendorAdapter,
#Value("${${enviroment}.databaseSchema}") String databaseSchema,
#Qualifier("datasourceWalletExampleDB") DataSource dataSource,
//#Value("${ConnectionFactoryProperties}") String ConnectionFactoryProperties,
#Value("${${enviroment}.openjpa.ConnectionFactoryProperties}") String
ConnectionFactoryProperties,
#Value("${${enviroment}.openjpa.log}") String logLevel
){
Map<String, String> jpaProperties = new HashMap<String, String>();
jpaProperties.put("openjpa.jdbc.Schema", databaseSchema);
jpaProperties.put("openjpa.Log", logLevel);
jpaProperties.put("openjpa.ConnectionFactoryProperties", ConnectionFactoryProperties);
jpaProperties.put("openjpa.jdbc.DBDictionary.disableAlterSequenceIncrementBy", "true");
//debug only
//jpaProperties.put("openjpa.jdbc.SynchronizeMappings", "buildSchema(ForeignKeys=true)");
LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean = new
LocalContainerEntityManagerFactoryBean();
localContainerEntityManagerFactoryBean.setPersistenceUnitName("pu_Example");
localContainerEntityManagerFactoryBean.setDataSource(dataSource);
localContainerEntityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter);
localContainerEntityManagerFactoryBean.setJpaPropertyMap(jpaProperties);
return localContainerEntityManagerFactoryBean;
}
...
and persistence ->
<persistence-unit name="pu_Example"
transaction-type="RESOURCE_LOCAL">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<class>exampleapp.dao.model.Example</class>
<properties>
<property name="openjpa.Log" value="DefaultLevel=ERROR" />
<property name="openjpa.ConnectionFactoryProperties" value="PrettyPrint=true,
PrettyPrintLineLength=72" />
<property name="openjpa.jdbc.Schema" value="EXAMPLE" />
<property name="openjpa.jdbc.DBDictionary" value="DisableAlterSeqenceIncrementBy=true" />
</properties>
</persistence-unit>
But it still keeps doing the alter sequence statement, what more can I do?
Some info that I have already read:
https://issues.apache.org/jira/browse/OPENJPA-2450
https://www-01.ibm.com/support/docview.wss?uid=swg1PI05956
How does the JPA #SequenceGenerator annotation work
Thanks to everybody who read this
Both properties(persistence.xml / configuration class) works fine, but I had to run the application at least one time with alter sequence privilege, probably some pk was not in sync between JPA and the DB in the first run of it, i think because of the DB cache. In this case the DB was migrated from another enviroment.
After the run with alter sequence privileges, the privileges were removed and after few more runs, i have not seen any more issue.
Usefull info that i found about caching in sequences and my help others:
https://developer.ibm.com/answers/questions/503873/why-does-the-jpa-sequence-generator-increase-more/
http://www.dba-oracle.com/t_sequence_caching.htm
https://www.logicbig.com/tutorials/java-ee-tutorial/jpa/seq-generator.html
We are trying to use EhCache as a distributed cache in our application.
EhCache instance are embedded in our server and we used a terracota cluster.
All our server (and ehcache) instances successfully connect to this tc.
We successfully inserts, update and get into each of our caches.
But we cannot iterates over any cache.
Maybe we have configured our caches in the wrong way, but it seems that the iterator methods are not yet implemented (in org.ehcache.clustered.client.internal.store.ClusteredStore):
#Override
public Iterator<Cache.Entry<K, ValueHolder<V>>> iterator() {
// TODO: Make appropriate ServerStoreProxy call
throw new UnsupportedOperationException("Implement me");
}
Our cache configuration looks like the following:
<service>
<tc:cluster>
<tc:connection url="terracotta://10.23.69.20:9510/clustered"/>
<tc:server-side-config auto-create="true">
<tc:default-resource from="default-resource"/>
</tc:server-side-config>
</tc:cluster>
</service>
<cache-template name="CacheTemplate">
<resources>
<tc:clustered-dedicated unit="MB">16</tc:clustered-dedicated>
</resources>
<tc:clustered-store consistency="strong"/>
</cache-template>
<cache alias="CacheDaemon" uses-template="CacheTemplate">
<key-type>java.lang.String</key-type>
<value-type>com.server.cache.DaemonInstance</value-type>
</cache>
<cache alias="CacheProperty" uses-template="CacheTemplate">
<key-type>java.lang.String</key-type>
<value-type>java.lang.String</value-type>
</cache>
I don't found any other way, even getting the key list.
So did we made a mistake in the cache configuration ?
Or is it that the EhCache distributed mode is totally incompatible with this method (and so we won't use EhCache).
I am trying to configure Ehcache as a hibernate 2nd-level cache, and so far all the examples I've found instruct you to create an ehcache.xml file in the classpath like:
<ehcache updateCheck="false">
<diskStore path="java.io.tmpdir" />
<defaultCache maxElementsInMemory="10000" eternal="false"
statistics="true" timeToIdleSeconds="120" timeToLiveSeconds="120"
overflowToDisk="true" diskPersistent="false"
diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" />
<cache name="com.yourcompany.CachedEntity" eternal="true" maxElementsInMemory="1000" />
</ehcache>
And then configure Hibernate as follows:
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"/>
<property name="net.sf.ehcache.configurationResourceName" value="/ehcache.xml" />
<property name="hibernate.cache.use_query_cache" value="true" />
<property name="hibernate.cache.use_second_level_cache" value="true" />
In my workplace we are encouraged to use java config wherever possible and avoid XML config files. I'd appreciate any pointers on how this can be achieved.
The stackoverflow question using ehcache in spring 4 without xml mentioned by learningJava shows how to configure an ehcache CacheManager in java but you still need a way to tell hibernate that it should use your java configured CacheManager.
A possible way to this would be to configure a custom ehcache region factory via the hibernate.cache.region.factory_class property. If you look at the implementation of SingletonEhCacheRegionFactory you'll see that it will be quite easy to swap in your own CacheManager.
I suspect that the mapping between the java configuration and the xml configuration of a cache is quite straightforward, but if ehcache performs some 'magic stuff' behind the scenes when it parses the xml configuration, the job of getting your java configuration right might be a bit trickier.
I also could not find sample code for this problem so I thought that I would share my solution. #Pieter 's answer set me on the right track and you need to implement your own EhCacheRegionFactory class so that you can provide custom java Configuration objects. It appears that the stock library only supports xml. Here's what my solution looks like:
public class CustomEhCacheRegionFactory extends EhCacheRegionFactory{
private static final EhCacheMessageLogger LOG = Logger.getMessageLogger(
EhCacheMessageLogger.class,
CustomCacheRegionFactory.class.getName()
);
#Override
public void start(Settings settings, Properties properties) throws CacheException {
this.settings = settings;
if ( manager != null ) {
LOG.attemptToRestartAlreadyStartedEhCacheProvider();
return;
}
try {
final Configuration configuration = new Configuration();
configuration.addCache( getEntityCacheConfiguration() );
manager = new CacheManager( configuration );
mbeanRegistrationHelper.registerMBean( manager, properties );
}
catch (net.sf.ehcache.CacheException e) {
//handle
}
}
/**
* Create the basic second level cache configuration
* #return
*/
private CacheConfiguration getEntityCacheConfiguration()
{
CacheConfiguration config = new CacheConfiguration("entity", 50000);
config.setTimeToIdleSeconds(86400);
config.setTimeToLiveSeconds(86400);
return config;
}
}
I have following method which returns data from cache.
Customer customer = customerDao.findByName("Carl");
It returns just one Customer object with name="Carl". When I make following change:
customer.setName("Ron");
and then I call the method again
Customer customer = customerDao.findByName("Carl");
It returns null. I dont make any persist or update operations and the cached object is directly changed.
I tried to use copyOnRead attribute in ehcache config but it doesnt help.
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<!-- default configuration -->
<defaultCache maxElementsInMemory="1000000"
eternal="true" overflowToDisk="false" copyOnRead="true" />
</ehcache>
Is there any way how to force ehcache to copy objects from cache?
Thank you
It looks to me as though support for multi tenancy has been added to hibernate for nearly six months now and updated at least once since.
It looks fairly trivial to obtain a multi-tenant Session outside of JPA:
Session session = sessionFactory.withOptions().tenantIdentifier( "jboss" ).openSession();
But how would you enable it in an application that uses hibernate via JPA? (If possible).
Thanks in advance.
You can configure it via properties in persistence.xml as follows:
<property name="hibernate.multiTenancy" value="DATABASE"/>
<property name="hibernate.multi_tenant_connection_provider" value="com.example.MyConnectionProvider" />
<property name="hibernate.tenant_identifier_resolver" value="com.example.MyTenantIdResolver" />
If you use SCHEMA as multi-tenancy strategy hibernate.multi_tenant_connection_provider is not needed.
You can also set these properties in your code and pass them in a map to Persistence.createEntityManagerFactory(). In this case you can pass an object instance, not just a class name.
More info in Hibernate documentation.
EntityManager.getDelegate() will return underlying SessionImpl.