11

We have an extensive entity model with 100+ entity classes. All the entity classes are subclasses of a single entity superclasses. The shared cache mode has been set to ALL.

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@Table(name = "entities")
public abstract class LongIdEntity {

  @Id
  @GeneratedValue(strategy = GenerationType.TABLE)
  private Long id;

  @Version
  @Column(name = "OPT_LOCK_VERSION")
  private Long lockVersion;

  etc...

}

An example subclass:

@Entity
@Table(name = "cars")
public class Car extends LongIdEntity { ... }

We'd like to cache all entities in the 2nd level cache. The problem is that only 1 cache region is made for all the entities; named LongIdEntity.

Debugging shows Hibernate did find all the entity classes, but assigns them the same region anyway. Because at SessionFactoryImpl:339 :

String cacheRegionName = cacheRegionPrefix + model.getRootClass().getCacheRegionName();

In our case, the call to model.getRootClass() will always yield "LongIdEntity".

I presume this would indeed cache all the entities, but without any control of eviction. Some classes are very frequent and read-only. So we want to keep them pinned into memory. Some are typically used in a certain time span, etc... Cramming it all into the same cache invalidates it all.

Specifying the region in the annotation has no effect. For example:

@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE,region = "cars")
@Entity
@Table(name = "cars")
public class Car extends LongIdEntity { ... }

The weird thing is that only shared cache mode ALL picks up the entity classes. In any other mode no entities are - even when annotated with @Cache and/or @Cacheable. Maybe this is an indication ?

Somebody has an idea how I can assign specific entity classes specific regions ?

TIA :-)

persistence.xml is elementary:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" 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">
  <persistence-unit name="cars" transaction-type="RESOURCE_LOCAL">
    <shared-cache-mode>ALL</shared-cache-mode>
  </persistence-unit>
</persistence>

The session factory is made the classic way:

  <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceProviderClass" value="org.hibernate.ejb.HibernatePersistence"/>
    <property name="persistenceUnitName" value="optimus"/>
    <property name="dataSource" ref="dataSource"/>
    <property name="jpaProperties">
      <props>
        <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</prop>
        <prop key="hibernate.cache.use_second_level_cache">true</prop>
        <prop key="hibernate.cache.use_query_cache">true</prop>
        <prop key="hibernate.generate_statistics">true</prop>
        <prop key="hibernate.cache.default_cache_concurrency_strategy">NONSTRICT_READ_WRITE</prop>
        <prop key="hibernate.hbm2ddl.auto">update</prop>
        <prop key="hibernate.jdbc.batch_size">1000</prop>
        <prop key="hibernate.show_sql">false</prop>
        <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
        <prop key="hibernate.search.default.directory_provider">filesystem</prop>
        <prop key="hibernate.search.default.indexBase">/hibernate-search</prop>
      </props>
    </property>
  </bean>

The environment

  • JDK6
  • Linux x64
  • Hibernate 4.1.10
  • Spring 3.2.1

UPDATE: Use @MappedSuperclass

@MappedSuperclass
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class LongIdEntity { ... }

Does not change a thing either.

Jan Goyvaerts
  • 2,913
  • 4
  • 35
  • 48
  • Can you check if [this question](http://stackoverflow.com/questions/4452242/specifying-global-ehcache-capacity) doesn't address the same problem as the one you're having? – mindas Mar 11 '13 at 15:48
  • I'm trying to get the opposite really. He's trying to have one big cache and I'm trying to have a cache for each entity. Or override this behavior with @Cache( region= ....). It used to work that way in the early day, but today it is ignored. – Jan Goyvaerts Mar 11 '13 at 16:08

2 Answers2

11

Hibernate caches the entire entity hierarchy in one region explicitly. There is no control over this. Its the only way to properly handled cached resolution of polymorphic lookups. Other providers (I think) do allow some control over where/how each subclass is cached (at least thats my guess based on the options provided by JPA).

Steve Ebersole
  • 9,339
  • 2
  • 48
  • 46
  • Not true. I've did this in the past. I've had the suggestion to try a different inheritance strategy. But I don't see what that has to do with caching really. – Jan Goyvaerts Mar 17 '13 at 09:50
  • 11
    I am the lead developer of Hibernate. I assure you, it caches an entire mapped hierarchy in a single region. And has done so every since it has had caching ;) – Steve Ebersole Mar 18 '13 at 13:57
  • 1
    Well using @MappedSuperclass is not an entity hierarchy. Bit of semantics. Essentially in that modified model, each direct entity subclass of LongIdEntity is a distinct persistent hierarchy. – Steve Ebersole Mar 18 '13 at 16:15
  • Making it @MappedSuperclass didn't change a thing. Unfortunately. So it's probably like the person of the thread suggests - related to the joined inheritance. But what's the relationship with caching ? – Jan Goyvaerts Mar 18 '13 at 20:30
  • @SteveEbersole Do you have any plans to implement ability of using different regions for subclasses? If yes, please give us a link to JIRA issue, I want to vote for it :) – Sergey Ponomarev Oct 21 '15 at 13:44
  • 1
    No, I have no plans for that. – Steve Ebersole Apr 17 '16 at 00:36
1

I have had this issue, and searching for internet I have ending in this Stackoverflow thread. It is really instructive to me due to I have the same issue that the one explained in this question. Reading the discussion between Steve Ebersole and Jan Goyvaerts address me to the solution of my case. Specially the link provided by one of them to Hibernate forum.

Testing the recommendations of the forum (also has passed three years from this question, then probably something has changed) I have success in generating different cached regions for different children of a parent class.

In my case, the solution was using @MappedSuperclass removing @Inheritance(strategy = InheritanceType.JOINED). In this case, I can see the correct hits in the correct areas.

For reference, my code is similar to that:

@MappedSuperclass
public abstract class ParentObject implements Serializable {
    ....


@Entity
@Cacheable(true)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class ChildObject extends ParentObject {
    ....

Now in the log I can see:

Cache: com.<...>.ChildObject store hit for com.<...>.ChildObject#52

Instead of:

Cache: com.<...>.ParentObject store hit for com.<...>.ParentObject#52

Of course, changing this annotations has a different behaviour that the one used in the question, and is not the desired one in some cases. But still I think it worth it to let this comment here as future reference. Maybe can help other person.

King Midas
  • 1,442
  • 4
  • 29
  • 50
  • Are you sure that the `@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)` is really needed ? The @MappedSuperclass does not require the `@Inheritance` annotation. – Thierry Jan 10 '17 at 09:58
  • You are right, it is out of this example. I use this code to avoid to create a table for some intermediate abstract class due to I have forgotten to add also `@MappedSuperclass` to these intermediate classes. I fix it also in my code and I update this answer. The solution is not to put `@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)`, but remove the `@Inheritance(strategy = InheritanceType.JOINED)` – King Midas Jan 10 '17 at 10:08