我在参加Michael Nygard在JAOO上的演讲,他谈论了在生产系统中处理故障。他写了《发布它!》,这是一本模式书,描述了常见的反模式,并说明了它们如何导致大规模停机和企业损失。在他的演讲中,他指出了ORM工具在处理一些问题上的不足,这促使我写这篇博客。
无界结果集
他提到ORM工具在处理他所说的“无界结果集”模式上表现不佳,这是由应用程序假设查询只会返回少量记录,但当投入生产时,查询突然开始返回数百万行记录,而不是开发/测试系统中的那几个。
我问Michael他实际上指的是什么,因为Hibernate一直都有分页支持,允许您以受控的方式安全地遍历结果。
Query q = sess.createQuery("from OrderLines lines where lines.order = :id") .setProperties(order) .setFirstResult(20).setMaxResults(10) List lines = q.list();
结果是(正如我所预期的),Michael所指的实际上是开发人员没有做专注的查询映射,而是盲目地映射整个对象模型,并做了这样的事情
List lines = order.getOrderLines();
这在99%的情况下可能工作得很好,因为订单中很少有多条记录,但有时企业会下大订单,你可能会得到数千条项目,这可能会在每次订单行及其关联对象被检索/使用时对内存和CPU使用产生连锁反应。
在我看来,这并不是ORM工具的过错,而是Java集合没有提供对大型集合进行透明迭代的API。但这样说并不能解决问题,一些ORM工具通过供应商特定的集合解决了这个问题 - 在Hibernate中,我们有集合过滤器
Collection lines = session.createFilter( order.getOrderLines(), "") .setFirstResult(20).setMaxResults(10) .list();
这为您在映射集合上提供了与正常查询相同的分页功能,并帮助您避免“无界结果集”反模式。
超时
Michael还强烈建议开发人员始终为任何远程调用使用超时,包括数据库查询。
当您访问可能过载并开始有缓慢响应时间或可能根本不响应的外部系统时,超时很重要。
Hibernate 支持 事务超时
try { //set transaction timeout to 3 seconds sess.getTransaction().setTimeout(3); sess.getTransaction().begin(); // do some work ... sess.getTransaction().commit() } catch (RuntimeException e) { sess.getTransaction().rollback(); throw e; // or display error message } finally { sess.close(); }
和 查询超时。
Query q = sess.createQuery("from OrderLines lines where lines.order = :id") .setProperties(order) .setFirstResult(20).setMaxResults(10) .setTimeOut(3); List lines = q.list();
这些也可以用在集合过滤器上。
Collection lines = session.createFilter( order.getOrderLines(), "") .setFirstResult(20).setMaxResults(10) .setTimeout(3) .list();
Hibernate 在这里并没有进行任何神奇的操作,我们只是将超时委托给底层系统,因此这些功能依赖于你的数据库或应用服务器供应商是否实现了标准的超时功能。
希望 Michael 会很高兴知道(我知道他知道 ;),如果你不小心重复了这些反模式中的某些,Hibernate 确实有一些功能可以帮助你避免它们。