Hibernate 性能最佳实践?
Posted
技术标签:
【中文标题】Hibernate 性能最佳实践?【英文标题】:Hibernate Performance Best Practice? 【发布时间】:2012-04-25 15:52:44 【问题描述】:我正在使用 Hibernate 3 编写一个 Web 应用程序。
所以,过了一会儿我注意到有些东西很慢。所以我测试了hibernate profiler,发现hibernate会为简单的操作进行不合理的许多db调用。原因当然是我加载了一个对象(这个对象有几个“父母”),而这些“父母”还有其他“父母”。所以基本上休眠会加载它们,即使我只需要基本对象。 好的,所以我研究了延迟加载。这导致我进入延迟加载异常,因为我有一个 MVC webapp。
所以现在我有点困惑,我最好的方法是什么。 基本上我只需要更新对象上的单个字段。我已经有了对象键。
我应该: 1.深入研究延迟加载。然后重写我的应用程序以获得开放会话视图? 2. 深入研究延迟加载。然后重写我的道更具体。例如。编写 DAO 方法来返回实例化的对象,这些对象只包含每个用例所需的内容?可能是很多额外的方法... 3.从头开始hibernate自己做? 4. 现在真的想不出其他解决方案。有什么建议吗?
最佳做法是什么?
【问题讨论】:
【参考方案1】: 除非确实需要,否则不要使用连接。它们不允许您既不使用延迟加载,也不使用二级缓存进行关联 对大型集合使用lazy="extra",在您询问之前它不会检索所有元素,您也可以使用 size() 方法而不从数据库中获取元素如果可能,请使用 load() 方法,因为它在需要之前不会发出选择查询。例如。如果您有 Book 和 Author 并且您想将它们关联在一起,这将不会发出任何选择,只会发出单个插入:
Book b = (Book) session.load(Book.class, bookId); Author a = (Author) session.load(Author.class, authorId); b.setAuthor(a); session.save(b);
使用命名查询(在您的 hbm 文件或 @NamedQuery 中),以便在每次查询期间不解析它们。在需要之前不要使用 Criteria API(在这种情况下无法使用 PreparedStatement 缓存)
在您的网络应用程序中使用 OSIV,因为它只会在需要时/如果需要时加载数据 对仅选择使用只读模式:session.setReadOnly(object, true)
。这将使 Hibernate 不会在持久上下文中保留所选实体的原始快照以进行进一步的脏检查。
用于只读数据和只读数据的用户二级缓存和查询缓存。
使用 FlushMode.COMMIT 而不是 AUTO,这样 Hibernate 不会在更新前发出 select,但要做好准备,这可能会导致写入过时的数据(尽管 Optimistic Locking 可以帮助您)。
查看批量提取(批量大小),以便一次选择多个实体/集合,而不是为每个实体/集合发出单独的查询。
执行诸如“从实体中选择新实体(id,someField)”之类的查询,以便仅检索必填字段。看看结果转换器。
如果需要,使用批处理操作(如删除)
如果您使用本机查询,请明确指定应使哪些缓存区域无效(默认为全部)。
查看树状结构的物化路径和嵌套集。
设置c3p0.max_statements
以便在池中启用 PreparedStatment 缓存,并在默认情况下关闭数据库的语句缓存。
如果可能,请使用 StatelessSession,它可以克服脏检查、级联、拦截器等。
不要将分页(setMaxResults()
、setFirstResult()
)与包含集合连接的查询一起使用,这将导致从数据库中提取所有记录,并且分页将由 Hibernate 在内存中进行。如果你想要分页,理想情况下你不应该使用连接。如果您无法逃脱它,请再次使用 - 使用批量提取。
其实有很多花样,不过暂时想不起来了。
【讨论】:
OSIV 你能解释一下我没明白吗【参考方案2】:您可以做很多事情来提高 Hibernate 的性能,例如:
-
启用 SQL 语句日志记录,以便您可以验证所有语句,甚至在测试期间检测 N+1 个查询问题。
使用 FlexyPool 进行数据库连接管理和监控
JDBC 批处理可减少提交 INSERT、UPDATE 和 DELETE 语句所需的往返次数。
JDBC 语句缓存
JPA 标识符优化器,例如 pooled 或 pooled-lo
选择紧凑型列类型
使用正确的关系:双向
@OneToMany
而不是单向关系,使用@MapsId
代表@OneToOne
,使用Set
代表@ManyToMany
以正确的方式使用继承并出于性能原因首选 SINGLE_TABLE
注意持久性上下文的大小并避免长时间运行的事务
在跳转到二级缓存之前使用操作系统缓存和数据库缓存,这对于在进行数据库复制时卸载主节点也很有用
通过 SQL 原生查询释放数据库查询功能
在多个一对一实体之间拆分写入,以[减少乐观锁定误报,即使在修改某些实体时也有更好的机会访问数据库缓存。
【讨论】:
【参考方案3】:我相信您想查看此section in the Hibernate manual。
我希望您最初的“...不合理的许多数据库调用...”问题是他们所谓的“N+1 选择问题”的一个实例。如果是这样,他们可以选择如何处理它。
-
使提取类型加入。然后,假设没有中间集合,您将拥有一个包含多个连接的单一选择。
延迟加载。
可能还有其他一些,例如我没有经验的 FetchProfiles。
前两个可以在关联级别指定,获取类型可以在查询级别覆盖。您应该能够让您的查询只做您需要的事情,而不是更多,并使用这些工具通过“良好”的 SQL 查询来完成它。
【讨论】:
以上是关于Hibernate 性能最佳实践?的主要内容,如果未能解决你的问题,请参考以下文章
确保 AUTHENTICATED 用户被授权访问资源的最佳实践 - Spring mvc、Spring Security、Hibernate