Reputation: 21
I'm trying to implement hibernate 2lvl entity cache invalidation. As second level cache provider I use Infinispan. Entities are configured in persistence.xml file:
<persistence-unit name="default" transaction-type="JTA">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<jta-data-source>java:jboss/datasources/MSSQLDS</jta-data-source>
<properties>
<property name="jboss.as.jpa.providerModule" value="org.hibernate:5.3" />
<property name="jboss.entity.manager.factory.jndi.name" value="java:jboss/EntityManagerFactory"/>
<property name="jboss.entity.manager.jndi.name" value="java:jboss/EntityManager"/>
<property name="hibernate.multiTenancy" value="DATABASE"/>
<property name="hibernate.multi_tenant_connection_provider" value="com.hibernate.DatabaseMultiTenantProviderImpl" />
<property name="hibernate.tenant_identifier_resolver" value="com.hibernate.DatabaseTenantResolverImpl" />
<property name="hibernate.dialect" value="org.hibernate.dialect.SQLServer2012Dialect"/>
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.use_sql_comments" value="false" />
<property name="hibernate.jpa.compliance.global_id_generators" value="false" />
<!-- l2 cache configuration -->
<property name="hibernate.cache.use_query_cache" value="true"/>
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<!-- For testing purposes only -->
<property name="hibernate.generate_statistics" value="false" />
<!-- Use Infinispan second level cache provider -->
<property name="hibernate.cache.region.factory_class" value="infinispan"/>
<property name="hibernate.transaction.factory_class" value="org.hibernate.transaction.CMTTransactionFactory"/>
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/>
<!--
Force using local configuration when only using a single node.
Otherwise a clustered configuration is loaded.
-->
<property name="hibernate.cache.infinispan.cfg" value="org/infinispan/hibernate/cache/commons/builder/infinispan-configs-local.xml"/>
<!-- added specific configuration for each cached entity -->
<property name="hibernate.cache.infinispan.destiny.ear/entity-ejb3.jar#default.com.entity.ejb3.ConfigSite.cfg" value="small-cache" />
<property name="hibernate.cache.infinispan.destiny.ear/entity-ejb3.jar#default.com.entity.ejb3.CircItem.cfg" value="small-cache" />
</properties>
</persistence-unit>
There is a cache container, configured in standalone.xml(WildFly):
<cache-container name="hibernate" marshaller="JBOSS" modules="org.infinispan.hibernate-cache" default-cache="entity">
<local-cache name="entity">
<transaction mode="NONE"/>
<expiration max-idle="120000"/>
</local-cache>
<local-cache name="small-cache">
<transaction mode="NONE"/>
<expiration lifespan="${CACHE_MAX_AGE_MILLIS}"/>
<heap-memory size="${SMALL_ENTITY_MAX_CAPACITY}" size-unit="ENTRIES"/>
</local-cache>
<local-cache name="medium-cache">
<transaction mode="NONE"/>
<expiration lifespan="${CACHE_MAX_AGE_MILLIS}"/>
<heap-memory size="${MEDIUM_ENTITY_MAX_CAPACITY}" size-unit="ENTRIES"/>
</local-cache>
<local-cache name="large-cache">
<transaction mode="NONE"/>
<expiration lifespan="${CACHE_MAX_AGE_MILLIS}"/>
<heap-memory size="${LARGE_ENTITY_MAX_CAPACITY}" size-unit="ENTRIES"/>
</local-cache>
<local-cache name="immutable-entity">
<transaction mode="NONE"/>
<expiration max-idle="120000"/>
</local-cache>
<local-cache name="local-query">
<expiration max-idle="300000"/>
</local-cache>
<local-cache name="timestamps">
<transaction mode="NONE"/>
</local-cache>
<local-cache name="pending-puts">
<transaction mode="NONE"/>
<expiration max-idle="60000"/>
</local-cache>
</cache-container>
Entity itself:
@Entity
@Cache(usage=CacheConcurrencyStrategy.TRANSACTIONAL)
@GenericGenerator(name = HibernateSequenceCache.NAME,
strategy = HibernateSequenceCache.STRATEGY,
parameters = { @Parameter(name=HibernateSequenceCache.SEQUENCE, value=DbSequenceSpecs.SEQUENCE_SITEID) }
)
public class ConfigSite implements Serializable {
private static final long serialVersionUID = 1l;
@Id
@GeneratedValue(generator = HibernateSequenceCache.NAME)
private Long siteID;
private String siteName;
}
pom.xml:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.3.28.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-core</artifactId>
<version>13.0.10.Final</version>
<scope>provided</scope>
</dependency>
I've tried to invalidate with Hibernate itself:
@PersistenceUnit(unitName = EJBFactory.PERSISTENCE_UNIT)
SessionFactory factory;
public void invalidateEntityCache(String jndiName, Object primaryKey) {
Cache cache = factory.getCache();
cache.evictEntityData(jndiName, (Serializable) primaryKey);
}
As a primary key I pass a Long value of Object id, jndiName is a full class name, like "com.entity.ejb3.ConfigSite". Unfortunatly, invalidation doesn't work (cache regions are created and filled with keys and values). I debugged a lot and observed that: evictEntityData() to create key hibernate uses this approach:
public Object generateCacheKey(
Object id,
EntityPersister rootEntityDescriptor,
SessionFactoryImplementor factory,
String tenantIdentifier) { some impl }
final Object key = cacheAccess.generateCacheKey( identifier, entityDescriptor, sessionFactory, null );
i don't know why but it passes tenantId as null and eviction fails On the other hand during session.find(Class entityClass, Object primaryKey) during second lvl cache check
entity = loadFromSecondLevelCache( event, persister, keyToLoad ); --->
final Object ce = getFromSharedCache( event, persister, source );
it uses another approach and put tenantId in key and needed entity can be found:
final Object ck = cache.generateCacheKey(
event.getEntityId(),
persister,
factory,
source.getTenantIdentifier()
);
Do you guys have any ideas how this problem can be solved? (looks like tenantId is missed) May be I need extend hibernate classes and override some hibernate methods? or upgrade hibernate version where this problem is solved. Any suggestions are welcome and I will check them.
Thanks in advance!
UPDATE I achieved cache invalidation through InfinispanCacheManager:
public void invalidateEntityCache(String jndiName, Object primaryKey) {
SessionFactoryImplementor factoryImpl = (SessionFactoryImplementor) factory;
String tenantIdentifier = UserContext.getMyContextName();
EntityPersister persister = factoryImpl.getMetamodel().entityPersister(jndiName);
EmbeddedCacheManager cacheManager = InfinispanCacheManagerProvider.getCacheManager();
jndiName = CACHE_PREFIX + jndiName;
if (cacheManager.cacheExists(jndiName)) {
Cache<Object,Object> infinispanCache = cacheManager.getCache(jndiName);
Object entityKey = DefaultCacheKeysFactory.staticCreateEntityKey(primaryKey, persister, factoryImpl, tenantIdentifier);
infinispanCache.remove(entityKey);
}
}
But still expect to do it with hibernate
Upvotes: 1
Views: 194