📄 performance.html
字号:
<html><head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>第 14 章 性能提升(Improving performance)</title><link rel="stylesheet" href="../shared/css/html.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.65.1"><link rel="home" href="index.html" title="HIBERNATE - 符合Java习惯的关系数据库持久化"><link rel="up" href="index.html" title="HIBERNATE - 符合Java习惯的关系数据库持久化"><link rel="previous" href="querysql.html" title="第 13 章 原生SQL查询"><link rel="next" href="toolsetguide.html" title="第 15 章 工具箱指南"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">第 14 章 性能提升(Improving performance)</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="querysql.html">上一页</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="toolsetguide.html">下一页</a></td></tr></table><hr></div><div class="chapter" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title"><a name="performance"></a>第 14 章 性能提升(Improving performance)</h2></div></div><div></div></div><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="performance-collections"></a>14.1. 理解集合的性能</h2></div></div><div></div></div><p> 我们已经花了很长时间在讨论集合(collections)了。在本章,我们会特别关注一些关于集合在运行时如何运作的问题。 </p><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="performance-collections-taxonomy"></a>14.1.1. 分类</h3></div></div><div></div></div><p>Hibernate定义了三种基本类型的集合:</p><div class="itemizedlist"><ul type="disc"><li><p>值集合</p></li><li><p>一对多关联</p></li><li><p>多对多关联</p></li></ul></div><p> 这个分类是按照不同的表和外键关系类型来区分的,但是没有告诉我们关于关系模型的一切。要完全理解他们的关系结构和性能特点,我们必须思考用于更新或删除集合行的主键的结构。这得到了如下的分类: </p><div class="itemizedlist"><ul type="disc"><li><p>有序集合类</p></li><li><p>集合(sets)</p></li><li><p>包(bags)</p></li></ul></div><p> 有序集合类(maps, lists, arrays)有一个包含有<tt class="literal"><key></tt>和<tt class="literal"><index></tt>字段的主键。这种情况下集合类更新是特别高效的——主键会有效索引,当Hibernate试图更新或删除一行时,可以迅速找到这一行。 </p><p> 集合(sets)的主键包含有 <tt class="literal"><key></tt> 和元素字段。对于有些元素类型来说,这会变得低效,特别是组合元素或者大文本、大二进制字段;数据库可能无法有效对复杂的主键进行索引。另一方面,对于一对多或多对多关联,特别是合成的标识符来说,它会达到同样的高效。(附注:如果你希望<tt class="literal">SchemaExport</tt>为你的<tt class="literal"><set></tt>创建主键,你必须把所有的字段都声明为<tt class="literal">not-null="true"</tt>。) </p><p> Bag是最差的。因为bag允许重复的元素值,也没有索引字段,不可能定义主键。Hibernate没有办法来判断出重复的行。每当这种集合被更改,Hibernate会完整地移除(通过一个<tt class="literal">DELETE</tt>),再重建整个集合。这会非常低效。 </p><p> 请注意对一对多关联来说,“主键”可能是数据库表的物理主键——但就算在这种情况下,上面的分类仍然是有用的。(它会反映Hibernate是如何在集合的各个行中“定位”的。) </p></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="performance-collections-mostefficientupdate"></a>14.1.2. Lists, maps 和sets用于更新效率最高</h3></div></div><div></div></div><p> 根据我们上面的讨论,显然有序类型和大多数set可以在增加/删除/修改元素的时候得到最好的性能。 </p><p> 但是,在多对多关联,或者对值元素而言,有序集合类比集合(set)有一个好处。因为<tt class="literal">Set</tt>的结构,如果“改变”了一个元素,Hibernate并不会<tt class="literal">UPDATE</tt>这一行。对<tt class="literal">Set</tt>来说,只有<tt class="literal">INSERT</tt>和<tt class="literal">DELETE</tt>才有效。注意这一段描述对一对多关联并不适用。 </p><p> 注意到数组无法延迟转载,我们可以得出结论,list, map和set是最高效的集合类型。(当然,我们警告过了,由于集合中的值的关系,set可能性能下降。) </p><p> Set可以被看作是Hibernate程序中最普遍的集合类型。 </p><p> <span class="emphasis"><em>这个版本的Hibernate有一个没有写在文档中的功能。<tt class="literal"><idbag></tt>可以对值集合和多对多关联实现bag语义,并且性能比上面任何类型都高!</em></span> </p></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="performance-collections-mostefficentinverse"></a>14.1.3. Bag和list是反向集合类中效率最高的</h3></div></div><div></div></div><p> 好了,在你把bag扔到水沟里面再踩上一只脚之前要了解,有一种情况下bag(包括list)要比set性能高得多。对于指明了<tt class="literal">inverse="true"</tt>的集合类(比如说,标准的双向一对多关联),我们可以在不初始化(fetch)包元素的情况下就增加新元素!这是因为<tt class="literal">Collection.add()</tt>或者<tt class="literal">Collection.addAll()</tt>对bag或者<tt class="literal">List</tt>总是返回true的(与<tt class="literal">Set</tt>不同)。对于下面的代码来说,速度会快得多。 </p><pre class="programlisting">Parent p = (Parent) sess.load(Parent.class, id); Child c = new Child(); c.setParent(p); p.getChildren().add(c); //no need to fetch the collection! sess.flush();</pre></div><div class="sect2" lang="zh-cn"><div class="titlepage"><div><div><h3 class="title"><a name="performance-collections-oneshotdelete"></a>14.1.4. 一次性删除(One shot delete)</h3></div></div><div></div></div><p> 有时候,一个一个的删除集合类中的元素是极度低效的。Hibernate没那么笨,如果你想要把整个集合都删除(比如说调用<tt class="literal">list.clear()</tt>),Hibernate只需要一个<tt class="literal">DELETE</tt>就搞定了。 </p><p> 假设我们在一个长度为20的集合类中新增加了一个元素,然后删除了两个。Hibernate会安排一个<tt class="literal">INSERT</tt>语句和两条<tt class="literal">DELETE</tt>语句(除非集合类是一个bag)。这当然是可以想见的。 </p><p> 但是,如果假设我们删除了18个元素,只剩下2个,然后新增3个。有两种处理方式: </p><div class="itemizedlist"><ul type="disc"><li><p>把这18个元素一个一个的干掉,再新增三个</p></li><li><p>把整个集合类都咔嚓掉(只用一句<tt class="literal">DELETE</tt>语句),然后增加5个元素。 </p></li></ul></div><p> Hibernate还没那么聪明,知道第二种选择可能会比较快。(也许让Hibernate不要这么聪明也是好事,否则可能会引发意外的数据库触发器什么的。) </p><p> 幸运的是,你可以强制使用第二种策略。你需要把原来的整个集合类都取消(取消其引用),然后返回一个新实例化的集合类,只包含需要的元素。有些时候这是非常有用的。 </p></div></div><p> 我们已经为您展示了如何在对集合持久化时使用延迟装载(lazy initialization)。对于通常的对象引用,使用CGLIB代理可以达到类似的效果。我们也提到过Hibernate在<tt class="literal">Session</tt>级别缓存持久化对象。还有更多先进的缓存策略,你可以为每一个类单独配置。 </p><p> 这本章里,我们来教你如何使用这些特性,在必要的时候得到高得多的性能。 </p><div class="sect1" lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="performance-proxies"></a>14.2. 用于延迟装载的代理</h2></div></div><div></div></div><p> Hibernate使用动态字节码增强技术来实现持久化对象的延迟装载代理(使用优秀的CGLIB库)。 </p><p> 映射文件为每一个类声明一个类或者接口作为代理接口。建议使用这个类自身: </p><pre class="programlisting"><class name="eg.Order" proxy="eg.Order"></pre><p> 运行时的代理应该是<tt class="literal">Order</tt>的子类。注意被代理的类必须实现一个默认的构造器,并且至少在包内可见。 </p><p> 在扩展这种方法来对应多形的类时,要注意一些细节,比如: </p><pre class="programlisting"><class name="eg.Cat" proxy="eg.Cat"> ...... <subclass name="eg.DomesticCat" proxy="eg.DomesticCat"> .....
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -