Hibernate / JPA的持久性,其中对象身份很重要

时间:2019-02-16 21:13:50

标签: java performance hibernate jpa caching

我使用Hibernate / JPA作为持久性后端,本质上可以归结为使用Java编写的游戏的mod。

在这种情况下,对我来说非常重要的是,我在主线程上尽可能少地查询数据库。尽管可能,但是异步地这样做是不切实际的,因为我不得不从其他线程中调用游戏对象的方法,而这种方法通常是行不通的。这意味着我必须使用缓存的对象在内存中做尽可能多的事情,以最大化性能(因为使用内存比等待查询从数据库返回结果要快得多)。

说我有如下定义的实体:

@Entity
class Town {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name = "id", updatable = false, nullable = false)
    private Long id;

    @OneToMany(mappedBy = "town", fetch = FetchType.EAGER) // use eager fetching to save on having to query the database later for this
    private Set<Resident> residents;

    // ... other fields and associated getters/setters
}

@Entity
class Resident {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name = "id", updatable = false, nullable = false)
    private Long id;

    @ManyToOne(fetch = FetchType.EAGER) // use eager fetching to save on having to query the database later for this
    @JoinColumn(name = "town_id")
    private Town town;

    // ... otehr fields and associated getters/setters
}

我的问题如下:

如果我要使用Hibernate检索所有常驻实体,并将其存储在内存中(例如,使用HashMap),然后如果我要继续使用Hibernate检索所有Town实体并以相同的方式缓存它们,则调用Town#getResidents()会返回对内存中与常驻缓存中存在的某些相同对象的引用

本质上,Hibernate是否会重用以前在查询中返回的仍然有效的对象来填充新创建的集合?

对于我的一般方法或如何改进它的建议,我也不会提出任何批评。先感谢您! :)

3 个答案:

答案 0 :(得分:2)

缓存是一个非常复杂的主题。您不必自己照顾缓存。这就是休眠second-level-cache的目的。

  

数据库抽象层(例如ORM)的优点之一   (对象关系映射)框架是   透明地缓存从基础存储中检索到的数据。   帮助消除频繁访问的数据的数据库访问成本。

您仍然必须将实体配置为可缓存,以及休眠应该如何积极地休眠,但是其余的将由休眠处理

WITH(NOEXPAND)

答案 1 :(得分:1)

如果堆消耗不是问题,或者生成的实例不是很多,那么您的方法也不错。我看到您已经在使用FetchType.EAGER,这是重要的部分。

我想说您甚至不需要检索Resident,您只需收集每个residents的{​​{1}} Set<Resident>

一旦检索到所有实例,我也将明确Town它们。

是的,Hibernate维护多个缓存级别。参见documentation


如果我要问的话,为什么要使用JPA?毕竟,不是更底层的方法(也许使用MyBatis)会是更好的方法吗?依靠像Hibernate这样的重量级框架不是过大吗?

答案 2 :(得分:0)

我不同意关于缓存的公认答案。我还有另一个答案,在这里我详细解释了为什么我不喜欢休眠二级缓存hibernate second level cache with Redis -will it improve performance?的原因到目前为止,休眠二级缓存的使用并不是一种常见的缓存策略。造成这种情况的原因有很多:

  • 休眠二级缓存非常低效。它使用默认的Java序列化,这非常慢,而且内存不足。
  • 通常需要使用休眠二级缓存,以保持关系的一致性。一个这样的例子是当您需要从集合中删除一个元素时。如果使用简单的pojoes,保持一致性通常不是什么大问题,但是当您开始将持久性逻辑与缓存混合在一起时,它就会变得很烦人。
  • 如果您决定从纯第二级缓存转到具有休眠的分布式缓存。复杂性不会以良好的方式飞速增长,然后您将了解为何休眠缓存效率低下的困难方法。

与接受的答案相反,我建议您将缓存与持久性分离为简单的Pojos。并通过这些Pojoes管理缓存。

现在就您的模特而言。我不知道您要涵盖的功能是什么,但我强烈怀疑有人会与所有居民一起来一个小镇。我建议您删除OneToMant关系,从城镇到居民。基于此,我看到以下情况:

  • 以居民为中心的数据处理,您可能会对居民产生重复次点击。您可以决定缓存整个居民加上城镇,或者,如果您未找到同一居民,则可以决定仅缓存城镇。
  • 将城镇和居民都缓存在同一地区,并且您的处理以居民为中心。您可以选择同时使用与镇和居民相同的密钥一起缓存,您会牺牲一些内存。但是您将一口气直接击中记忆和城镇。
  • 两个用于居民和城镇的缓存区域,但是随后您需要为1个居民执行两个查找。就内存而言,在性能而非真实方面更有效。
  • 仅缓存城镇。无论如何,无论您决定如何。我个人不会使用休眠二级缓存:)