测试jpa时需要注意的二级缓存
最近写集成测试来测试一个实体+集合缓存的程序,单独执行没有问题,一旦多个测试用例一起执行会造成从第二个出现可能加载到第一个测试用例集合的内容。
测试用例:
第一个测试用例:
1、通过加载sql装载:
User#1
farms
Farm#1
Farm#2
2、接下来执行没有问题;
第二个测试用例:
1、通过加载sql装载:
User#1
farms
Farm#3
Farm#4
Farm#5
2、接着调用jpa的:
entityManager.getEntityManagerFactory().getCache().evictAll(); 清空二级缓存;
entityManager.clear(); 清空一级缓存
3、接下来会发现:
User#1 得到的farms还是 Farm#1、Farm#2 而没有新装载的,也就是得到第一个测试用例的。
这是为什么呢?
因为jpa没有集合缓存的概念,而是hibernate里特有的,因此entityManager.getEntityManagerFactory().getCache().evictAll(); 只清空实体缓存,而实体对应的集合缓存还是存在的,因此不会查数据库获取最新的。。。
jpa Cache相关的概念可参考http://en.wikibooks.org/wiki/Java_Persistence/Caching
hibernate jpa cache的实现代码如下(org.hibernate.ejb.EntityManagerFactoryImpl.JPACache):
public void evictAll() { sessionFactory.getCache().evictEntityRegions(); // TODO : if we want to allow an optional clearing of all cache data, the additional calls would be: //sessionFactory.getCache().evictCollectionRegions(); //sessionFactory.getCache().evictQueryRegions(); }
那就找能删除集合缓存的api吧,entityManager.getEntityManagerFactory().getCache()中是没有的。。咋办? 此时只能获取hibernate的SessionFactory 然后清空二级缓存了。
因此写了个工具类:
/** * Copyright (c) 2005-2012 https://github.com/zhangkaitao * * Licensed under the Apache License, Version 2.0 (the "License"); */ package com.sishuok.es.common.repository.hibernate; import org.hibernate.Cache; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.ejb.HibernateEntityManagerFactory; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; /** * 根据 jpa api 获取hibernate相关api * <p>User: Zhang Kaitao * <p>Date: 13-5-23 下午6:24 * <p>Version: 1.0 */ public class HibernateUtils { /** * 根据jpa EntityManager 获取 hibernate Session API * @param em * @return */ public static Session getSession(EntityManager em) { return (Session) em.getDelegate(); } /** * 根据jpa EntityManager 获取 hibernate SessionFactory API * @see #getSessionFactory(javax.persistence.EntityManagerFactory) * @param em * @return */ public static SessionFactory getSessionFactory(EntityManager em) { return getSessionFactory(em.getEntityManagerFactory()); } /** * 根据jpa EntityManagerFactory 获取 hibernate SessionFactory API * @param emf * @return */ public static SessionFactory getSessionFactory(EntityManagerFactory emf) { return ((HibernateEntityManagerFactory)emf).getSessionFactory(); } /** * 根据 jpa EntityManager 获取hibernate Cache API * @see #getCache(javax.persistence.EntityManagerFactory) * @param em * @return */ public static Cache getCache(EntityManager em) { return getCache(em.getEntityManagerFactory()); } /** * 根据jpa EntityManagerFactory 获取 hibernate Cache API * @param emf * @return */ public static Cache getCache(EntityManagerFactory emf) { return getSessionFactory(emf).getCache(); } /** * 清空一级缓存 * @param em */ public static void clearLevel1Cache(EntityManager em) { em.clear(); } /** * 根据jpa EntityManager 清空二级缓存 * @see #clearLevel2Cache(javax.persistence.EntityManagerFactory) * @param em */ public static void clearLevel2Cache(EntityManager em) { clearLevel2Cache(em.getEntityManagerFactory()); } /** * 根据jpa EntityManagerFactory 清空二级缓存 包括: * 1、实体缓存 * 2、集合缓存 * 3、查询缓存 * 注意: * jpa Cache api 只能evict 实体缓存,其他缓存是删不掉的。。。 * @see org.hibernate.ejb.EntityManagerFactoryImpl.JPACache#evictAll() * * @param emf */ public static void clearLevel2Cache(EntityManagerFactory emf) { Cache cache = HibernateUtils.getCache(emf); cache.evictEntityRegions(); cache.evictCollectionRegions(); cache.evictDefaultQueryRegion(); cache.evictQueryRegions(); cache.evictNaturalIdRegions(); } }
此时可以调用clearLevel2Cache清空二级缓存的所有内容。
相关阅读: