hibernate-orm/reference/zh-cn/modules/best_practices.xml

149 lines
10 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<chapter id="best-practices" revision="2">
<title>最佳实践(Best Practices)</title>
<variablelist spacing="compact">
<varlistentry>
<term>设计细颗粒度的持久类并且使用<literal>&lt;component&gt;</literal>来实现映射。</term>
<listitem>
<para>
使用一个<literal>Address</literal>持久类来封装 <literal>street</literal>,
<literal>suburb</literal>, <literal>state</literal>, <literal>postcode</literal>.
这将有利于代码重用和简化代码重构(refactoring)的工作。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>对持久类声明标识符属性。</term>
<listitem>
<para>
Hibernate中标识符属性是可选的不过有很多原因来说明你应该使用标识符属性。我们建议标识符应该是“人造”的(自动生成,不涉及业务含义)。虽然原生类型从语法上可能更易于使用,但使用<literal>long</literal><literal>java.lang.Long</literal>没有任何区别,。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>为每个持久类写一个映射文件</term>
<listitem>
<para>
不要把所有的持久类映射都写到一个大文件中。把 <literal>com.eg.Foo</literal> 映射到<literal>com/eg/Foo.hbm.xml</literal>中, 在团队开发环境中,这一点显得特别有意义。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>把映射文件作为资源加载</term>
<listitem>
<para>
把映射文件和他们的映射类放在一起进行部署。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>考虑把查询字符串放在程序外面</term>
<listitem>
<para>
如果你的查询中调用了非ANSI标准的SQL函数那么这条实践经验对你适用。把查询字符串放在映射文件中可以让程序具有更好的可移植性。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>使用绑定变量</term>
<listitem>
<para>
就像在JDBC编程中一样应该总是用占位符"?"来替换非常量值,不要在查询中用字符串值来构造非常量值!更好的办法是在查询中使用命名参数。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>不要自己来管理JDBC connections</term>
<listitem>
<para>
Hibernate允许应用程序自己来管理JDBC connections但是应该作为最后没有办法的办法。如果你不能使用Hibernate内建的connections providers那么考虑实现自己来实现<literal>org.hibernate.connection.ConnectionProvider</literal>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>考虑使用用户自定义类型(custom type)</term>
<listitem>
<para>
假设你有一个Java类型来自某些类库需要被持久化但是该类没有提供映射操作需要的存取方法。那么你应该考虑实现<literal>org.hibernate.UserType</literal>接口。这种办法使程序代码写起来更加自如不再需要考虑类与Hibernate type之间的相互转换。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>在性能瓶颈的地方使用硬编码的JDBC</term>
<listitem>
<para>
在对性能要求很严格的一些系统中,一些操作(例如批量更新和批量删除)也许直接使用JDBC会更好但是请先<emphasis>搞清楚</emphasis>这是否是一个瓶颈并且不要想当然认为JDBC一定会更快。如果确实需要直接使用JDBC那么最好打开一个
Hibernate <literal>Session</literal> 然后从 <literal>Session</literal>获得connection按照这种办法你仍然可以使用同样的transaction策略和底层的connection provider。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>理解<literal>Session</literal>清洗( flushing</term>
<listitem>
<para>
Session会不时的向数据库同步持久化状态如果这种操作进行的过于频繁性能会受到一定的影响。有时候你可以通过禁止自动flushing尽量最小化非必要的flushing操作或者更进一步在一个特定的transaction中改变查询和其它操作的顺序。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>在三层结构中,考虑使用 <literal>saveOrUpdate()</literal></term>
<listitem>
<para>
当使用一个servlet / session bean 类型的架构的时候, 你可以把已加载的持久对象在session bean层和servlet / JSP 层之间来回传递。使用新的session来为每个请求服务使用 <literal>Session.update()</literal> 或者<literal>Session.saveOrUpdate()</literal>来更新对象的持久状态。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>在两层结构中考虑断开session.</term>
<listitem>
<para>
为了得到最佳的可伸缩性,数据库事务(Database Transaction)应该尽可能的短。但是,程序常常需要实现长时间运行的“应用程序事务(Application Transaction)”,包含一个从用户的观点来看的原子操作。这个应用程序事务可能跨越多次从用户请求到得到反馈的循环。请使用脱管对象(与session脱离的对象)或者在两层结构中把Hibernate Session从JDBC连接中脱离开下次需要用的时候再连接上。绝不要把一个Session用在多个应用程序事务(Application Transaction)中,否则你的数据可能会过期失效。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>不要把异常看成可恢复的</term>
<listitem>
<para>
这一点甚至比“最佳实践”还要重要,这是“必备常识”。当异常发生的时候,必须要回滚 <literal>Transaction</literal> ,关闭<literal>Session</literal>。如果你不这样做的话Hibernate无法保证内存状态精确的反应持久状态。尤其不要使用<literal>Session.load()</literal>来判断一个给定标识符的对象实例在数据库中是否存在,应该使用<literal>find()</literal>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>对于关联优先考虑lazy fetching </term>
<listitem>
<para>
谨慎的使用主动外连接抓取(eager (outer-join) fetching)。对于大多数没有JVM级别缓存的持久对象的关联应该使用代理(proxies)或者具有延迟加载属性的集合(lazy collections)。对于被缓存的对象的关联,尤其是缓存的命中率非常高的情况下,应该使用<literal>outer-join="false"</literal>显式的禁止掉eager fetching。如果那些特殊的确实适合使用outer-join fetch 的场合,请在查询中使用<literal>left join</literal>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>考虑把Hibernate代码从业务逻辑代码中抽象出来</term>
<listitem>
<para>
把Hibernate的数据存取代码隐藏到接口(interface)的后面,组合使用<emphasis>DAO</emphasis><emphasis>Thread Local Session</emphasis>模式。通过Hibernate的<literal>UserType</literal>你甚至可以用硬编码的JDBC来持久化那些本该被Hibernate持久化的类。 (该建议更适用于规模足够大应用软件中对于那些只有5张表的应用程序并不适合。)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>使用与业务有关的键值来实现<literal>equals()</literal><literal>hashCode()</literal> .</term>
<listitem>
<para>
如果你在Session外比较对象,你必须要实现<literal>equals()</literal><literal>hashCode()</literal>。在Session内部Java的对象识别机制是可以保证的。如果你实现了这些方法不要再使用数据库(主键)辨识!瞬时对象不具有(数据库)标识值Hibernate会在对象被保存的时候赋予它一个值。如果对象在被保存的时候位于Set内hash code就会变化要约就被违背。为了实现用与业务有关的键值编写<literal>equals()</literal><literal>hashCode()</literal>你应该使用类属性的唯一组合。记住这个键值只是当对象位于Set内部时才需要保证稳定且唯一并不是在其整个生命周期中都需要不需要达到数据库主键这样的稳定性。绝不要在<literal>equals()</literal>中比较集合(要考虑延迟装载),并且小心对待其他可能被代理过的类。
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>不要用怪异的连接映射</term>
<listitem>
<para>
多对多连接用得好的例子实际上相当少见。大多数时候你在“连接表”中需要保存额外的信息。这种情况下,用两个指向中介类的一对多的连接比较好。实际上,我们认为绝大多数的连接是一对多和多对一的,你应该谨慎使用其它连接风格,用之前问自己一句,是否真的必须这么做。
</para>
</listitem>
</varlistentry>
</variablelist>
</chapter>