Hibernate查询缓存问题

时间:2012-01-30 06:58:29

标签: java hibernate spring ehcache query-cache

我在我的应用中发现了一个奇怪的错误。我简化了它,这就是它的再现方式:

(我使用DbUnit创建表和HSQLDB作为数据库,但这实际上并不重要)

package test;

import java.io.IOException;
import java.io.Serializable;
import java.io.StringReader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Set;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

import org.dbunit.DatabaseUnitException;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
import org.dbunit.operation.DatabaseOperation;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.springframework.orm.hibernate3.HibernateTemplate;

public class DatabaseBugReproduction {

    @Entity(name = "A")
    @Table(name = "a")
    public static class A {

        private int     id;
        private Set <B> bs;

        @Id
        public int getId() {
            return id;
        }

        @ManyToMany
        @JoinTable(
            name = "ab",
            joinColumns = @JoinColumn(name = "a_id"),
            inverseJoinColumns = @JoinColumn(name = "b_id")
        )
        public Set <B> getBs() {
            return bs;
        }

        void setId(int id) {
            this.id = id;
        }

        void setBs(Set <B> engines) {
            this.bs = engines;
        }
    }

    @Entity(name = "B")
    @Table(name = "b")
    public static class B {

        private int id;

        @Id
        public int getId() {
            return id;
        }

        void setId(int id) {
            this.id = id;
        }
    }

    private static SessionFactory getSessionFactory() throws SQLException, IOException, DatabaseUnitException {
        String driverClass = "org.hsqldb.jdbc.JDBCDriver";
        String jdbcUrl = "jdbc:hsqldb:mem:seoservertooltest";
        String dbUsername = "test";
        String dbPassword = "test";
        String dbDialect = "org.hibernate.dialect.HSQLDialect";
        Configuration config = new Configuration()//
        .setProperty("hibernate.connection.driver_class", driverClass)//
        .setProperty("hibernate.connection.url", jdbcUrl)//
        .setProperty("hibernate.connection.username", dbUsername)//
        .setProperty("hibernate.connection.password", dbPassword)//
        .setProperty("hibernate.dialect", dbDialect)//
        .setProperty("hibernate.hbm2ddl.auto", "create-drop")//
        .setProperty("hibernate.current_session_context_class", "thread")//
        .setProperty("hibernate.cache.use_query_cache", "true")//
        .setProperty("hibernate.cache.use_second_level_cache", "true")//
        .setProperty("hibernate.cache.region.factory_class", "net.sf.ehcache.hibernate.EhCacheRegionFactory")//
        .setProperty("hibernate.cache.region_prefix", "")//
        // .setProperty("hibernate.show_sql", "true")//
        // .setProperty("hibernate.format_sql", "true")//
        .addAnnotatedClass(A.class) //
        .addAnnotatedClass(B.class) //
        ;
        SessionFactory result = config.buildSessionFactory();
        try (Connection con = DriverManager.getConnection(jdbcUrl, dbUsername, dbPassword)) {
            con.createStatement().executeUpdate("SET DATABASE REFERENTIAL INTEGRITY FALSE;");
            String xml = "<?xml version='1.0' encoding='UTF-8'?>"//
                    + "<dataset>"//
                    + "<a id='1'/>"//
                    + "<b id='1'/>"//
                    + "<ab a_id='1' b_id='1' />"//
                    + "</dataset>";
            final IDatabaseConnection dbCon = new DatabaseConnection(con);
            try {
                final FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder();
                final IDataSet dataSet = builder.build(new StringReader(xml));
                DatabaseOperation.CLEAN_INSERT.execute(dbCon, dataSet);
            } finally {
                dbCon.close();
            }
        }
        return result;
    }

    public static void main(String[] args) throws Exception {
        HibernateTemplate hibTemplate = new HibernateTemplate(getSessionFactory());
        hibTemplate.setCacheQueries(true);
        System.out.println(hibTemplate.find("select a.bs from A a"));
        System.out.println(hibTemplate.find("select a.bs from A a"));
    }
}

输出是:

[test.DatabaseBugReproduction$B@2942ce]
[null]

看起来缓存在某种程度上是错误配置的。错误在哪里,我该如何解决?

用于:

  • JDK 1.7.0_01 x32和x64
  • Hibernate 3.6.7
  • Ehcache 2.5.0
  • Spring 3.1.0
  • 数据库:至少适用于HSQLDB,H2和MySQL

1 个答案:

答案 0 :(得分:2)

经过一些调试后,我发现查询缓存和集合查询似乎存在问题。将存储在缓存中的集合进行分解的方法始终返回null

事实上,在谷歌搜索之后,事实证明这个问题是由于Hibernate中的bug引起的。有关详细信息,请参阅问题说明。

虽然这个问题没有解决(似乎不会),但您可以重新编写查询,这样就不需要收集查询了:

public static void main(String[] args) throws Exception {
    HibernateTemplate hibTemplate = new HibernateTemplate(getSessionFactory());
    hibTemplate.setCacheQueries(true);
    //System.out.println(hibTemplate.find("select a.bs from A a"));
    //System.out.println(hibTemplate.find("select a.bs from A a"));
    System.out.println(hibTemplate.find("select bs from A a inner join a.bs as bs"));
    System.out.println(hibTemplate.find("select bs from A a inner join a.bs as bs"));
}

我已经测试过它并且工作正常。