【问题标题】:Setting hibernate second level cache设置休眠二级缓存
【发布时间】:2011-07-08 01:52:31
【问题描述】:

我是 hibernate 和 spring 的新手,我尝试使用 hibernate 二级缓存。但它似乎不起作用。我有以下测试类:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
@TransactionConfiguration
@Transactional
public class CacheTest extends AbstractTransactionalJUnit4SpringContextTests
{   
    @Test
        public void testCache1() 
        {
        System.out.println("Running testCache1");
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        MutableDAO<AppUser> appUserDAO = new MutableDAOImpl<AppUser>(AppUser.class, (SessionFactory) ctx.getBean("OnMediaSessionFactory"), 10);
        assertNotNull("AppUser DAO is null.", appUserDAO);

        SessionFactory sessionFactory = (SessionFactory)ctx.getBean("OnMediaSessionFactory");
        long numberOfUsers = appUserDAO.countAll();

        System.out.println("Number of rows :" + numberOfUsers);
        final String cacheRegion = AppUser.class.getCanonicalName();

        SecondLevelCacheStatistics settingsStatistics = sessionFactory.getStatistics().
            getSecondLevelCacheStatistics(cacheRegion);
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        appUserDAO.findAll();
        stopWatch.stop();
        System.out.println("Query time : " + stopWatch.getTotalTimeSeconds());
        System.out.println(settingsStatistics);
     }

    @Test
    public void testCache2() 
    {
        System.out.println("Running testCache2");
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        MutableDAO<AppUser> appUserDAO = new MutableDAOImpl<AppUser>(AppUser.class, (SessionFactory) ctx.getBean("OnMediaSessionFactory"), 10);
        assertNotNull("AppUser DAO is null.", appUserDAO);

        SessionFactory sessionFactory = (SessionFactory)ctx.getBean("OnMediaSessionFactory");
        long numberOfUsers = appUserDAO.countAll();

        System.out.println("Number of rows :" + numberOfUsers);
        final String cacheRegion = AppUser.class.getCanonicalName();

        SecondLevelCacheStatistics settingsStatistics = sessionFactory.getStatistics().
            getSecondLevelCacheStatistics(cacheRegion);
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        appUserDAO.findAll();
        stopWatch.stop();
        System.out.println("Query time : " + stopWatch.getTotalTimeSeconds());
        System.out.println(settingsStatistics);
     }
}

我有

<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.use_sql_comments">true</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.generate_statistics">true</prop>
<prop key="hibernate.cache.use_structured_entries">true</prop>

但我得到这样的输出:

Running testCache1
Number of rows :81
Query time : 0.129
SecondLevelCacheStatistics[hitCount=0,missCount=0,putCount=81,elementCountInMemory=81,elementCountOnDisk=0,sizeInMemory=219634]
Running testCache2
Number of rows :81
Query time : 0.063
SecondLevelCacheStatistics[hitCount=0,missCount=0,putCount=81,elementCountInMemory=81,elementCountOnDisk=0,sizeInMemory=219634]

我必须做些什么才能让它工作?

【问题讨论】:

  • 实体类本身必须明确可缓存 - 你这样做了吗?
  • 是的,我有@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)

标签: hibernate spring second-level-cache


【解决方案1】:

您的测试看起来很奇怪,您为每个测试创建了一个新的应用程序上下文,因此 Hibernate SessionFactory 在测试之间无法生存,以及它的二级缓存。

正确的测试应该是这样的:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
public class CacheTest extends AbstractTransactionalJUnit4SpringContextTests
{   
    @Autowired
    private MutableDAO<AppUser> appUserDAO;

    @Autowired
    private SessionFactory sessionFactory;

    private TransactionTemplate tx;

    @Autowired
    public void setPtm(PlatformTransactionManagement ptm) {
        tx = new TransactionTemplate(ptm);
    }

    @Test
    public void doTestCache() {
        // Using programmatic transaction management since we need 2 transactions
        // inside the same method

        // 1st attempt
        tx.execute(new TransactionCallbackWithoutResult() {
            public void doInTransactionWithoutResult(TransactionStatus status) {
                testCache();
            }
        });

        // 2nd attempt
        tx.execute(new TransactionCallbackWithoutResult() {
            public void doInTransactionWithoutResult(TransactionStatus status) {
                testCache();
            }
        });

    }

    public void testCache() {
        long numberOfUsers = appUserDAO.countAll();

        System.out.println("Number of rows :" + numberOfUsers);
        final String cacheRegion = AppUser.class.getCanonicalName();

        SecondLevelCacheStatistics settingsStatistics = sessionFactory.getStatistics().
            getSecondLevelCacheStatistics(cacheRegion);
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        appUserDAO.findAll();
        stopWatch.stop();
        System.out.println("Query time : " + stopWatch.getTotalTimeSeconds());
        System.out.println(settingsStatistics);
     }      
 }

【讨论】:

  • 感谢您的回答。但是@Autowired public void setPtm(PlatformTransactionManagement ptm) { tx = new TransactionTemplate(ptm); 是什么? } 尤其是 PlatformTransactionManagement ?
  • @Daniel:程序化事务管理需要它,请参阅10.6 Programmatic transaction management。由于要检查缓存值在事务之间是否存在,因此需要在一个测试方法中执行多个事务,而程序化事务管理是一种很好的安排方式。
  • 我执行了你的代码,但现在我得到了行数:81 查询时间:0.142 SecondLevelCacheStatistics[hitCount=0,missCount=0,putCount=0,elementCountInMemory=0,elementCountOnDisk=0,sizeInMemory=0 ] 行数:81 查询时间:0.031 SecondLevelCacheStatistics[hitCount=0,missCount=0,putCount=0,elementCountInMemory=0,elementCountOnDisk=0,sizeInMemory=0]
  • @Daniel:如果缓存配置正确,加载单个实体时应该可以正常工作。但是,findAll() 似乎正在执行查询,因此您需要为该查询启用查询缓存,see here
  • @axtavt 我将 findAll() 更改为 AppUser aUser = (AppUser)sessionFactory.getCurrentSession().load(AppUser.class, 13351L) 现在我得到:行数:81 查询时间:0.0 SecondLevelCacheStatistics[hitCount=0,missCount=2,putCount=4,elementCountInMemory=4,elementCountOnDisk=0,sizeInMemory=10816] 行数:81 查询时间:0.0 SecondLevelCacheStatistics[hitCount=0,missCount=2,putCount=4,elementCountInMemory =4,elementCountOnDisk=0,sizeInMemory=10816] 但我仍然得到 hitCount=0
【解决方案2】:

首先,请记住 Hibernate 默认不使用任何缓存提供程序。因此,您需要为 Hibernate 的 2L 缓存提供“外部”缓存提供程序。对于我的回答,我将使用 ehcache 和 Hibernate 3.3。请注意,在更新的 Hibernate 版本中配置已更改,因此请阅读文档以了解您正在使用的确切版本。

在您的 Hibernate 配置中,您错过了一个部分,即将 Hibernate 指向实际的提供者。 hibernate.cache.provider_class 属性为 Hibernate 3.3 执行此操作。将其值设置为net.sf.ehcache.hibernate.SingletonEhCacheProvider

现在,您还需要一个 ehcache.xml,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache>

    <diskStore path="./cache" />

    <defaultCache   maxElementsInMemory="10000" 
                    eternal="true"
                    overflowToDisk="true"
                    diskPersistent="true" 
                    diskExpiryThreadIntervalSeconds="120"
                    memoryStoreEvictionPolicy="FIFO" />

    <cache  name="com.mycompany.jpa.MyEntity" 
            maxElementsInMemory="50" 
            overflowToDisk="true" />

    <cache  name="org.hibernate.cache.StandardQueryCache" 
            maxElementsInMemory="50" 
            overflowToDisk="true" />

    <cache  name="org.hibernate.cache.UpdateTimestampsCache" 
            maxElementsInMemory="5000"
            overflowToDisk="true" />

</ehcache>

你没有展示你的 DAO,所以我不确定它是否正确。请注意,您始终需要明确说明缓存,因为它旨在用于特定位置的解决方案,而不是用于所有内容的通用解决方案。这意味着,在您的 DAO 中,您将添加一个查询提示,说明您的查询是可缓存的(从您的测试来看,您似乎需要查询缓存,而不仅仅是实体缓存)。

如果仍然无法使其工作,请参阅以下 JIRA 中的附件。它包含一个启用缓存的 maven 项目,因此,您可以将其与您的代码进行比较:

https://issues.jboss.org/browse/JBPAPP-4224

【讨论】:

    猜你喜欢
    • 2010-10-20
    • 2017-07-05
    • 1970-01-01
    • 1970-01-01
    • 2019-04-02
    • 2010-10-29
    • 2023-03-03
    • 1970-01-01
    相关资源
    最近更新 更多