Hibernate.org Community Documentation

- 上一页
- 下一页

## 第 27 章 最佳实践（Best Practices）

设计细颗粒度的持久类并且使用 `<component>` 来实现映射：

  使用一个 `Address` 持久类来封装`street`，`suburb`，`state`，`postcode`。 这将有利于代码重用和简化代码重构（refactoring）的工作。

对持久类声明标识符属性（identifier properties）：

  Hibernate 中标识符属性是可选的，不过有很多原因来说明你应该使用标识符属性。我们建议标识符应该是“人造”的（自动生成，不涉及业务含义）。

使用自然键（natural keys）标识：

  对所有的实体都标识出自然键，用 `<natural-id>` 进行映射。实现 `equals()` 和 `hashCode()`，在其中用组成自然键的属性进行比较。

为每个持久类写一个映射文件：

  不要把所有的持久类映射都写到一个大文件中。把 `com.eg.Foo` 映射到 `com/eg/Foo.hbm.xml` 中。在团队开发环境中，这一点尤其重要。

把映射文件作为资源加载：

  把映射文件和他们的映射类放在一起进行部署。

考虑把查询字符串放在程序外面：

  如果你的查询中调用了非 ANSI 标准的 SQL 函数，那么这条实践经验对你适用。把查询字符串放在映射文件中可以让程序具有更好的可移植性。

使用绑定变量

  就像在 JDBC 编程中一样，应该总是用占位符 "?" 来替换非常量值，不要在查询中用字符串值来构造非常量值。你也应该考虑在查询中使用命名参数。

不要自己来管理 JDBC 连接：

  Hibernate 允许应用程序自己来管理 JDBC 连接，但是应该作为最后没有办法的办法。如果你不能使用 Hibernate 内建的 connections providers，那么考虑实现自己来实现 `org.hibernate.connection.ConnectionProvider`。

考虑使用用户自定义类型（custom type）：

  假设你有一个 Java 类型，来自某些类库，需要被持久化，但是该类没有提供映射操作需要的存取方法。那么你应该考虑实现 `org.hibernate.UserType` 接口。这种办法使程序代码写起来更加自如，不再需要考虑类与 Hibernate type 之间的相互转换。

在性能瓶颈的地方使用硬编码的 JDBC：

  在系统中对性能要求很严格的一些部分，某些操作也许直接使用 JDBC 会更好。但是请先确认这的确是一个瓶颈，并且不要想当然认为 JDBC 一定会更快。如果确实需要直接使用 JDBC，那么最好打开一个 Hibernate `Session` 然后将 JDBC 操作包裹为 `org.hibernate.jdbc.Work` 并使用 JDBC 连接。按照这种办法你仍然可以使用同样的 transaction 策略和底层的 connection provider。

理解 `Session` 冲刷（flushing）：

  Session 会不时的向数据库同步持久化状态，如果这种操作进行的过于频繁，性能会受到一定的影响。有时候你可以通过禁止自动 flushing，尽量最小化非必要的 flushing 操作，或者更进一步，在一个特定的 transaction 中改变查询和其它操作的顺序。

在三层结构中，考虑使用脱管对象（detached object）：

  当使用一个 servlet / session bean 类型的架构的时候, 你可以把已加载的持久对象在 session bean 层和 servlet / JSP 层之间来回传递。使用新的 session 来为每个请求服务，使用 `Session.merge()` 或者 `Session.saveOrUpdate()` 来与数据库同步。

在两层结构中，考虑使用长持久上下文（long persistence contexts）：

  为了得到最佳的可伸缩性，数据库事务（Database Transaction）应该尽可能的短。但是，程序常常需要实现长时间运行的“应用程序事务（Application Transaction）”，包含一个从用户的观点来看的原子操作。这个应用程序事务可能跨越多次从用户请求到得到反馈的循环。用脱管对象（与 session 脱离的对象）来实现应用程序事务是常见的。或者，尤其在两层结构中，把 Hibernate Session 从 JDBC 连接中脱离开，下次需要用的时候再连接上。绝不要把一个 Session 用在多个应用程序事务（Application Transaction）中，否则你的数据可能会过期失效。

不要把异常看成可恢复的：

  这一点甚至比“最佳实践”还要重要，这是“必备常识”。当异常发生的时候，必须要回滚 `Transaction` ，关闭 `Session`。如果你不这样做的话，Hibernate 无法保证内存状态精确的反应持久状态。尤其不要使用 `Session.load()` 来判断一个给定标识符的对象实例在数据库中是否存在，应该使用 `Session.get()` 或者进行一次查询。

对于关联优先考虑 lazy fetching：

  谨慎的使用主动抓取（eager fetching）。对于关联来说，若其目标是无法在第二级缓存中完全缓存所有实例的类，应该使用代理（proxies）与/或具有延迟加载属性的集合（lazy collections）。若目标是可以被缓存的，尤其是缓存的命中率非常高的情况下，应该使用 `lazy="false"`，明确的禁止掉 eager fetching。如果那些特殊的确实适合使用 join fetch 的场合，请在查询中使用 `left join fetch`。

使用 open session in view 模式，或者执行严格的装配期（assembly phase）策略来避免再次抓取数据带来的问题：

  Hibernate 让开发者们摆脱了繁琐的 Data Transfer Objects（DTO）。在传统的 EJB 结构中，DTO 有双重作用：首先，他们解决了 entity bean 无法序列化的问题；其次，他们隐含地定义了一个装配期，在此期间，所有在 view 层需要用到的数据，都被抓取、集中到了 DTO 中，然后控制才被装到表示层。Hibernate 终结了第一个作用。然而，除非你做好了在整个渲染过程中都维护一个打开的持久化上下文（session）的准备，你仍然需要一个装配期（想象一下，你的业务方法与你的表示层有严格的契约，数据总是被放置到脱管对象中）。这并非是 Hibernate 的限制，这是实现安全的事务化数据访问的基本需求。

考虑把 Hibernate 代码从业务逻辑代码中抽象出来：

  把 Hibernate 的数据存取代码隐藏到接口（interface）的后面，组合使用 DAO 和 Thread Local Session 模式。通过 Hibernate 的`UserType`，你甚至可以用硬编码的 JDBC 来持久化那些本该被 Hibernate 持久化的类。然而，该建议更适用于规模足够大应用软件中，对于那些只有 5 张表的应用程序并不适合。

不要用怪异的连接映射：

  多对多连接用得好的例子实际上相当少见。大多数时候你在“连接表”中需要保存额外的信息。这种情况下，用两个指向中介类的一对多的连接比较好。实际上，我们认为绝大多数的连接是一对多和多对一的。i因此，你应该谨慎使用其它连接风格。

偏爱双向关联：

  单向关联更加难于查询。在大型应用中，几乎所有的关联必须在查询中可以双向导航。

版权 © 2004 Red Hat, Inc.

- 上一页第 26 章 示例：复杂映射实例
- 上一级
- 起始页
- 下一页第 28 章 数据库移植性考量