Bonjour

J'utilise Spring 2.5.4, Hibernate 3.2.6 et ehcache 1.5.

Je suis confronté au problème suivant. J'ai configuré, dans la session factory, un objet de domaine pour qu'il soit géré dans le cache de second niveau. Le cache de second niveau est EhCache. Lorsque j'atteins le nombre limite d'éléments dans la région du cache allouée à cet objet de domaine, Hibernate ne va plus chercher les objets dans le cache et lance des requêtes en base de données. Ceci quel que soit l'objet de domaine. J'ai pourtant configuré une stratégie d'éviction LRU (Least Frequently Used) qui est censée retirer du cache seulement l'objet de domaine utilisé le moins récemment.
Tout ce passe bien tant que je n'ai pas atteint la limite.

La configuration de ma session factory (avec Spring) :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 
	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="mappingResources">
			<list>
				...
				<value>com/corp/app/domain/hibernate/mapping/livre/TypeOuvrage.hbm.xml</value>
			</list>
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
				<prop key="hibernate.hbm2ddl.auto">create</prop>
				<prop key="hibernate.use_sql_comments">true</prop>
				<prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.SingletonEhCacheProvider</prop>
			</props>
		</property>
		<property name="entityCacheStrategies">
			<props>
				<prop key="com.corp.app.domain.livre.TypeOuvrage">read-only</prop>
			</props>
		</property>
	</bean>

La configuration de la région concernée dans ehcache.xml (fichier de conf EhCache) :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
	<defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" diskSpoolBufferSizeMB="30"
		maxElementsOnDisk="10000000" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" />

	<cache name="com.corp.app.domain.livre.TypeOuvrage" maxElementsInMemory="20" eternal="true" timeToLiveSeconds="0" overflowToDisk="false" />
Mon JUnit qui étend la class Spring AbstractTransactionalSpringContextTests :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
 
	public void test() {
		final TypeOuvrage type1 = new TypeOuvrage();
		type1.setCode("ROMAN");
		type1.setLibelle("Roman");
 
		final TypeOuvrage type2 = new TypeOuvrage();
		type2.setCode("BD");
		type2.setLibelle("Bande dessinée");
 
		final TypeOuvrage type3 = new TypeOuvrage();
		type3.setCode("DICO");
		type3.setLibelle("Dictionnaire");
 
		final Session session = super.getSession();
		session.save(type1);
		session.save(type2);
		session.save(type3);
 
		super.flushAndClearHibernateSession();
 
		final Statistics statistics = super.sessionFactory.getStatistics();
		statistics.setStatisticsEnabled(true);
		statistics.clear();
		TypeOuvrage t1 = (TypeOuvrage) session.get(TypeOuvrage.class, type1.getId());
		assertEquals(type1, t1);
		TypeOuvrage t2 = (TypeOuvrage) session.get(TypeOuvrage.class, type2.getId());
		assertEquals(type2, t2);
		TypeOuvrage t3 = (TypeOuvrage) session.get(TypeOuvrage.class, type3.getId());
		assertEquals(type3, t3);
		assertEquals(3, statistics.getPrepareStatementCount());
		assertEquals(3, statistics.getSecondLevelCachePutCount());
		assertEquals(0, statistics.getSecondLevelCacheHitCount());
		assertEquals(3, statistics.getSecondLevelCacheMissCount());
 
		super.flushAndClearHibernateSession();
		statistics.clear();
 
		t1 = (TypeOuvrage) session.get(TypeOuvrage.class, type1.getId());
		assertEquals(type1, t1);
		t2 = (TypeOuvrage) session.get(TypeOuvrage.class, type2.getId());
		assertEquals(type2, t2);
		t3 = (TypeOuvrage) session.get(TypeOuvrage.class, type3.getId());
		assertEquals(type3, t3);
		assertEquals(0, statistics.getPrepareStatementCount());
		assertEquals(0, statistics.getSecondLevelCachePutCount());
		assertEquals(3, statistics.getSecondLevelCacheHitCount());
		assertEquals(0, statistics.getSecondLevelCacheMissCount());
	}
Explication du test :
Dans un premier temps, je crée les objets de domaine. Je "flush" et je "clear" la session afin de m'assurer que les objets de domaine ne vont pas être récupérés dans le cache de premier niveau par la suite.
Ensuite je charge une première fois mes objets de domaine créés précédemment. Vu qu'ils ne sont ni dans le cache de premier niveau (session flushée et clearée), ni dans le cache de second niveau, des requêtes à la base sont lancées. Je m'assure de tout ceci avec des asserts et l'objet Statistics de Hibernate.
Enfin, dans le dernier bloc de code, je fais exactement pareil, sauf que cette fois ci, aucune requête n'est lancée car les objets ont été "putés" dans le cache de second niveau lors du bloc de code précédent.

Tout cela marche très bien quand je configure ma région ehcache avec maxElementsInMemory >= "3". Si je mets maxElementsInMemory="2", ça marche plus. Cela est normal car je ne peux avoir que 2 objets de domaine caché. Je devrais donc avoir 1 requête lancée pour accéder à l'objet de domaine qui n'a pas pu être caché car viré du cache pour cause de maxElementInMemory. Or Je n'ai pas une requête qui est lancée mais 3 !

Pourquoi ?

Merci d'avance pour votre aide