views:

288

answers:

1

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.

+1  A: 

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.

Pascal Thivent
I've done this already. It avoids the query only for those employees which are already loaded. Not employees not in cache who also have the same skills as another employee already in cache.In my initial example it would work if I load Employee - 1 again but not when I load Employee - 2.
Chandru
@Chandru Can you please show your mappings **and** the generated SQL?
Pascal Thivent
I'ev updated my question to add the information.
Chandru