Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use second level cache for lazy loaded collections in Hibernate?

Let's say I have two entities, Employee and Skill. Every employee has a set of skills. Now when I load the skills lazily through the Employee instances the cache is not used for skills in different instances of Employee.

Let's Consider the following data set.

Employee - 1 : Java, PHP
Employee - 2 : Java, PHP

When I load Employee - 2 after Employee - 1, I do not want hibernate to hit the database to get the skills and instead use the Skill instances already available in cache. Is this possible? If so how?

Hibernate Configuration

<session-factory>
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="hibernate.connection.password">pass</property>
    <property name="hibernate.connection.url">jdbc:mysql://localhost/cache</property>
    <property name="hibernate.connection.username">root</property>
    <property name="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>

    <property name="hibernate.cache.use_second_level_cache">true</property>
    <property name="hibernate.cache.use_query_cache">true</property>
    <property name="hibernate.cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</property>
    <property name="hibernate.hbm2ddl.auto">update</property>
    <property name="hibernate.show_sql">true</property>

    <mapping class="org.cache.models.Employee" />
    <mapping class="org.cache.models.Skill" />
</session-factory>

The Entities with imports, getters and setters Removed

@Entity
@Table(name = "employee")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    private String name;

    public Employee() {
    }

    @ManyToMany
    @JoinTable(name = "employee_skills", joinColumns = @JoinColumn(name = "employee_id"), inverseJoinColumns = @JoinColumn(name = "skill_id"))
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    private List<Skill> skills;
}

@Entity
@Table(name = "skill")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Skill {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    private String name;
}

SQL for Loading the Second Employee and his Skills

Hibernate: select employee0_.id as id0_0_, employee0_.name as name0_0_ from employee employee0_ where employee0_.id=?
Hibernate: select skills0_.employee_id as employee1_1_, skills0_.skill_id as skill2_1_, skill1_.id as id1_0_, skill1_.name as name1_0_ from employee_skills skills0_ left outer join skill skill1_ on skills0_.skill_id=skill1_.id where skills0_.employee_id=?

In that I specifically want to avoid the second query as the first one is unavoidable anyway.

like image 929
Chandra Sekar Avatar asked Feb 18 '10 10:02

Chandra Sekar


People also ask

Does Hibernate support second level caching?

A Hibernate second-level cache is one of the data caching components available in the Hibernate object-relational mapping (ORM) library. Hibernate is a popular ORM library for the Java language, and it lets you store your Java object data in a relational database management system (RDBMS).

What is the disadvantage of 2nd level caching?

Performance degrade. Yes, having caching do NOT necessary to give you better performance. Hibernate needs to do extra work to store and update the cache. If the entities cached are changed frequently and you are not querying them frequently, enabling the cache is just adding unnecessary extra burden.


1 Answers

You need to cache the Employee--<>Skills association. Example taken from Speed Up Your Hibernate Applications with Second-Level Caching below:

<hibernate-mapping package="com.wakaleo.articles.caching.businessobjects">
    <class name="Employee" table="EMPLOYEE" dynamic-update="true">
        <meta attribute="implement-equals">true</meta>    

      <id name="id" type="long" unsaved-value="null" >
            <column name="emp_id" not-null="true"/>
            <generator class="increment"/>
      </id>

    <property column="emp_surname" name="surname" type="string"/>
    <property column="emp_firstname" name="firstname" type="string"/>

    <many-to-one name="country"
            column="cn_id"
                class="com.wakaleo.articles.caching.businessobjects.Country"  
            not-null="true" />

    <!-- Lazy-loading is deactivated to demonstrate caching behavior -->    
    <set name="languages" table="EMPLOYEE_SPEAKS_LANGUAGE" lazy="false">
        <cache usage="read-write"/>
        <key column="emp_id"/>
            <many-to-many column="lan_id" class="Language"/>
    </set>                              
    </class>
</hibernate-mapping>

Note the <cache> element inside the of languages.

like image 105
Pascal Thivent Avatar answered Nov 15 '22 03:11

Pascal Thivent