Hibernate最佳实践
使用一个Address持久类来封装 street, suburb, state, postcode. 这将有利于代码重用和简化代码重构(refactoring)的工作。
Hibernate中标识符属性是可选的,不过有很多原因来说明你应该使用标识符属性。我们建议标识符应该是“人造”的(自动生成,不涉及业务含义),并且不是基本类型。为了最大的灵活性,应该使用java.lang.Long or java.lang.String
对所有使用自然主键标志符的尸体使用<natural-id>,实现equals()和hashCode()方法来比较树型。
不要把所有的持久类映射都写到一个大文件中。把 com.eg.Foo 映射到com/eg/Foo.hbm.xml中, 在团队开发环境中,这一点显得特别有意义。
把映射文件和他们的映射类放在一起进行部署。
如果你的查询中调用了非ANSI标准的SQL函数,那么这条实践经验对你适用。把查询字符串放在映射文件中可以让程序具有更好的可移植性。
就像在JDBC编程中一样,应该总是用占位符"?"来替换非常量值,不要在查询中用字符串值来构造非常量值!更好的办法是在查询中使用命名参数。
Hibernate允许应用程序自己来管理JDBC connections,但是应该作为最后没有办法的办法。如果你不能使用Hibernate内建的connections providers,那么考虑实现自己来实现net.sf.hibernate.connection.ConnectionProvider
net.sf.hibernate.UserType. 假设你有一个Java类型,来自某些类库,需要被持久化,但是该类没有提供映射操作需要的存取方法。那么你应该考虑实现net.sf.hibernate.UserType接口。这种办法使程序代码写起来更加自如,不再需要考虑类与Hibernate type之间的相互转换。
在对性能要求很严格的一些系统中,一些操作(例如批量更新和批量删除)也许直接使用JDBC会更好,但是请先搞清楚这是否是一个瓶颈,并且不要想当然认为JDBC一定会更快。如果确实需要直接使用JDBC,那么最好打开一个 Hibernate Session 然后从 Session获得connection,按照这种办法你仍然可以使用同样的transaction策略和底层的connection provider。
Session会不时的向数据库同步持久化状态,如果这种操作进行的过于频繁,那么性能会受到一定的影响。有时候你可以通过禁止自动flushing尽量最小化非必要的flushing操作,或者更进一步,在一个特殊transaction中改变查询和其它操作的顺序。
当使用servlet / session bean体系结构的时候,需要使用Session读取持久对象,并将值对象传递到servlet / JSP层。为每一个请求使用一个新的Session。使用Session.merge()或者Session.saveOrUpdate()方法来让对象与数据库同步。
为了提高性能,数据库事务应该尽可能短。然而在实际业务中经常需要实现长时间运行的应用事务,每个用户的每个视图使用一个简单的工作单元。应用程序事务将横跨多个客户请求响应周期。通常情况下,使用Detached对象来实现应用程序事务。在两层体系结构中,通常,比较恰当的方法是在整个应用事务中维护一个单一的打开的持久连接(Session),然后在每个请求结束时简单的关闭掉JDBC连接,然后在并发请求中,重新连接它们。
这一点甚至比“最佳实践”还要重要,这是“必备常识”。当异常发生的时候,回滚 Transaction ,关闭Session。如果你不这样做的话,Hibernate无法保证内存状态精确的反应持久状态。尤其不要使用Session.load()来判断一个给定标识符的对象实例在数据库中是否存在,应该使用find()。当然,有一些例外情况,比如说StaleObjectStateException和ObjectNotFoundException。
补充:运行时异常 和 编译时异常
编译时异常 必须要在代码中 catch,否则无法通过编译
运行时异常 可以不显式的去catch
谨慎的使用主动外连接抓取(eager (outer-join) fetching)。对于大多数没有JVM级别缓存的持久对象的关联,应该使用代理(proxies)或者具有延迟加载属性的集合(lazy collections)。对于被缓存的对象的关联,尤其是缓存的命中率非常高的情况下,应该使用outer-join="false",显式的禁止掉eager fetching。如果那些特殊的确实适合使用outer-join fetch 的场合,请在查询中使用left join。
Hibernate允许开发冗长的数据传输对象(DTO)。在传统的EJB体系结构中,使用DTOs具有两个目的:第一,他们解决了实体Bean没有实现serializable接口的问题;第二,他们在为表示层提供数据时,DTO对象屏蔽了一些对象间的关联操作。Hibernate排除了第一种情况。然而,仍然需要一个连接阶段(设想业务逻辑严格定义表示层使用的数据对象),否则,在表现层渲染阶段就可能会将持久传递到表现层了。这不是Hibenate的局限性,这是保证事务性数据访问的基础原则。
把Hibernate的数据存取代码隐藏到接口(interface)的后面,组合使用DAO和Thread Local Session模式。通过Hibernate的UserType,你甚至可以用硬编码的JDBC来持久化那些本该被Hibernate持久化的类。 (该建议更适用于规模足够大应用软件中,对于那些只有5张表的应用程序并不适合。)
好的真正的many-to-many连接是非常稀少的。多数时候,需要在链接表中存储附加信息。由于这个原因,使用两个one-to-many连接来代替一个中介的链接类是更明智的选择。事实上,更多的连接情况应该是one-to-many和many-to-one的,应该小心使用其它种类的连接,如果设计中确实出现了其它种类的连接情况,仔细审查一下,以确认他们是必须的。
如果你在Session外比较对象,你必须要实现equals()和 hashCode()。在Session内部,Java的对象识别可以值得信赖。如果你实现了这些方法,不要再使用数据库辨识!瞬时对象不具有标识值,Hibernate会在对象被保存的时候赋予它一个值。如果对象在被保存的时候位于Set内,hash code就会变化,要约就被违背。为了实现用与业务有关的键值编写equals()和hashCode(),你应该使用类属性的唯一组合。记住,这个键值只是当对象位于Set内部时才需要保证稳定且唯一,并不是在其整个生命周期中都需要(不需要达到数据库主键这样的稳定性)。绝不要在equals()比较中使用集合(要考虑延迟装载),这些相关联的类可能被代理过。
多对多连接用得好的例子实际上相当少见。大多数时候你在“连接表”中需要保存额外的信息。这种情况下,用两个指向中介类的一对多的连接比较好。实际上,我们认为绝大多数的连接是一对多和多对一的,你应该谨慎使用其它连接风格,用之前问自己一句,是否真的必须这么做。