【问题标题】:Caching Hibernate entity in a servlet session在 servlet 会话中缓存 Hibernate 实体
【发布时间】:2016-06-03 06:53:49
【问题描述】:

比方说,我在 servlet 会话(或类似的)中缓存当前用户,并收到类似“获取当前用户的所有书籍”的请求。天真地,我会去的

hibernateSession
.createCriteria(Book.class)
.add(Restrictions.eq("user", currentUser))
.list();

这可行,但在更复杂的情况下(我现在无法重现),我得到了一个

TransientObjectException: object references an unsaved transient instance

所以我想,我应该以某种方式将当前用户附加到会话中。我试过了

currentUser = (User) session.merge(currentUser);

只是发现它发出了一个数据库查询,因此使缓存并不比只存储 id 更好

这样的缓存能否高效完成?

【问题讨论】:

    标签: java performance hibernate session caching


    【解决方案1】:

    我还建议只缓存 ID,而不是整个实体。但是,您正在寻找 load(...) 方法而不是 merge(...) 或 get(...)。 load(...) 方法的优点是它只生成一个附加到您的会话的实体,而无需对数据库进行查询。假设没有其他人可以出现并删除记录,以便您知道您的记录仍然存在,那么加载方法的缺点(肯定存在)与您的用例无关。

    https://docs.jboss.org/hibernate/orm/3.5/javadocs/org/hibernate/Session.html#load(java.lang.Class, java.io.Serializable)

    这将为您提供一个可用于查询的对象,如示例中所示

    hibernateSession
    .createCriteria(Book.class)
    .add(Restrictions.eq("user", currentUser))
    .list();
    

    或者你也可以在持久化的同时在其他实体中设置

    book.setUser(currentUser);
    

    在我测试的hibernate版本中,访问hashCode()、toString()或equals()方法会导致实体被初始化,包括数据库查询。但是如果你只需要使用它来查询或设置你的外键引用,同时持久化其他实体,那么 load(Class, Serializable) 方法就是你要找的。​​p>

    已编辑 - 如果记录不存在会发生什么 你得到一个 ObjectNotFoundException。它扩展了 UnresolvableObjectException,后者扩展了 HibernateException,这当然是一个 RuntimeException。这意味着您的代码退出相当不优雅(除非您想在任何地方捕获此异常 - 但当然这不是一个合理的解决方案)

    org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.yourcode.YourEntity#id that does not exist]
    at org.hibernate.impl.SessionFactoryImpl$2.handleEntityNotFound(SessionFactoryImpl.java:419)
    at org.hibernate.proxy.AbstractLazyInitializer.checkTargetState(AbstractLazyInitializer.java:154)
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:143)
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:174)
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
    at com.yourcode.YourEntity_$$_javassist_143.getNonIdentifierMethod(YourEntity_$$_javassist_143.java)
    at com.yourcode.YourBusinessLogic.method(YourBusinessLogic.java:56)
    

    【讨论】:

    • 非常感谢,我想这就是我所追求的。您能否添加一些有关将load 与已删除实体一起使用时会发生什么灾难的信息?这不是我的问题的一部分,而是我似乎从未从 Hibernate 文档中理解的内容。
    【解决方案2】:

    为什么不只缓存 id?对于 Books 查询,您可以这样做:

    hibernateSession
    .createCriteria(Book.class)
    .add(Restrictions.eq("user.id", currentUserId))
    .list();
    

    如果您出于其他原因需要缓存的用户对象,您可以简单地使用currentUser.id,而不是currentUserId

    【讨论】:

    • 当然,但是对于Book 创建我需要user 本身。也许我可以使用一些级联合并...(就目前而言,我正在避免级联,因为我更喜欢明确说明)。
    • 合并(或加载)方法仍然提供性能优势,因为您只需要访问持久实体进行某些操作。我猜在您的应用程序中,查看图书列表的频率将比创建新图书的频率高。
    猜你喜欢
    • 1970-01-01
    • 2011-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多