【问题标题】:How to fetch a <map> in Hibernate如何在 Hibernate 中获取 <map>
【发布时间】:2014-02-11 13:56:36
【问题描述】:

更新:我创建了一个example on GitHub 来演示我的问题; HibernateMapTest 当前由于 HashMap 键是代理对象而失败。我希望有人可以建议一种方法,我可以查询实体并获取地图,以便测试通过...

我只是想获取一个保存在 Hibernate 中的 HashMap 的内容,但是我在找到正确的方法时遇到了一些麻烦...

HBM 映射如下,我没有创建它,但根据我的研究,它似乎是一个具有多对多关系的ternary association mapping(更新:为了简化我的问题,我已将地图强制设置为 lazy="false" 以避免加入)

<hibernate-mapping>
    <class name="database.Document" table="document">
        ...
        <map name="documentbundles" table="document_bundles" lazy="false">
            <key column="id"/>
            <index-many-to-many column="pkgitemid" class="database.PkgItem"/>
            <many-to-many column="child" class="database.Document" />
        </map>
    </class>
</hibernate-mapping>

为简单起见,我目前只是尝试获取填充了此地图数据的所有记录:

DetachedCriteria criteria = DetachedCriteria.forClass(Document.class);
criteria.add(Restrictions.eq("id", 1));
List<Document> result = hibernateTemplate.findByCriteria(criteria);

在 last 为 false 之后,我现在无需抛出 LazyInitializationException 即可获取 Map 的内容;但没有一个关键对象被正确初始化。我已经转储了一个屏幕截图来澄清我的意思:

我知道这些字段已填充到数据库中,我怀疑我的获取策略仍然是罪魁祸首。如何在 Hibernate 中正确获取 &lt;map&gt;

【问题讨论】:

  • 您可能不需要 stackoverflow.com/questions/1041773/…这个人成功了
  • 感谢您观看宙斯。我认为他们使用的集合映射类型与我正在查看的有点不同,我已经确定了映射不同的原因并扩展了我的问题。
  • 第二张截图来自哪个程序,OOC?
  • @hd1 我使用的 IDE 是带有内置“Darcula”主题的 Intelli-J IDEA 13。屏幕截图来自调试器变量面板。

标签: java mysql hibernate


【解决方案1】:

错误是由于HibernateTemplate打开了一个Hibernate会话来执行这个查询:

List results = hibernateTemplate.find("from database.Document d where d.name = 'doc1'");

然后在查询运行后立即关闭会话。然后在遍历键时,映射链接到的会话被关闭,因此无法再加载数据,导致代理抛出LazyInitializationException

此异常意味着代理无法再透明地加载数据,因为链接到的会话现在也已关闭。

HibernateTemplate 的主要目标之一是知道何时打开和关闭会话。如果有正在进行的事务,模板将保持会话打开。

所以这里的关键是将单元测试包装在TransactionTemplate(等效于@Transactional 的模板)中,这会导致会话由HibernateTemplate 保持打开状态。由于会话保持打开状态,因此不会再发生延迟初始化异常。

这样修改测试即可解决问题(注意使用TransactionTemplate):

import database.Document;
import database.PkgItem;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.HibernateTransactionManager;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.HashMap;
import java.util.List;
import java.util.Set;

public class HibernateMapTest {

    private static final String TEST_DIALECT = "org.hibernate.dialect.HSQLDialect";
    private static final String TEST_DRIVER = "org.hsqldb.jdbcDriver";
    private static final String TEST_URL = "jdbc:hsqldb:mem:adportal";
    private static final String TEST_USER = "sa";
    private static final String TEST_PASSWORD = "";

    private HibernateTemplate hibernateTemplate;
    private TransactionTemplate transactionTemplate;

    @Before
    public void setUp() throws Exception {
        hibernateTemplate = new HibernateTemplate();
        LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
        sessionFactory.getHibernateProperties().put("hibernate.dialect", TEST_DIALECT);
        sessionFactory.getHibernateProperties().put("hibernate.connection.driver_class", TEST_DRIVER);
        sessionFactory.getHibernateProperties().put("hibernate.connection.password", TEST_PASSWORD);
        sessionFactory.getHibernateProperties().put("hibernate.connection.url", TEST_URL);
        sessionFactory.getHibernateProperties().put("hibernate.connection.username", TEST_USER);
        sessionFactory.getHibernateProperties().put("hibernate.hbm2ddl.auto", "create");
        sessionFactory.getHibernateProperties().put("hibernate.show_sql", "true");
        sessionFactory.getHibernateProperties().put("hibernate.jdbc.batch_size", "0");
        sessionFactory.getHibernateProperties().put("hibernate.cache.use_second_level_cache", "false");

        sessionFactory.setMappingDirectoryLocations(new Resource[]{new ClassPathResource("database")});
        sessionFactory.afterPropertiesSet();

        hibernateTemplate.setSessionFactory(sessionFactory.getObject());

        transactionTemplate = new TransactionTemplate(new HibernateTransactionManager(sessionFactory.getObject()));
    }

    @After
    public void tearDown() throws Exception {
        hibernateTemplate.getSessionFactory().close();
    }

    @Test
    public void testFetchEntityWithMap() throws Exception {

        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                // Store the entities and mapping
                PkgItem key = new PkgItem();
                key.setName("pkgitem1");
                hibernateTemplate.persist(key);

                Document doc2 = new Document();
                doc2.setName("doc2");
                hibernateTemplate.persist(doc2);

                Document doc1 = new Document();
                doc1.setName("doc1");
                HashMap<PkgItem, Document> documentbundles = new HashMap<PkgItem, Document>();
                documentbundles.put(key, doc2);
                doc1.setDocumentbundles(documentbundles);
                hibernateTemplate.persist(doc1);

                // Now attempt a query
                List results = hibernateTemplate.find("from database.Document d where d.name = 'doc1'");
                Document result = (Document)results.get(0);

                // Check the doc was returned
                Assert.assertEquals("doc1", result.getName());

                key = (PkgItem)hibernateTemplate.find("from database.PkgItem").get(0);
                Set<PkgItem> bundleKeys = result.getDocumentbundles().keySet();

                // Check the key is still present in the map. At this point the test fails because
                // the map contains a proxy object of the key...
                Assert.assertEquals(key, bundleKeys.iterator().next());
            }
        });

    }
}

这些是测试结果和更改后的日志:

【讨论】:

  • 当,你打败了我,很好的答案!无论如何,有没有像这样的简单程序示例用@Transactional 标记它?除非你有完整的 Spring 配置和 &lt;tx:annotation-driven&gt;,否则不要认为它会激活?
  • 这正是我走上正轨所需要的。我仍然想在 Hibernate 的影响之外使用我生成的哈希映射,但经过一些实验后,我能够使用事务方法重建带有原始键的映射。我将在补充答案中发布我的发现。谢谢!
【解决方案2】:

这是对 jhadesdev 答案的补充,因为我需要做更多的工作才能得到我想要的东西。

总而言之,您不能通过 Hibernate 查询获取 PersistedMap 并像典型的 Java 哈希映射一样立即开始使用它。 键始终是代理;急切获取/加入仅获取映射值,而不是键。

这意味着任何处理哈希映射的代码都需要包装在 Hibernate 事务中,这给我带来了一些架构问题,因为我的数据和服务层是分开的。

我通过在单个事务中迭代哈希映射并将键替换为最初传入的键来解决此问题。我通过批量处理我想要获取的键并一次性检索它们来保持性能:

// Build a list of keys we want to fetch in one go
final List<PkgItem> pkgItems = Arrays.asList(pkgItem1, pkgItem2, ...);

Map<PkgItem, Document> bundles = transactionTemplate.execute(new TransactionCallback< Map<PkgItem, Document> >() {
    @Override
    public Map<PkgItem, Document> doInTransaction(TransactionStatus transactionStatus) {
        if (doc1.getId() == null) return null;

        // Merge the parent document into this transaction
        Document container = hibernateTemplate.merge(doc1);

        // Copy the original package items into the key set
        Map<PkgItem, Document> out = new HashMap<PkgItem, Document>();
        for (PkgItem dbKey : container.getDocumentbundles().keySet()) {
            int keyIndex = pkgItems.indexOf(dbKey);
            if (keyIndex > -1) out.put(pkgItems.get(keyIndex), container.getDocumentbundles().get(dbKey));
        }
        return out;
    }
});

// Now we can perform a standard lookup
assertEquals("doc2", result.get(pkgItem1).getName());

我现在可以在生成的代码中使用不带 Hibernate 的地图,而对性能的影响很小。我还更新了我的示例 GitHub 项目中的测试,以演示它是如何工作的。

【讨论】:

    猜你喜欢
    • 2016-05-24
    • 2020-05-09
    • 2020-04-23
    • 1970-01-01
    • 2013-05-03
    • 2020-12-27
    • 2020-11-27
    • 2016-08-22
    • 2020-07-14
    相关资源
    最近更新 更多