持久化模式,第 2 部分: 提高代码重用和改进性能
2010-04-02 00:00:00 来源:WEB开发网清单 8 中的查询首先构造一个一般的条件查询,但是有意思的部分是对 setFetchMode 的调用。这个调用覆盖职员地址关联默认的惰性抓取策略。FetchMode.JOIN 在搜索查询期间获取 Address 实例,使用联结在一个查询中返回所有数据。在许多情况下,这就是我们需要的,它的效果比任何其他策略都要好。
但是,在使用 FetchMode.JOIN 时要注意一些问题。在使用基于集合的关联时,查询返回的行数可能与默认抓取策略返回的行数不同。这是因为返回的一个父对象可能有多个子对象会返回。Hibernate 在运行时无缝地隐藏这些细节,它会正确地解析结果集并返回正确的对象列表。但是,如果查询中还使用了 setFirstResult 或 setMaxResults,这种行为就会导致问题。在一般情况下,Hibernate 使用与数据库相关的 SQL 语句实现这些特性,但是因为原始的 SQL 查询返回不正确的行数,与数据库相关的技术无法发挥作用。相反,会从数据库获取完整的数据集,然后 Hibernate 提取满足请求所需的数据部分。这样的话,原本用来纠正 n+1 选择问题 的一个简单的性能调整却导致把大量不使用的行装载到应用程序层中。
Hibernate 还提供了第二种可以在查询中使用的抓取模式设置。FetchMode.SELECT 覆盖即时抓取策略,让关联使用惰性抓取。但是,无法在查询时覆盖关联来使用默认的即时抓取技术(第二个查询立即执行)。
批量惰性装载
n+1 选择问题 的另一个解决方案是,通过批量处理惰性装载请求,混合使用惰性抓取和即时抓取。这种方式仍然以惰性方式装载数据,但并不是每次惰性装载一个关联,而是装载多个关联。许多 ORM 框架实现了这种基本思想。Hibernate 把这个设置称为 BatchSize,TopLink 把它称为批量读取(batch reading)。
对于前面的示例,假设一个查询获取 10 个 Employee 实例。与 Employee 相关联的 Address 实例是惰性装载的,每批装载 5 个。最初,使用一个查询获取 Employee 实例,但是不执行针对 Address 实例的查询。当访问 Address 实例之一时,Hibernate 获取这个 Address 实例和后面 4 个惰性装载的实例。假设要访问与 Employee 实例相关联的所有 Address 实例,那么只需要用两个查询获取所有 Address 实例,而不是 10 个查询。
为了使用这种策略,需要在 Address 类中使用 @BatchSize 注解,见清单 9:
清单 9. @BatchSize 注解
@Entity
@Table(name="ADDRESS")
@BatchSize(size=5)
public class Address extends AuditableEntity {//...}
注意,在清单 9 中,是对Address 加上注解,而不是 Employee 类。加上这个注解之后,每次自动装载 5 个 Address 实例(如果可惰性装载的记录不足 5 个,那么装载的实例数量会更少)。还可以给集合加上 @BatchSize 注解,从而批量装载实体的集合。
结束语
本系列讨论了许多解决常见的持久化层问题的方法。尽管解决方案很简单(把主键重构在基类中以及修改模型的抓取策略),但是能够显著改进 Hibernate 和领域模型的效果,产生更容易维护的应用程序。通过 Hibernate 等框架把继承和多态性等面向对象概念应用于数据库,就能够产生表现力和可重用性更强的领域模型。我们希望这里介绍的最佳实践能够适用于您的环境和领域。
本文示例源代码或素材下载
更多精彩
赞助商链接