📄 collections.html
字号:
<span class="emphasis"><em>重要提示</em></span>:如果<tt class="literal">一对多</tt>关联中的<tt class="literal"><key></tt>字段定义成<tt class="literal">NOT NULL</tt>,那么当创建和更新关联关系时Hibernate可能引起约束违例。为了预防这个问题,<span class="emphasis"><em>你必须使用双向关联</em></span>,并且在“多”这一端(Set或者是bag)指明<tt class="literal">inverse="true"</tt>。参阅本章后面关于双向关联的讨论。 </p></div><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="collections-lazy"></a>6.5. 延迟初始化(延迟加载)(Lazy Initialization)</h2></div></div><div></div></div><p><a name="collections-lazy-translate-comment-zh-cn"></a> (译者注: 本翻译稿中,对Lazy Initiazation和Eager fetch中的lazy,eager采取意译的方式,分别翻译为延迟初始化和预先抓取。lazt initiazation就是指直到第一次调用时才加载。) </p><p> 集合(不包括数组)是可以延迟初始化的,意思是仅仅当应用程序需要访问时,才载入他们的值。对于使用者来说,初始化是透明的, 因此应用程序通常不需要关心这个(事实上,透明的延迟加载也就是为什么Hibernate需要自己的集合实现的主要原因)。但是, 如何应用程序试图执行以下程序: </p><pre class="programlisting">s = sessions.openSession();User u = (User) s.find("from User u where u.name=?", userName, Hibernate.STRING).get(0);Map permissions = u.getPermissions();s.connection().commit();s.close();Integer accessLevel = (Integer) permissions.get("accounts"); // Error!</pre><p> 这个错误可能令你感到意外。因为在这个<tt class="literal">Session</tt>被提交(commit)之前, permissions没有被初始化,那么这个集合将永远不能载入他的数据了。 解决方法是把读取集合数据的语句提到Session被提交之前。(然而,还有一种更先进的方法来解决这个问题。) </p><p> 另外一种选择是不使用延迟初始化集合。既然延迟初始化可能引起上面这样错误,默认是不使用延迟初始化的。但是, 为了效率的原因, 我们希望对绝大多数集合(特别是实体集合)使用延迟初始化。 </p><p> 延迟初始化集合时发生的例外被封装在<tt class="literal">LazyInitializationException</tt>中。 </p><p> 使用可选的 <tt class="literal">lazy</tt> 属性来定义延迟初始化集合: </p><pre class="programlisting"><set name="names" table="NAMES" lazy="true"> <key column="group_id"/> <element column="NAME" type="string"/></set></pre><p> 在一些应用程序的体系结构中,特别是使用hibernate访问数据的结构, 代码可能会用在不用的应用层中, 可能没有办法保证当一个集合在初始化的时候, session仍然打开着。 这里有两个基本方法来解决这个问题: </p><div class="itemizedlist"><ul type="disc"><li><p> 在基于Web的应用程序中, 一个servlet过滤器可以用来在用户请求的完成之前来关闭<tt class="literal">Session</tt>。当然,这个地方(关闭session)严重依赖于你的应用程序结构中例外处理的正确性。在请求返回给用户之前关闭<tt class="literal">Session</tt>和结束事务是非常重要的,即使是在构建视图(译者注: 返回给用户的HTML页面)的时候发生了例外,也必须确保这一点。考虑到这一点,servlet过滤器可以保证能够操作这个<tt class="literal">Session</tt>。我们推荐使用一个<tt class="literal">ThreadLocal</tt>变量来保存当前的<tt class="literal">Session</tt>。(参阅第一章,<a href="quickstart.html#quickstart-playingwithcats" title="1.4. 与猫同乐">第 1.4 节 “与猫同乐”</a>,有一个参考实现。) </p></li><li><p> 在一个有单独的商业层的应用程序中, 商业逻辑必须在返回之前“准备好”Web层所需要的所有集合。这就是说商业逻辑层应该装载好所有的数据,把一个用例所需要的所有数据都初始化好,传递给表现/web层。通常, 应用程序为每个Web层需要的集合调用<tt class="literal">Hibernate.initialize()</tt>(必须在Session被关闭之前调用)或者通过使用Hibernate 查询,用<tt class="literal">FETCH</tt>子句来明确获取到整个集合。 </p></li><li><p> You may also attach a previously loaded object to a new <tt class="literal">Session</tt> with <tt class="literal">update()</tt> or <tt class="literal">lock()</tt> before accessing unitialized collections (or other proxies). Hibernate can not do this automatically, as it would introduce ad hoc transaction semantics! 你也可以把先前装载的对象附加到一个新的<tt class="literal">Session</tt>去,需要在访问未初始化的集合类(或其他代理)前使用<tt class="literal">update()</tt> 或 <tt class="literal">lock()</tt>。Hibernate不能自动做这件事,这回带来特别的事务语义! </p></li></ul></div><p> 你可以使用Hibernate Session API中的<tt class="literal">filter()</tt> 方法来在初始化之前得到集合的大小: </p><pre class="programlisting">( (Integer) s.filter( collection, "select count(*)" ).get(0) ).intValue()</pre><p> <tt class="literal">filter()</tt> 或者 <tt class="literal">createFilter()</tt>同样被用于有效的重新载入一个集合的子集而不需要载入整个集合。 </p></div><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="collections-sorted"></a>6.6. 集合排序(Sorted Collections)</h2></div></div><div></div></div><p> Hibernate支持实现<tt class="literal">java.util.SortedMap</tt>和<tt class="literal">java.util.SortedSet</tt>的集合。你必须在映射文件中指定一个比较器: </p><pre class="programlisting"><set name="aliases" table="person_aliases" sort="natural"> <key column="person"/> <element column="name" type="string"/></set><map name="holidays" sort="my.custom.HolidayComparator" lazy="true"> <key column="year_id"/> <index column="hol_name" type="string"/> <element column="hol_date" type="date"/></map></pre><p> <tt class="literal">sort</tt>属性中允许的值包括<tt class="literal">unsorted</tt>,<tt class="literal">natural</tt>和某个实现了<tt class="literal">java.util.Comparator</tt>的类的名称。 </p><p> 分类集合的行为事实上象<tt class="literal">java.util.TreeSet</tt>或者<tt class="literal">java.util.TreeMap</tt>。 </p><p> 如果你希望数据库自己对集合元素排序,可以利用<tt class="literal">set</tt>,<tt class="literal">bag</tt>或者<tt class="literal">map</tt>映射中的<tt class="literal">order-by</tt>属性。这个解决方案只能在jdk1.4或者更高的jdk版本中才可以实现(通过LinkedHashSet或者LinkedHashMap实现)。 它是在SQL查询中完成排序,而不是在内存中。 </p><pre class="programlisting"><set name="aliases" table="person_aliases" order-by="name asc"> <key column="person"/> <element column="name" type="string"/></set><map name="holidays" order-by="hol_date, hol_name" lazy="true"> <key column="year_id"/> <index column="hol_name" type="string"/> <element column="hol_date" type="date"/></map></pre><p> 注意: 这个<tt class="literal">order-by</tt>属性的值是一个SQL排序子句而不是HQL的!
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -