﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>语源科技BlogJava-AllBlue</title><link>http://www.blogjava.net/lzn1446/</link><description /><language>zh-cn</language><lastBuildDate>Thu, 18 Jun 2026 23:48:39 GMT</lastBuildDate><pubDate>Thu, 18 Jun 2026 23:48:39 GMT</pubDate><ttl>60</ttl><item><title>【转】hibernate lazy(延迟加载）</title><link>http://www.blogjava.net/lzn1446/articles/354970.html</link><dc:creator>blue_leo</dc:creator><author>blue_leo</author><pubDate>Mon, 25 Jul 2011 03:42:00 GMT</pubDate><guid>http://www.blogjava.net/lzn1446/articles/354970.html</guid><wfw:comment>http://www.blogjava.net/lzn1446/comments/354970.html</wfw:comment><comments>http://www.blogjava.net/lzn1446/articles/354970.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lzn1446/comments/commentRss/354970.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lzn1446/services/trackbacks/354970.html</trackback:ping><description><![CDATA[<div>hibernate lazy(延迟加载）              			<div id="appShareOpt">                                              </div>               	                    <p>hibernate lazy策略可以使用在：<br /> * &lt;class&gt;标签上，可以取值：true/false ，在hibernate3以上版本，默认是true<br /> * &lt;property&gt;标签上，可以取值：true/false需要类增强工具<br /> * &lt;set&gt;&lt;list&gt;标签上，可以取值：true/false/extra<br /> * &lt;one-to-one&gt;&lt;many-to-one&gt;单端关联上，可以取值：false/proxy/no-proxy<br /> <br /> <strong>lazy概念：只有真正使用该对象时，才会创建，对于hibernate而言，正真使用的时候才会发出sql</strong></p> <p>hibernate支持lazy策略只有在session打开状态下有效</p> <p> </p>  <p><strong>=====================================================</strong></p> <p><strong>1. &lt;class&gt;标签上:</strong></p> <p><strong>hbm</strong></p> <p><strong>group.hbm.xml</strong></p> <p>&lt;?xml version="1.0" encoding="utf-8"?&gt;<br /> &lt;!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"<br /> "<a href="http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd</a>"&gt;<br /> &lt;hibernate-mapping package="com.model"&gt;<br /> &nbsp;&nbsp;&nbsp;  &lt;class name="Group" table="group5" lazy="true" &gt; //lazy，默认true，可不写<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;id name="id" column="id" type="java.lang.Integer"&gt;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;generator class="native" /&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;/id&gt;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;property name="name" column="name" length="50" type="java.lang.String" /&gt;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  <br /> &nbsp;&nbsp;&nbsp;  &lt;/class&gt;<br /> <br /> &lt;/hibernate-mapping&gt;</p> <p><strong>测试用例：</strong></p> <p>public class LazyTest extends TestCase {<br /> <br /> public void testLoad1(){<br /> &nbsp;&nbsp;  Session session = null;<br /> &nbsp;&nbsp;  Transaction ta = null;<br /> &nbsp;&nbsp;  try{<br /> &nbsp;&nbsp;&nbsp;  session = HibernateUtil.getSession();<br /> &nbsp;&nbsp;&nbsp;  ta = session.beginTransaction();</p> <p>&nbsp;&nbsp;&nbsp;  Group g2 = (Group) session.load(Group.class, 1); //还没发出sql，lazy起延迟作用,若lazy=false,则发出sql<br /> //&nbsp;&nbsp;&nbsp;  Group g2 = (Group) session.get(Group.class, 1); //不支持lazy<br /> &nbsp;&nbsp;&nbsp;  System.out.println("group.id=" + g2.getId()); //还没发出sql，<br /> &nbsp;&nbsp;&nbsp;  System.out.println("group.name=" + g2.getName()); //发出sql<br /> &nbsp;&nbsp;&nbsp;  ta.commit();<br /> &nbsp;&nbsp;  }catch(Exception e){<br /> &nbsp;&nbsp;&nbsp;  e.printStackTrace();<br /> &nbsp;&nbsp;&nbsp;  if(ta != null){<br /> &nbsp;&nbsp;&nbsp;&nbsp;  ta.rollback();<br /> &nbsp;&nbsp;&nbsp;  }<br /> &nbsp;&nbsp;  }finally{<br /> &nbsp;&nbsp;&nbsp;  //关闭session， user变为detached离线对象<br /> &nbsp;&nbsp;&nbsp;  HibernateUtil.closeSession(session);<br /> &nbsp;&nbsp;  }<br /> &nbsp;&nbsp;  //System.out.println("group.name=" + g2.getName()); //hibernate支持lazy策略只有在session打开状态下有效,所以此出Exception<br /> }<br /> </p> <p>}</p> <p> </p> <p> </p>  <p><strong>&lt;class&gt;标签上的lazy特性只对普通属性起作用</strong></p> <p><strong>&lt;class&gt;标签上的lazy不会影响到单端关联上的lazy特性</strong></p> <p>＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</p> <p><strong>2.&lt;set&gt;&lt;list&gt;标签上</strong>，可以取值：true/false/extra，默认是true</p> <p>hbm.xml</p> <p>Classes.hbm.xml</p> <p>&lt;?xml version="1.0" encoding="utf-8"?&gt;<br /> &lt;!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"<br /> "<a href="http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd</a>"&gt;<br /> &lt;hibernate-mapping package="com.zd.model"&gt;<br /> &nbsp;&nbsp;&nbsp;  &lt;class name="Classes" table="classes" &gt;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;id name="id" column="id" type="java.lang.Integer"&gt;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;generator class="native" /&gt;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;/id&gt;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;property name="name" column="name" type="java.lang.String" /&gt;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;set name="students" lazy="true"&gt; //可不配lazy，因默认是true<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;key column="class_id" /&gt;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;one-to-many class="com.zd.model.Student" /&gt;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;/set&gt;<br /> &nbsp;&nbsp;&nbsp;  &lt;/class&gt;<br /> <br /> &lt;/hibernate-mapping&gt;</p> <p><strong>测试用例：</strong></p> <p>public void testLoad1(){<br /> &nbsp;&nbsp;  Session session = null;<br /> &nbsp;&nbsp;  Transaction ta = null;<br /> &nbsp;&nbsp;  try{<br /> &nbsp;&nbsp;&nbsp;  session = HibernateUtil.getSession();<br /> &nbsp;&nbsp;&nbsp;  ta = session.beginTransaction();<br /> &nbsp;&nbsp;&nbsp;  Classes c = (Classes) session.load(Classes.class, new Integer(2)); //没有sql<br /> &nbsp;&nbsp;&nbsp;  System.out.println("Class.name=" + c.getName());//发出一条sql，但不查 set<br /> &nbsp;&nbsp;&nbsp;  Set stuSet = c.getStudents();//没有发出查询sql，不是统计sql<br /> &nbsp;&nbsp;&nbsp;  //System.out.println(stuSet.size());//发出查询sqlsql<br /> &nbsp;&nbsp;&nbsp;  if(stuSet != null &amp;&amp; !stuSet.isEmpty()){//发出查询sqlsql<br /> &nbsp;&nbsp;&nbsp;&nbsp;  for(Iterator it = stuSet.iterator(); it.hasNext();){<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  Student s = (Student) it.next();//若没有.size(),isEmpty(),就在这边发出sql<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  System.out.println("student.name=" + s.getName());<br /> &nbsp;&nbsp;&nbsp;&nbsp;  }<br /> &nbsp;&nbsp;&nbsp;  }<br /> &nbsp;&nbsp;&nbsp;  ta.commit();<br /> &nbsp;&nbsp;  }catch(Exception e){<br /> &nbsp;&nbsp;&nbsp;  e.printStackTrace();<br /> &nbsp;&nbsp;&nbsp;  if(ta != null){<br /> &nbsp;&nbsp;&nbsp;&nbsp;  ta.rollback();<br /> &nbsp;&nbsp;&nbsp;  }<br /> &nbsp;&nbsp;  }finally{<br /> &nbsp;&nbsp;&nbsp;  //关闭session， user变为detached离线对象<br /> &nbsp;&nbsp;&nbsp;  HibernateUtil.closeSession(session);<br /> &nbsp;&nbsp;  }<br /> &nbsp;&nbsp;<br /> }</p> <p>Hibernate: select classes0_.id as id0_0_, classes0_.name as name0_0_ from classes classes0_ where classes0_.id=?<br /> Class.name=Java Class<br /> Hibernate: select students0_.class_id as  class3_1_, students0_.id as id1_, students0_.id as id1_0_,  students0_.name as name1_0_, students0_.class_id as class3_1_0_ from  student students0_ where students0_.class_id=?<br /> 2<br /> student.name=z3<br /> student.name=l4</p> <p> </p> <p><strong>若&lt;set name="students" lazy="false"&gt; //不延迟加载, 马上加载</strong></p> <p>则在</p> <p>System.out.println("Class.name=" + c.getName());// 就发出2条查询语句了。</p> <p>Hibernate: select classes0_.id as id0_0_, classes0_.name as name0_0_ from classes classes0_ where classes0_.id=?<br /> Hibernate: select students0_.class_id as  class3_1_, students0_.id as id1_, students0_.id as id1_0_,  students0_.name as name1_0_, students0_.class_id as class3_1_0_ from  student students0_ where students0_.class_id=?<br /> Class.name=Java Class<br /> student.name=l4<br /> student.name=z3</p> <p> </p> <p><strong>若&lt;set name="students" lazy="extra"&gt; //和true差不多，只是在写set.size()时，发出selcet count的sql语句，比true好一些。</strong></p> <p><strong>测试用例：</strong></p> <p>public void testLoad1(){<br /> &nbsp;&nbsp;  Session session = null;<br /> &nbsp;&nbsp;  Transaction ta = null;<br /> &nbsp;&nbsp;  try{<br /> &nbsp;&nbsp;&nbsp;  session = HibernateUtil.getSession();<br /> &nbsp;&nbsp;&nbsp;  ta = session.beginTransaction();<br /> &nbsp;&nbsp;&nbsp;  Classes c = (Classes) session.load(Classes.class, new Integer(2));<br /> &nbsp;&nbsp;&nbsp;  System.out.println("Class.name=" + c.getName());<br /> &nbsp;&nbsp;&nbsp;  Set stuSet = c.getStudents();<br /> &nbsp;&nbsp;  System.out.println(stuSet.size());<br /> &nbsp;&nbsp;&nbsp;  if(stuSet != null &amp;&amp; !stuSet.isEmpty()){<br /> &nbsp;&nbsp;&nbsp;&nbsp;  for(Iterator it = stuSet.iterator(); it.hasNext();){<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  Student s = (Student) it.next();<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  System.out.println("student.name=" + s.getName());<br /> &nbsp;&nbsp;&nbsp;&nbsp;  }<br /> &nbsp;&nbsp;&nbsp;  }<br /> &nbsp;&nbsp;&nbsp;  ta.commit();<br /> &nbsp;&nbsp;  }catch(Exception e){<br /> &nbsp;&nbsp;&nbsp;  e.printStackTrace();<br /> &nbsp;&nbsp;&nbsp;  if(ta != null){<br /> &nbsp;&nbsp;&nbsp;&nbsp;  ta.rollback();<br /> &nbsp;&nbsp;&nbsp;  }<br /> &nbsp;&nbsp;  }finally{<br /> &nbsp;&nbsp;&nbsp;  //关闭session， user变为detached离线对象<br /> &nbsp;&nbsp;&nbsp;  HibernateUtil.closeSession(session);<br /> &nbsp;&nbsp;  }<br /> &nbsp;&nbsp;<br /> }</p> <p>Hibernate: select classes0_.id as id0_0_, classes0_.name as name0_0_ from classes classes0_ where classes0_.id=?<br /> Class.name=Java Class<br /> Hibernate: select count(id) from student where class_id =?<br /> 2<br /> Hibernate: select students0_.class_id as class3_1_, students0_.id as  id1_, students0_.id as id1_0_, students0_.name as name1_0_,  students0_.class_id as class3_1_0_ from student students0_ where  students0_.class_id=?<br /> student.name=z3<br /> student.name=l4</p> <p> </p> <p>＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</p> <p><strong>3.&lt;one-to-one&gt;&lt;many-to-one&gt;单端关联</strong>上，可以取值：false/proxy/no-proxy，默认是proxy(代理），延迟加载作用</p> <p><strong>hbm.xml</strong></p> <p><strong>User.hbm.xml 多的一端</strong></p> <p>&lt;?xml version="1.0" encoding="utf-8"?&gt;<br /> &lt;!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"<br /> "<a href="http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd</a>"&gt;<br /> &lt;hibernate-mapping package="com.model"&gt;<br /> &nbsp;&nbsp;&nbsp;  &lt;class name="User" table="user1" &gt;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;id name="id" column="user_id" type="java.lang.Integer"&gt;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;generator class="native" /&gt;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;/id&gt;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;property name="name" length="50" type="java.lang.String" /&gt;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &lt;many-to-one name="group" column="group_id" lazy="proxy"&gt;&lt;/many-to-one&gt; //可不写，默认是proxy<br /> &nbsp;&nbsp;&nbsp;  &lt;/class&gt;<br /> <br /> &lt;/hibernate-mapping&gt;</p> <p>测试用例：</p> <p>public void testGet1(){<br /> &nbsp;&nbsp;  Session session = null;<br /> &nbsp;&nbsp;  Transaction ta = null;<br /> &nbsp;&nbsp;  User user = null;<br /> &nbsp;&nbsp;  try{<br /> &nbsp;&nbsp;&nbsp;  session = HibernateUtil.getSession();<br /> &nbsp;&nbsp;&nbsp;  ta = session.beginTransaction();<br /> &nbsp;&nbsp;&nbsp;  user = (User)session.load(User.class, new Integer(3)); //无sql<br /> &nbsp;&nbsp;&nbsp;  System.out.println("user.name=" + user.getName()); //有一条sql<br /> &nbsp;&nbsp;&nbsp;  Group group = user.getGroup();//无sql<br /> &nbsp;&nbsp;&nbsp;  System.out.println("group.name=" + group.getName());//有一条sql<br /> &nbsp;&nbsp;&nbsp;  ta.commit();<br /> &nbsp;&nbsp;  }catch(Exception e){<br /> &nbsp;&nbsp;&nbsp;  e.printStackTrace();<br /> &nbsp;&nbsp;&nbsp;  ta.rollback();<br /> &nbsp;&nbsp;  }finally{<br /> &nbsp;&nbsp;&nbsp;  //关闭session， user变为detached离线对象<br /> &nbsp;&nbsp;&nbsp;  HibernateUtil.closeSession(session);<br /> &nbsp;&nbsp;  }<br /> &nbsp;&nbsp;<br /> }</p> <p>若&lt;many-to-one name="group" column="group_id"<strong><em> lazy="false</em></strong>"&gt;&lt;/many-to-one&gt;</p> <p>不延迟加载，立即加载，</p> <p>System.out.println("user.name=" + user.getName()); //发出2条sql语句</p> <p> </p> <p>==========================================</p> <p>hibernate 中的 lazy="proxy" 和 <strong>lazy="no-proxy"</strong> 到底是什么意思?</p> <p>举个例子吧：   <br />   Child   &lt;-   many-to-one   -&gt;Parent   <br />     <br />   class   Child   {   <br />       private   Parent   parent;   <br />     <br />       public   Parent   getParent   (){   <br />           return   this.parent;//访问了实例变量   <br />       }   <br />         <br />   }   <br />     <br />   class   Parent   {   <br />       private   String   name;   <br />     <br />       public   String   getName(){   <br />           return   this.name;//访问了实例变量   <br />       }   <br />     <br />       public   void   f(){   <br />           System.out.println("invokeing   f()");//没有访问实例变量   <br />       }   <br />   }   <br />     <br />   如果   <strong>many-to-one     的lazy设为proxy,当child.getParent().getName()或child.getParent().f()时，parent都 会被抓取，若设为no-proxy,调用child.getParent().f()时，parent是不会被抓取的，同时这种方式需要编译时字节码增强，否则和proxy没区别。 (注：测试发现真和proxy一样，不能理解  编译时字节码增强，要再哪修改，或是什么条件？）</strong></p> <p> </p> <p>如 果设置了   lazy="proxy"，就&nbsp;&nbsp;  ，当通过   child   来调用其关联的   parent，   如果调用    parent   类中定义的任何方法,都会抓取   parent   (所谓的抓取是不是就是从数据库查询,执行一次   select   ?)      <br />   如果设置了   lazy="no-proxy"   ,只有调用   parent   类中牵涉到类变量的方法,才会抓取   parent,否则,就像调用   f(),   不会抓取   parent&nbsp;&nbsp;  </p> <p>."编译时字节码增强"   是什么意思?   <br />   "字节码增强"分编译期和运行期2种，编译期是修改java类编译后的class字节码文件，在上面附加&#8220;增强&#8221;操作。（不明白why？）</p> <p> </p> <p>＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</p> lazy  (可选 - 默认为 proxy):  默认情况下，单点关联是经过代理的。lazy="no-proxy"指定此属性应该在实例变量第一次被访问时应该延迟抓取（fetche  lazily）（需要运行时字节码的增强）。 lazy="false"指定此关联总是被预先抓取。注意，如果constrained="false",  不可能使用代理，Hibernate会采取预先抓取！ </div><img src ="http://www.blogjava.net/lzn1446/aggbug/354970.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lzn1446/" target="_blank">blue_leo</a> 2011-07-25 11:42 <a href="http://www.blogjava.net/lzn1446/articles/354970.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转】贫血，充血模型的解释以及一些经验（非常经典）</title><link>http://www.blogjava.net/lzn1446/articles/343367.html</link><dc:creator>blue_leo</dc:creator><author>blue_leo</author><pubDate>Sat, 22 Jan 2011 02:59:00 GMT</pubDate><guid>http://www.blogjava.net/lzn1446/articles/343367.html</guid><wfw:comment>http://www.blogjava.net/lzn1446/comments/343367.html</wfw:comment><comments>http://www.blogjava.net/lzn1446/articles/343367.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lzn1446/comments/commentRss/343367.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lzn1446/services/trackbacks/343367.html</trackback:ping><description><![CDATA[<span class="Apple-style-span" style="border-collapse: separate; color: rgb(0, 0, 0); font-family: 'black verdana'; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: 18px; orphans: 2; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"><span class="Apple-style-span" style="font-family: verdana;">为了补大家的遗憾，在此总结下ROBBIN的领域模型的一些观点和大家的补充，在网站和演讲中，robbin将领域模型初步分为4大类：<br />
1，失血模型<br />
2，贫血模型<br />
3，充血模型<br />
4，胀血模型<br />
那么让我们看看究竟有这些领域模型的具体内容，以及他们的优缺点：<span class="Apple-converted-space">&nbsp;</span><br />
<br />
一、失血模型<span class="Apple-converted-space">&nbsp;</span><br />
<br />
失血模型简单来说，就是domain object只有属性的getter/setter方法的纯数据类，所有的业务逻辑完全由business<span class="Apple-converted-space">&nbsp;</span>object来完成(又称<br />
<br />
TransactionScript)，这种模型下的domain<span class="Apple-converted-space">&nbsp;</span>object被Martin<span class="Apple-converted-space">&nbsp;</span>Fowler称之为&#8220;贫血的domain<span class="Apple-converted-space">&nbsp;</span>object&#8221;。下面用举一个具体的代码来说明，代码<br />
<br />
来自Hibernate的caveatemptor，但经过我的改写：<span class="Apple-converted-space">&nbsp;</span><br />
<br />
一个实体类叫做Item，指的是一个拍卖项目<span class="Apple-converted-space">&nbsp;</span><br />
一个DAO接口类叫做ItemDao<span class="Apple-converted-space">&nbsp;</span><br />
一个DAO接口实现类叫做ItemDaoHibernateImpl<span class="Apple-converted-space">&nbsp;</span><br />
一个业务逻辑类叫做ItemManager(或者叫做ItemService)<span class="Apple-converted-space">&nbsp;</span><br />
<br />
java代码:&nbsp;&nbsp;<br />
<br />
public class Item implements Serializable {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; private Long id = null;<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; private int version;<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; private String name;<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; private User seller;<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; private String description;<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; private MonetaryAmount initialPrice;<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; private MonetaryAmount reservePrice;<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; private Date startDate;<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; private Date endDate;<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; private Set categorizedItems = new HashSet();<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; private Collection bids = new ArrayList();<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; private Bid successfulBid;<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; private ItemState state;<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; private User approvedBy;<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; private Date approvalDatetime;<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; private Date created = new Date();<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; getter/setter方法省略不写，避免篇幅太长<span class="Apple-converted-space">&nbsp;</span><br />
}<br />
<br />
<br />
<br />
java代码:&nbsp;&nbsp;<br />
<br />
public interface ItemDao {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public Item getItemById(Long id);<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public Collection findAll();<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public void updateItem(Item item);<span class="Apple-converted-space">&nbsp;</span><br />
}<br />
<br />
<br />
<br />
ItemDao定义持久化操作的接口，用于隔离持久化代码。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
java代码:&nbsp;&nbsp;<br />
<br />
public class ItemDaoHibernateImpl implements ItemDao extends HibernateDaoSupport {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public Item getItemById(Long id) {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (Item) getHibernateTemplate().load(Item.class, id);<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; }<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public Collection findAll() {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (List) getHibernateTemplate().find("from Item");<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; }<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public void updateItem(Item item) {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getHibernateTemplate().update(item);<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; }<span class="Apple-converted-space">&nbsp;</span><br />
}<br />
<br />
<br />
ItemDaoHibernateImpl完成具体的持久化工作，请注意，数据库资源的获取和释放是在ItemDaoHibernateImpl里面处理的，每个DAO方法调用之<br />
<br />
前打开Session，DAO方法调用之后，关闭Session。(Session放在ThreadLocal中，保证一次调用只打开关闭一次)<span class="Apple-converted-space">&nbsp;</span><br />
<br />
java代码:&nbsp;&nbsp;<br />
<br />
public class ItemManager {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; private ItemDao itemDao;<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public void setItemDao(ItemDao itemDao) { this.itemDao = itemDao;}<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public Bid loadItemById(Long id) {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; itemDao.loadItemById(id);<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; }<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public Collection listAllItems() {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;&nbsp; itemDao.findAll();<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; }<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public Bid placeBid(Item item, User bidder, MonetaryAmount bidAmount,<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Bid currentMaxBid, Bid currentMinBid) throws BusinessException {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (currentMaxBid != null &amp;&amp; currentMaxBid.getAmount().compareTo(bidAmount) &gt; 0) {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new BusinessException("Bid too low.");<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; }<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; // Auction is active<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; if ( !state.equals(ItemState.ACTIVE) )<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new BusinessException("Auction is not active yet.");<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; // Auction still valid<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; if ( item.getEndDate().before( new Date() ) )<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new BusinessException("Can't place new bid, auction already ended.");<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; // Create new Bid<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; Bid newBid = new Bid(bidAmount, item, bidder);<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; // Place bid for this Item<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; item.getBids().add(newBid);<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; itemDao.update(item);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; 调用DAO完成持久化操作<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; return newBid;<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; }<span class="Apple-converted-space">&nbsp;</span><br />
}<br />
<br />
<br />
<br />
事务的管理是在ItemManger这一层完成的，ItemManager实现具体的业务逻辑。除了常见的和CRUD有关的简单逻辑之外，这里还有一个placeBid<br />
<br />
的逻辑，即项目的竞标。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
以上是一个完整的第一种模型的示例代码。在这个示例中，placeBid，loadItemById，findAll等等业务逻辑统统放在ItemManager中实现，而<br />
<br />
Item只有getter/setter方法。<br />
<br />
二、贫血模型<span class="Apple-converted-space">&nbsp;</span><br />
<br />
简单来说，就是domain ojbect包含了不依赖于持久化的领域逻辑，而那些依赖持久化的领域逻辑被分离到Service层。<span class="Apple-converted-space">&nbsp;</span><br />
Service(业务逻辑，事务封装) --&gt; DAO ---&gt; domain object<span class="Apple-converted-space">&nbsp;</span><br />
这也就是Martin Fowler指的rich domain object<span class="Apple-converted-space">&nbsp;</span><br />
<br />
一个带有业务逻辑的实体类，即domain object是Item<span class="Apple-converted-space">&nbsp;</span><br />
一个DAO接口ItemDao<span class="Apple-converted-space">&nbsp;</span><br />
一个DAO实现ItemDaoHibernateImpl<span class="Apple-converted-space">&nbsp;</span><br />
一个业务逻辑对象ItemManager<span class="Apple-converted-space">&nbsp;</span><br />
<br />
java代码:&nbsp;&nbsp;<br />
<br />
public class Item implements Serializable {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; 所有的属性和getter/setter方法同上，省略<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public Bid placeBid(User bidder, MonetaryAmount bidAmount,<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Bid currentMaxBid, Bid currentMinBid)<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throws BusinessException {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Check highest bid (can also be a different Strategy (pattern))<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (currentMaxBid != null &amp;&amp; currentMaxBid.getAmount().compareTo(bidAmount) &gt; 0) {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new BusinessException("Bid too low.");<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Auction is active<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( !state.equals(ItemState.ACTIVE) )<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new BusinessException("Auction is not active yet.");<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Auction still valid<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( this.getEndDate().before( new Date() ) )<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new BusinessException("Can't place new bid, auction already ended.");<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Create new Bid<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Bid newBid = new Bid(bidAmount, this, bidder);<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Place bid for this Item<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.getBids.add(newBid);&nbsp;&nbsp; // 请注意这一句，透明的进行了持久化，但是不能在这里调用ItemDao，Item不能对ItemDao产生<br />
<br />
依赖！<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return newBid;<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; }<span class="Apple-converted-space">&nbsp;</span><br />
}<br />
<br />
<br />
<br />
竞标这个业务逻辑被放入到Item中来。请注意this.getBids.add(newBid); 如果没有Hibernate或者JDO这种O/R Mapping的支持，我们是无法实<br />
<br />
现这种透明的持久化行为的。但是请注意，Item里面不能去调用ItemDAO，对ItemDAO产生依赖！<span class="Apple-converted-space">&nbsp;</span><br />
<br />
ItemDao和ItemDaoHibernateImpl的代码同上，省略。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
java代码:&nbsp;&nbsp;<br />
<br />
public class ItemManager {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; private ItemDao itemDao;<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public void setItemDao(ItemDao itemDao) { this.itemDao = itemDao;}<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public Bid loadItemById(Long id) {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; itemDao.loadItemById(id);<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; }<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public Collection listAllItems() {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;&nbsp; itemDao.findAll();<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; }<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public Bid placeBid(Item item, User bidder, MonetaryAmount bidAmount,<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Bid currentMaxBid, Bid currentMinBid) throws BusinessException {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; item.placeBid(bidder, bidAmount, currentMaxBid, currentMinBid);<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; itemDao.update(item);&nbsp;&nbsp;&nbsp;&nbsp; // 必须显式的调用DAO，保持持久化<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; }<span class="Apple-converted-space">&nbsp;</span><br />
}<br />
<br />
<br />
<br />
在第二种模型中，placeBid业务逻辑是放在Item中实现的，而loadItemById和findAll业务逻辑是放在ItemManager中实现的。不过值得注意的<br />
<br />
是，即使placeBid业务逻辑放在Item中，你仍然需要在ItemManager中简单的封装一层，以保证对placeBid业务逻辑进行事务的管理和持久化的<br />
<br />
触发。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
这种模型是Martin Fowler所指的真正的domain model。在这种模型中，有三个业务逻辑方法：placeBid，loadItemById和findAll，现在的问<br />
<br />
题是哪个逻辑应该放在Item中，哪个逻辑应该放在ItemManager中。在我们这个例子中，placeBid放在Item中(但是ItemManager也需要对它进行<br />
<br />
简单的封装)，loadItemById和findAll是放在ItemManager中的。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
切分的原则是什么呢？ Rod Johnson提出原则是&#8220;case by case&#8221;，可重用度高的，和domain object状态密切关联的放在Item中，可重用度低<br />
<br />
的，和domain object状态没有密切关联的放在ItemManager中。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
经过上面的讨论，如何区分domain logic和business logic，我想提出一个改进的区分原则：<span class="Apple-converted-space">&nbsp;</span><br />
<br />
domain logic只应该和这一个domain object的实例状态有关，而不应该和一批domain object的状态有关；<span class="Apple-converted-space">&nbsp;</span><br />
<br />
当你把一个logic放到domain object中以后，这个domain object应该仍然独立于持久层框架之外(Hibernate,JDO)，这个domain object仍然可<br />
<br />
以脱离持久层框架进行单元测试，这个domain object仍然是一个完备的，自包含的，不依赖于外部环境的领域对象，这种情况下，这个logic<br />
<br />
才是domain logic。<span class="Apple-converted-space">&nbsp;</span><br />
这里有一个很确定的原则：logic是否只和这个object的状态有关，如果只和这个object有关，就是domain logic；如果logic是和一批domain<span class="Apple-converted-space">&nbsp;</span><br />
<br />
object的状态有关，就不是domain logic，而是business logic。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
<br />
Item的placeBid这个业务逻辑方法没有显式的对持久化ItemDao接口产生依赖，所以要放在Item中。请注意，如果脱离了Hibernate这个持久化<br />
<br />
框架，Item这个domain object是可以进行单元测试的，他不依赖于Hibernate的持久化机制。它是一个独立的，可移植的，完整的，自包含的<br />
<br />
域对象。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
而loadItemById和findAll这两个业务逻辑方法是必须显式的对持久化ItemDao接口产生依赖，否则这个业务逻辑就无法完成。如果你要把这两<br />
<br />
个方法放在Item中，那么Item就无法脱离Hibernate框架，无法在Hibernate框架之外独立存在。<br />
<br />
这种模型的优点：<span class="Apple-converted-space">&nbsp;</span><br />
1、各层单向依赖，结构清楚，易于实现和维护<span class="Apple-converted-space">&nbsp;</span><br />
2、设计简单易行，底层模型非常稳定<span class="Apple-converted-space">&nbsp;</span><br />
这种模型的缺点：<span class="Apple-converted-space">&nbsp;</span><br />
1、domain object的部分比较紧密依赖的持久化domain<span class="Apple-converted-space">&nbsp;</span>logic被分离到Service层，显得不够OO<span class="Apple-converted-space">&nbsp;</span><br />
2、Service层过于厚重<span class="Apple-converted-space">&nbsp;</span><br />
<br />
三、充血模型<span class="Apple-converted-space">&nbsp;</span><br />
充血模型和第二种模型差不多，所不同的就是如何划分业务逻辑，即认为，绝大多业务逻辑都应该被放在domain object里面(包括持久化逻辑)<br />
<br />
，而Service层应该是很薄的一层，仅仅封装事务和少量逻辑，不和DAO层打交道。<span class="Apple-converted-space">&nbsp;</span><br />
Service(事务封装) ---&gt; domain object &lt;---&gt; DAO<span class="Apple-converted-space">&nbsp;</span><br />
这种模型就是把第二种模型的domain object和business<span class="Apple-converted-space">&nbsp;</span>object合二为一了。所以ItemManager就不需要了，在这种模型下面，只有三个类，他<br />
<br />
们分别是：<span class="Apple-converted-space">&nbsp;</span><br />
<br />
Item：包含了实体类信息，也包含了所有的业务逻辑<span class="Apple-converted-space">&nbsp;</span><br />
ItemDao：持久化DAO接口类<span class="Apple-converted-space">&nbsp;</span><br />
ItemDaoHibernateImpl：DAO接口的实现类<span class="Apple-converted-space">&nbsp;</span><br />
<br />
由于ItemDao和ItemDaoHibernateImpl和上面完全相同，就省略了。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
java代码:&nbsp;&nbsp;<br />
<br />
public class Item implements Serializable {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; 所有的属性和getter/setter方法都省略<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp; private static ItemDao itemDao;<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public void setItemDao(ItemDao itemDao) {this.itemDao = itemDao;}<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp; public static Item loadItemById(Long id) {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (Item) itemDao.loadItemById(id);<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; }<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public static Collection findAll() {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (List) itemDao.findAll();<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; }<span class="Apple-converted-space">&nbsp;</span><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; public Bid placeBid(User bidder, MonetaryAmount bidAmount,<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Bid currentMaxBid, Bid currentMinBid)<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; throws BusinessException {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Check highest bid (can also be a different Strategy (pattern))<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (currentMaxBid != null &amp;&amp; currentMaxBid.getAmount().compareTo(bidAmount) &gt; 0) {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new BusinessException("Bid too low.");<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Auction is active<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( !state.equals(ItemState.ACTIVE) )<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new BusinessException("Auction is not active yet.");<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Auction still valid<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ( this.getEndDate().before( new Date() ) )<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new BusinessException("Can't place new bid, auction already ended.");<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Create new Bid<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Bid newBid = new Bid(bidAmount, this, bidder);<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Place bid for this Item<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.addBid(newBid);<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; itemDao.update(this);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; 调用DAO进行显式持久化<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return newBid;<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; }<span class="Apple-converted-space">&nbsp;</span><br />
}<br />
<br />
<br />
<br />
在这种模型中，所有的业务逻辑全部都在Item中，事务管理也在Item中实现。<br />
这种模型的优点：<span class="Apple-converted-space">&nbsp;</span><br />
1、更加符合OO的原则<span class="Apple-converted-space">&nbsp;</span><br />
2、Service层很薄，只充当Facade的角色，不和DAO打交道。<span class="Apple-converted-space">&nbsp;</span><br />
这种模型的缺点：<span class="Apple-converted-space">&nbsp;</span><br />
1、DAO和domain object形成了双向依赖，复杂的双向依赖会导致很多潜在的问题。<span class="Apple-converted-space">&nbsp;</span><br />
2、如何划分Service层逻辑和domain层逻辑是非常含混的，在实际项目中，由于设计和开发人员的水平差异，可能导致整个结构的混乱无序。<span class="Apple-converted-space">&nbsp;</span><br />
3、考虑到Service层的事务封装特性，Service层必须对所有的domain object的逻辑提供相应的事务封装方法，其结果就是Service完全重定义<br />
<br />
一遍所有的domain logic，非常烦琐，而且Service的事务化封装其意义就等于把OO的domain logic转换为过程的Service<span class="Apple-converted-space">&nbsp;</span>TransactionScript<br />
<br />
。该充血模型辛辛苦苦在domain层实现的OO在Service层又变成了过程式，对于Web层程序员的角度来看，和贫血模型没有什么区别了。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
1.事务我是不希望由Item管理的，而是由容器或更高一层的业务类来管理。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
2.如果Item不脱离持久层的管理，如JDO的pm，那么itemDao.update(this); 是不需要的，也就是说Item是在事务过程中从数据库拿出来的，并<br />
<br />
且声明周期不超出当前事务的范围。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
3.如果Item是脱离持久层，也就是在Item的生命周期超出了事务的范围，那就要必须显示调用update或attach之类的持久化方法的，这种时候<br />
<br />
就应该是按robbin所说的第2种模型来做。<br />
<br />
四、胀血模型<span class="Apple-converted-space">&nbsp;</span><br />
基于充血模型的第三个缺点，有同学提出，干脆取消Service层，只剩下domain object和DAO两层，在domain object的domain logic上面封装<br />
<br />
事务。<span class="Apple-converted-space">&nbsp;</span><br />
domain object(事务封装，业务逻辑) &lt;---&gt; DAO<span class="Apple-converted-space">&nbsp;</span><br />
似乎ruby on rails就是这种模型，他甚至把domain object和DAO都合并了。<span class="Apple-converted-space">&nbsp;</span><br />
该模型优点：<span class="Apple-converted-space">&nbsp;</span><br />
1、简化了分层<span class="Apple-converted-space">&nbsp;</span><br />
2、也算符合OO<span class="Apple-converted-space">&nbsp;</span><br />
该模型缺点：<span class="Apple-converted-space">&nbsp;</span><br />
1、很多不是domain logic的service逻辑也被强行放入domain object ，引起了domain ojbect模型的不稳定<span class="Apple-converted-space">&nbsp;</span><br />
2、domain object暴露给web层过多的信息，可能引起意想不到的副作用。<br />
<br />
<br />
<br />
<br />
<br />
评价：<br />
<br />
<br />
<br />
在这四种模型当中，失血模型和胀血模型应该是不被提倡的。而贫血模型和充血模型从技术上来说，都已经是可行的了。但是我个人仍然主张<br />
<br />
使用贫血模型。其理由：<span class="Apple-converted-space">&nbsp;</span><br />
<br />
1、参考充血模型第三个缺点，由于暴露给web层程序拿到的还是Service Transaction Script，对于web层程序员来说，底层OO意义丧失了。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
2、参考充血模型第三个缺点，为了事务封装，Service层要给每个domain logic提供一个过程化封装，这对于编程来说，做了多余的工作，非<br />
<br />
常烦琐。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
3、domain object和DAO的双向依赖在做大项目中，考虑到团队成员的水平差异，很容易引入不可预知的潜在bug。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
4、如何划分domain logic和service logic的标准是不确定的，往往要根据个人经验，有些人就是觉得某个业务他更加贴近domain，也有人认<br />
<br />
为这个业务是贴近service的。由于划分标准的不确定性，带来的后果就是实际项目中会产生很多这样的争议和纠纷，不同的人会有不同的划分<br />
<br />
方法，最后就会造成整个项目的逻辑分层混乱。这不像贫血模型中我提出的按照是否依赖持久化进行划分，这种标准是非常确定的，不会引起<br />
<br />
争议，因此团队开发中，不会产生此类问题。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
5、贫血模型的domain object确实不够rich，但是我们是做项目，不是做研究，好用就行了，管它是不是那么纯的OO呢？其实我不同意<br />
<br />
firebody认为的贫血模型在设计模型和实现代码中有很大跨越的说法。一个设计模型到实现的时候，你直接得到两个类：一个实体类，一个控<br />
<br />
制类就行了，没有什么跨越。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
简单评价一下：<span class="Apple-converted-space">&nbsp;</span><br />
<br />
第一种模型绝大多数人都反对，因此反对理由我也不多讲了。但遗憾的是，我观察到的实际情形是，很多使用Hibernate的公司最后都是这种模<br />
<br />
型，这里面有很大的原因是很多公司的技术水平没有达到这种层次，所以导致了这种贫血模型的出现。从这一点来说，Martin Fowler的批评声<br />
<br />
音不是太响了，而是太弱了，还需要再继续呐喊。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
第二种模型就是Martin Fowler一直主张的模型，实际上也是我一直在实际项目中采用这种模型。我没有看过Martin的POEAA，之所以能够自己<br />
<br />
摸索到这种模型，也是因为从02年我已经开始思考这个问题并且寻求解决方案了，但是当时没有看到Hibernate，那时候做的一个小型项目我已<br />
<br />
经按照这种模型来做了，但是由于没有O/R Mapping的支持，写到后来又不得不全部改成贫血的domain object，项目做完以后再继续找，随后<br />
<br />
就发现了Hibernate。当然，现在很多人一开始就是用Hibernate做项目，没有经历过我经历的那个阶段。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
不过我觉得这种模型仍然不够完美，因为你还是需要一个业务逻辑层来封装所有的domain logic，这显得非常罗嗦，并且业务逻辑对象的接口<br />
<br />
也不够稳定。如果不考虑业务逻辑对象的重用性的话(业务逻辑对象的可重用性也不可能好)，很多人干脆就去掉了xxxManager这一层，在Web层<br />
<br />
的Action代码直接调用xxxDao，同时容器事务管理配置到Action这一层上来。Hibernate的caveatemptor就是这样架构的一个典型应用。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
第三种模型是我很反对的一种模型，这种模型下面，Domain Object和DAO形成了双向依赖关系，无法脱离框架测试，并且业务逻辑层的服务也<br />
<br />
和持久层对象的状态耦合到了一起，会造成程序的高度的复杂性，很差的灵活性和糟糕的可维护性。也许将来技术进步导致的O/R Mapping管理<br />
<br />
下的domain object发展到足够的动态持久透明化的话，这种模型才会成为一个理想的选择。就像O/R Mapping的流行使得第二种模型成为了可<br />
<br />
能<br />
<br />
Martin Fowler的Domain Model，或者说我们的第二种模型难道是完美无缺的吗？当然不是，接下来我就要分析一下它的不足，以及可能的解决<br />
<br />
办法，而这些都来源于我个人的实践探索。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
在第二种模型中，我们可以清楚的把这4个类分为三层：<span class="Apple-converted-space">&nbsp;</span><br />
<br />
1、实体类层，即Item，带有domain logic的domain object<span class="Apple-converted-space">&nbsp;</span><br />
2、DAO层，即ItemDao和ItemDaoHibernateImpl，抽象持久化操作的接口和实现类<span class="Apple-converted-space">&nbsp;</span><br />
3、业务逻辑层，即ItemManager，接受容器事务控制，向Web层提供统一的服务调用<span class="Apple-converted-space">&nbsp;</span><br />
<br />
在这三层中我们大家可以看到，domain object和DAO都是非常稳定的层，其实原因也很简单，因为domain object是映射数据库字段的，数据库<br />
<br />
字段不会频繁变动，所以domain object也相对稳定，而面向数据库持久化编程的DAO层也不过就是CRUD而已，不会有更多的花样，所以也很稳<br />
<br />
定。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
问题就在于这个充当business workflow facade的业务逻辑对象，它的变动是相当频繁的。业务逻辑对象通常都是无状态的、受事务控制的、<br />
<br />
Singleton类，我们可以考察一下业务逻辑对象都有哪几类业务逻辑方法：<span class="Apple-converted-space">&nbsp;</span><br />
<br />
第一类：DAO接口方法的代理，就是上面例子中的loadItemById方法和findAll方法。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
ItemManager之所以要代理这种类，目的有两个：向Web层提供统一的服务调用入口点和给持久化方法增加事务控制功能。这两点都很容易理解<br />
<br />
，你不能既给Web层程序员提供xxxManager，也给他提供xxxDao，所以你需要用xxxManager封装xxxDao，在这里，充当了一个简单代理功能；而<br />
<br />
事务控制也是持久化方法必须的，事务可能需要跨越多个DAO方法调用，所以必须放在业务逻辑层，而不能放在DAO层。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
但是必须看到，对于一个典型的web应用来说，绝大多数的业务逻辑都是简单的CRUD逻辑，所以这种情况下，针对每个DAO方法，xxxManager都<br />
<br />
需要提供一个对应的封装方法，这不但是非常枯燥的，也是令人感觉非常不好的。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
<br />
第二类：domain logic的方法代理。就是上面例子中placeBid方法。虽然Item已经有了placeBid方法，但是ItemManager仍然需要封装一下Item<br />
<br />
的placeBid，然后再提供一个简单封装之后的代理方法。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
这和第一种情况类似，其原因也一样，也是为了给Web层提供一个统一的服务调用入口点和给隐式的持久化动作提供事务控制。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
同样，和第一种情况一样，针对每个domain logic方法，xxxManager都需要提供一个对应的封装方法，同样是枯燥的，令人不爽的。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
<br />
第三类：需要多个domain object和DAO参与协作的business workflow。这种情况是业务逻辑对象真正应该完成的职责。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
在这个简单的例子中，没有涉及到这种情况，不过大家都可以想像的出来这种应用场景，因此不必举例说明了。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
通过上面的分析可以看出，只有第三类业务逻辑方法才是业务逻辑对象真正应该承担的职责，而前两类业务逻辑方法都是&#8220;无奈之举&#8221;，不得<br />
<br />
不为之的事情，不但枯燥，而且令人沮丧。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
<br />
<br />
<br />
分析完了业务逻辑对象，我们再回头看一下domain object，我们要仔细考察一下domain logic的话，会发现domain logic也分为两类：<span class="Apple-converted-space">&nbsp;</span><br />
<br />
第一类：需要持久层框架隐式的实现透明持久化的domain logic，例如Item的placeBid方法中的这一句：<span class="Apple-converted-space">&nbsp;</span><br />
java代码:&nbsp;&nbsp;<br />
<br />
this.getBids().add(newBid);<br />
<br />
<br />
上面已经着重提到，虽然这仅仅只是一个Java集合的添加新元素的操作，但是实际上通过事务的控制，会潜在的触发两条SQL：一条是insert一<br />
<br />
条记录到bid表，一条是更新item表相应的记录。如果我们让Item脱离Hibernate进行单元测试，它就是一个单纯的Java集合操作，如果我们把<br />
<br />
他加入到Hibernate框架中，他就会潜在的触发两条SQL，这就是隐式的依赖于持久化的domain logic。<span class="Apple-converted-space">&nbsp;</span><br />
特别请注意的一点是：在没有Hibernate/JDO这类可以实现&#8220;透明的持久化&#8221;工具出现之前，这类domain logic是无法实现的。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
对于这一类domain logic，业务逻辑对象必须提供相应的封装方法，以实现事务控制。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
<br />
第二类：完全不依赖持久化的domain logic，例如readonly例子中的Topic，如下：<span class="Apple-converted-space">&nbsp;</span><br />
<br />
java代码:&nbsp;&nbsp;<br />
<br />
class Topic {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; boolean isAllowReply() {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Calendar dueDate = Calendar.getInstance();<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dueDate.setTime(lastUpdatedTime);<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dueDate.add(Calendar.DATE, forum.timeToLive);<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Date now = new Date();<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return now.after(dueDate.getTime());<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; }<span class="Apple-converted-space">&nbsp;</span><br />
}<br />
<br />
<br />
<br />
注意这个isAllowReply方法，他和持久化完全不发生一丁点关系。在实际的开发中，我们同样会遇到很多这种不需要持久化的业务逻辑(主要发<br />
<br />
生在日期运算、数值运算和枚举运算方面)，这种domain logic不管脱离不脱离所在的框架，它的行为都是一致的。对于这种domain logic，业<br />
<br />
务逻辑层并不需要提供封装方法，它可以适用于任何场合。<br />
概括说：action做为控制器 ，service面向use case，domain object是中间稳定的一层，dao是作为下层的服务，提供持久化服务，可以被<br />
<br />
domain object所使用。<span class="Apple-converted-space">&nbsp;</span><br />
针对上面帖子中分析的业务逻辑对象的方法有三类的情况，我们在实际的项目中会遇到一些困扰。主要的困扰就是业务逻辑对象的方法会变动<br />
<br />
的相当频繁，并且业务逻辑对象的方法数量会非常庞大。针对这个问题，我所知道的有两种解决方案，我姑且称之为第二种模型的两类变种：<span class="Apple-converted-space">&nbsp;</span><br />
<br />
第一类变种就是partech的那种模型，简单的来说，就是把业务逻辑对象层和DAO层合二为一；第二类变种就是干脆取消业务逻辑层，把事务控<br />
<br />
制前推至Web层的Action层来处理，下面分别分析一下两类变种的优缺点:<br />
<br />
第一类变种是合并业务逻辑对象和DAO层，这种设计代码简化为3个类，如下所示：<span class="Apple-converted-space">&nbsp;</span><br />
<br />
一个domain object：Item(同第二种模型的代码，省略)<span class="Apple-converted-space">&nbsp;</span><br />
一个业务层接口：ItemManager(合并原来的ItemManager方法签名和ItemDao接口而来)<span class="Apple-converted-space">&nbsp;</span><br />
一个业务层实现类：ItemManagerHibernateImpl(合并原来的ItemManager方法实现和ItemDaoHibernateImpl)<span class="Apple-converted-space">&nbsp;</span><br />
<br />
java代码:&nbsp;&nbsp;<br />
<br />
public interface ItemManager {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public Item loadItemById(Long id);<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public Collection findAll();<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public void updateItem(Item item);<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public Bid placeBid(Item item, User bidder, MonetaryAmount bidAmount, Bid currentMaxBid, Bid currentMinBid) throws<span class="Apple-converted-space">&nbsp;</span><br />
<br />
BusinessException;<span class="Apple-converted-space">&nbsp;</span><br />
}<br />
<br />
<br />
<br />
java代码:&nbsp;&nbsp;<br />
<br />
public class ItemManagerHibernateImpl implements ItemManager extends HibernateDaoSupport {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public Item loadItemById(Long id) {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (Item) getHibernateTemplate().load(Item.class, id);<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; }<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; Collection findAll() {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (List) getHibernateTemplate().find("from Item");<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; }<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public void updateItem(Item item) {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getHibernateTemplate().update(item);<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; }<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; public Bid placeBid(Item item, User bidder, MonetaryAmount bidAmount, Bid currentMaxBid, Bid currentMinBid) throws<span class="Apple-converted-space">&nbsp;</span><br />
<br />
BusinessException {<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; item.placeBid(bidder, bidAmount, currentMaxBid, currentMinBid);<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; updateItem(item);&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp; 确保持久化item<span class="Apple-converted-space">&nbsp;</span><br />
&nbsp;&nbsp;&nbsp;&nbsp; }<span class="Apple-converted-space">&nbsp;</span><br />
}<br />
<br />
<br />
<br />
第二种模型的第一类变种把业务逻辑对象和DAO层合并到了一起。<span class="Apple-converted-space">&nbsp;</span><br />
考虑到典型的web应用中，简单的CRUD操作占据了业务逻辑的绝大多数比例，因此第一类变种的优点是：避免了业务逻辑不得不大量封装DAO接<br />
<br />
口的问题，简化了软件架构设计，节省了大量的业务层代码量。<span class="Apple-converted-space">&nbsp;</span><br />
这种方案的缺点是：把DAO接口方法和业务逻辑方法混合到了一起，显得职责不够单一化，软件分层结构不够清晰；此外这种方案仍然不得不对<br />
<br />
隐式依赖持久化的domain logic提供封装方法，未能做到彻底的简化。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
总体而言，个人认为这种变种各方面权衡下来，是目前相对最为合理方案，这也是我目前项目中采用的架构<br />
第二种模型的第二类变种就是干脆取消ItemManager，保留原来的Item，ItemDao，ItemDaoHibernateImpl这3个类。在这种情况下把事务控制前<br />
<br />
推至Web层的Action去控制，具体来说，就是直接对Action的execute()方法进行容器事务声明。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
这种方式的优点是：极大的简化了业务逻辑层，避免了业务逻辑对象不得不大量封装DAO接口方法和大量封装domain logic的问题。对于业务逻<br />
<br />
辑非常简单的项目，采用这种方案是一个非常合适的选择。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
这种方式的缺点主要有3个：<span class="Apple-converted-space">&nbsp;</span><br />
<br />
1) 由于彻底取消了业务逻辑对象层，对于那些有重用需要的、多个domain object和多个DAO参与的、复杂业务逻辑流程来说，你不得不在<br />
<br />
Action中一遍又一遍的重复实现这部分代码，效率既低，也不利于软件重用。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
2) Web层程序员需要对持久层机制有相当高程度的了解和掌握，必须知道什么时候应该调用什么DAO方法进行必要的持久化。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
3) 事务的范围被扩大了。假设你在一个Action中，首先需要插入一条记录，然后再需要查询数据库，显示一个记录列表，对于这种情况，事务<br />
<br />
的作用范围应该是在插入记录的前后，但是现在扩大到了整个execute执行期间。如果插入动作完毕，查询动作过程中出现通往数据库服务器的<br />
<br />
网络异常，那么前面的插入动作将回滚，但是实际上我们期望的是插入应该被提交。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
总体而言，这种变种的缺陷比较大，只适合在业务逻辑非常简单的小型项目中，值得一提的是Hibernate的caveatemptor就是采用这种变种的架<br />
<br />
构，大家可以参考一下。<span class="Apple-converted-space">&nbsp;</span><br />
<br />
综上所述，在采用Rich Domain Object模型的三种解决方案中(第二模型，第二模型第一变种，第二模型第二变种)，我认为权衡下来，第二模<br />
<br />
型的第一变种是相对最好的解决方案，不过它仍然有一定的不足，在这里我也希望大家能够提出更好的解决方案。<br />
，partech 提出了 实体控制对象 和 实体对象 两种不同层次的 Domain Object ，由于 Domain Object 可以依赖于 XXXFinderDAO，因此，也<br />
<br />
就不存在&#8220;大数据量问题&#8221;，因此，整个 Domain 体系，对于实际业务表述的更为完整，更为一体化。我非常倾向这种方式。<br />
一般是这样的顺序：<span class="Apple-converted-space">&nbsp;</span><br />
Client--&gt;Service--&gt;D Object--&gt;DAO--&gt;DB<span class="Apple-converted-space">&nbsp;</span><br />
至于哪些该放在哪里，基本有这样的原则：（就是robbin的第二种了）<span class="Apple-converted-space">&nbsp;</span><br />
DO封装内在的业务逻辑<span class="Apple-converted-space">&nbsp;</span><br />
Service 封装外在于DO的业务逻辑<span class="Apple-converted-space">&nbsp;</span><br />
<br />
当然如果业务逻辑简单或者没有的话也可以：<span class="Apple-converted-space">&nbsp;</span><br />
Client--&gt;D Object--&gt;DAO--&gt;DB<span class="Apple-converted-space">&nbsp;</span><br />
<br />
对于第二种的第一个变种固然是个好办法，但如Robbin所说也有缺陷如果有多个Servcie要调用DAO的话，就有问题了。合并也意味中不能很好<br />
<br />
的重用<span class="Apple-converted-space">&nbsp;</span><br />
<br />
说到底就是粒度的问题，分得细重用好，但类多、结构复杂、繁琐。分得粗（干脆用一个类干所有的事）重用差，但类少、结构简单。</span></span>
<img src ="http://www.blogjava.net/lzn1446/aggbug/343367.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lzn1446/" target="_blank">blue_leo</a> 2011-01-22 10:59 <a href="http://www.blogjava.net/lzn1446/articles/343367.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转】Hibernate 原汁原味的四种抓取策略</title><link>http://www.blogjava.net/lzn1446/articles/339558.html</link><dc:creator>blue_leo</dc:creator><author>blue_leo</author><pubDate>Wed, 01 Dec 2010 16:05:00 GMT</pubDate><guid>http://www.blogjava.net/lzn1446/articles/339558.html</guid><wfw:comment>http://www.blogjava.net/lzn1446/comments/339558.html</wfw:comment><comments>http://www.blogjava.net/lzn1446/articles/339558.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lzn1446/comments/commentRss/339558.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lzn1446/services/trackbacks/339558.html</trackback:ping><description><![CDATA[<h4> 最近在研究 Hibernate 的性能优化的时候碰到了"抓取策略", 由于以前没有详细的研究过,</h4>
<p>&nbsp;&nbsp;&nbsp;
所以到处找资料, 但是无论从一些讲 Hibernate 书籍,还是他人 Blog 中都没有找到详细</p>
<p>&nbsp;&nbsp;&nbsp; 介绍
Hibernate 文档中所说的原汁原味的抓取策略, 综合懒加载等等特性混在了一起, 所</p>
<p>&nbsp;&nbsp;&nbsp;
以在这自己在借鉴了他人的基础上研究了下原汁原味的 Hibernate 四种"抓取策略";</p>
<ul>
    <li><em><strong>连
    接抓取（Join fetching）</strong></em> - Hibernate通过 在<code>SELECT</code>语句使用<code>OUTER
    JOIN       <br />
    </code>（外连接）来 获得对象的关联实例或者关联集合.      <hr />
    </li>
    <li><em><strong>查
    询抓取（Select fetching）</strong></em> - 另外发送一条 <code>SELECT</code>
    语句抓取当前对象的关联实      <br />
    体或集合。除非你显式的指定<code>lazy="false"</code>禁止 延迟抓取（lazy
    fetching），否      <br />
    则只有当你真正访问关联关系的时候，才会执行第二条select语句.      <hr />
    </li>
    <li><em><strong>子查询抓取（Subselect fetching）</strong></em> - 另外发送一条<code>SELECT</code>
    语句抓取在前面查询到      <br />
    （或者抓取到）的所有实体对象的关联集合。除非你显式的指定<code>lazy="false"</code>
    禁止延迟      <br />
    抓取（lazy fetching），否则只有当你真正访问关联关系的时候，才会执行第二条      <br />
    select
    语句      <hr />
    </li>
    <li><em><strong>批量抓取（Batch fetching）</strong></em> -
    对查询抓取的优化方案， 通过指定一个主键或外键      <br />
    列表，Hibernate使用单条<code>SELECT</code>语句获
    取一批对象实例或集合</li>
</ul>
<p>&nbsp;&nbsp;&nbsp; 这是文档中的四种抓取策略, 我用 Customer 与 Order
的一个双向一对多例子来使用四种</p>
<p>&nbsp;&nbsp;&nbsp; 抓取策略看看他们的不同之处;</p>
<p><em>&nbsp;&nbsp;&nbsp; Customer :</em></p>
<ol>
    <li>public class Customer {&nbsp; </li>
    <li>private long
    id;&nbsp; </li>
    <li>private String name;&nbsp; </li>
    <li>private
    Set&lt;Order&gt; orders;&nbsp; </li>
    <li>// getter/setter 略</li>
    <li>}&nbsp;
    </li>
</ol>
<p><em>&nbsp;&nbsp;&nbsp; Order :</em></p>
<ol>
    <li>public class Order {&nbsp; </li>
    <li>private long id;&nbsp; </li>
    <li>private String name;&nbsp; </li>
    <li>private Customer
    customer;&nbsp; </li>
    <li>// getter/setter略</li>
    <li>}&nbsp; </li>
</ol>
<p><em>Order 的映射文件是不变的, 放在这 :</em></p>
<ol>
    <li>&lt;hibernate-mapping
    package="com.purking.strategys.endUpOne"&gt;</li>
    <li>&lt;class
    name="Order" table="Order_Table"&gt;</li>
    <li>&lt;id name="id"&gt;</li>
    <li>&lt;generator class="native" /&gt;</li>
    <li>&lt;/id&gt;</li>
    <li>&lt;property name="name" length="20" column="Order_Name" /&gt;</li>
    <li>&lt;many-to-one name="customer"</li>
    <li>class="Customer"</li>
    <li>lazy="proxy"</li>
    <li>fetch="select"</li>
    <li>column="Cus_ID"</li>
    <li>cascade="save-update" /&gt;</li>
    <li>&lt;/class&gt;</li>
    <li>&lt;/hibernate-mapping&gt;</li>
</ol>
<h4><strong><em>连
接抓取（Join fetching）</em></strong></h4>
<p>&nbsp;&nbsp;&nbsp; 连接抓取, 使用连接抓取可以将原本需要查询两次(或多次)表的多次查询 整合到只需</p>
<p>要一次查询即可完成,
举个例子, 我们在初始化一个含有一对多关系的 Customer 与</p>
<p>Order 的时候, 会先查询 Customer
表,找到需要的 Customer , 然后再根据</p>
<p>Customer.id 到 Order 表中查询将Order 集合初始化,
那么在此完成初始化则需要</p>
<p>发送至少两条 SQL 语句, 而如果使用 join 查询的话, 其会根据需要查询的</p>
<p>Customer.id,
将 Customer 表与 Order 表连接起来进行查询,仅仅一条 SQL 语</p>
<p>句就可以将需要的数据全部查询回来;</p>
<p>使用连接抓取的配置文件 :</p>
<ol>
    <li>&lt;hibernate-mapping
    package="com.purking.strategys.endUpOne"&gt;</li>
    <li>&lt;class
    name="Customer" table="Customer_Table" lazy="true"&gt;</li>
    <li>&lt;id
    name="id"&gt;</li>
    <li>&lt;generator class="native" /&gt;</li>
    <li>&lt;/id&gt;</li>
    <li>&lt;property name="name" length="20" column="Cus_Name" /&gt;</li>
    <li>&lt;set name="orders"</li>
    <li>inverse="true"</li>
    <li>fetch="join"&nbsp;
    //---- Here&nbsp; </li>
    <li>&lt;!-- 这里关闭懒加载是为了试验明显 --&gt;</li>
    <li>lazy="false"&gt;</li>
    <li>&lt;key column="Cus_ID" /&gt;</li>
    <li>&lt;one-to-many
    class="Order" /&gt;</li>
    <li>&lt;/set&gt;</li>
    <li>&lt;/class&gt;</li>
    <li>&lt;/hibernate-mapping&gt;</li>
</ol>
<p>我们使用如此查询语句 :</p>
<ol>
    <li>Customer c1 = (Customer)session.get(Customer.class, 11l);&nbsp; </li>
    <li>c1.getOrders().size();&nbsp; </li>
</ol>
<p>Hibernate 发出的 SQL 语句为 : </p>
<ol>
    <li>select&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; customer0_.id as id0_1_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; customer0_.Cus_Name as Cus2_0_1_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;
    orders1_.Cus_ID as Cus3_3_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; orders1_.id as id3_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; orders1_.id as id1_0_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; orders1_.Order_Name
    as Order2_1_0_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; orders1_.Cus_ID as Cus3_1_0_&nbsp;&nbsp; </li>
    <li>from&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; Customer_Table customer0_&nbsp;&nbsp; </li>
    <li>left
    outer join&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; Order_Table orders1_&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    on customer0_.id=orders1_.Cus_ID&nbsp;&nbsp; </li>
    <li>where&nbsp; </li>
    <li>customer0_.id=?&nbsp;
    </li>
</ol>
<p>在此, Hibernate 使用了 left outer join 连接两个表以一条 SQL 语句将
Order 集合</p>
<p>给初始化了;</p>
<h4>   <br />
<strong><em>查询抓取（Select
fetching）</em></strong></h4>
<p><strong>&nbsp;&nbsp;&nbsp; 查询抓取, 这种策略是在集合抓取的时候的默认策略,
即如果集合需要初始化, 那么</strong></p>
<p><strong>会重新发出一条 SQL 语句进行查询; 这是集合默认的抓取策略,
也就是我们常会出现</strong></p>
<p><strong>N+1次查询的查询策略;</strong></p>
<p><strong>配
置文件 :</strong></p>
<ol>
    <li>&lt;hibernate-mapping
    package="com.purking.strategys.endUpOne"&gt;</li>
    <li>&lt;class
    name="Customer" table="Customer_Table" lazy="true"&gt;</li>
    <li>&lt;id
    name="id"&gt;</li>
    <li>&lt;generator class="native" /&gt;</li>
    <li>&lt;/id&gt;</li>
    <li>&lt;property name="name" length="20"
    column="Cus_Name" /&gt;</li>
    <li>&lt;set name="orders"</li>
    <li>inverse="true"</li>
    <li>fetch="select"&gt;</li>
    <li>&lt;key column="Cus_ID" /&gt;</li>
    <li>&lt;one-to-many class="Order" /&gt;</li>
    <li>&lt;/set&gt;</li>
    <li>&lt;/class&gt;</li>
    <li>&lt;/hibernate-mapping&gt;</li>
</ol>
<p> 查询语句不变, 看看 Hibernate 发出的 SQL 语句:</p>
<ol>
    <li>Hibernate:&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; select&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    customer0_.id as id0_0_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; customer0_.Cus_Name as
    Cus2_0_0_&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; from&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    Customer_Table customer0_&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; where&nbsp; </li>
    <li>customer0_.id=?&nbsp;
    </li>
    <li>Hibernate:&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; select&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    orders0_.Cus_ID as Cus3_1_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; orders0_.id as
    id1_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; orders0_.id as id1_0_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    orders0_.Order_Name as Order2_1_0_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    orders0_.Cus_ID as Cus3_1_0_&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; from&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    Order_Table orders0_&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; where&nbsp; </li>
    <li>orders0_.Cus_ID=?&nbsp;
    </li>
</ol>
<p><strong>这就是, 重新发出一条 SQL 语句, 初始化了 Orders
集合;</strong></p>
<h4><em>子查询抓取（Subselect fetching）</em><em>     <br />
</em></h4>
<p>&nbsp;&nbsp;&nbsp; 子查询抓取, 另外发送一条SELECT 语句抓取在前面查询到（或者抓取到）的所有实</p>
<p>体对象的关联集合.
这个理解起来有点糊涂, 举个例子 : 如果你使用 Query 查询出了</p>
<p>4 个 Customer 实体,
由于开启了懒加载,那么他们的 Orders 都没有被初始化, 那么我</p>
<p>现在手动初始化一个Customer 的 Orders
,此时由于我选的是 Subselect fetching</p>
<p>策略,所以 Hibernate 会将前面查询到的实体对象(4 个
Customer)的关联集合(在 </p>
<p>&lt;set name="orders" fetch="subselect" /&gt;
)使用一条 Select 语句一次性抓取</p>
<p>回来, 这样减少了与数据库的交互次数, 一次将每个对象的集合都给初始化了;</p>
<p>[他
是如何这么智能的呢? 原来,他是将上一次查询的 SQL 语句作为这一次查询的 SQL</p>
<p>语句的 where 子查询,
所以上次查询到几个对象,那么这次就初始化几个对象的集</p>
<p>合----- 正因为如此, 所以 subselect 只在
&lt;set&gt; 集合中出现 ];</p>
<p>配置文件: </p>
<ol>
    <li>&lt;hibernate-mapping
    package="com.purking.strategys.endUpOne"&gt;</li>
    <li>&lt;class
    name="Customer" table="Customer_Table" lazy="true"&gt;</li>
    <li>&lt;id
    name="id"&gt;</li>
    <li>&lt;generator class="native" /&gt;</li>
    <li>&lt;/id&gt;</li>
    <li>&lt;property name="name" length="20" column="Cus_Name" /&gt;</li>
    <li>&lt;set name="orders"</li>
    <li>inverse="true"</li>
    <li>fetch="subselect"</li>
    <li>lazy="true"&gt;</li>
    <li>&lt;key column="Cus_ID" /&gt;</li>
    <li>&lt;one-to-many class="Order" /&gt;</li>
    <li>&lt;/set&gt;</li>
    <li>&lt;/class&gt;</li>
    <li>&lt;/hibernate-mapping&gt;</li>
</ol>
<p>测试的语句有变化 :</p>
<ol>
    <li>List results = session&nbsp; </li>
    <li>.createQuery("From
    Customer c where c.id in (11,14,17,20)")&nbsp; </li>
    <li>.list();&nbsp; </li>
    <li>// 这里的四个 id 是我数据库中已经准备好的数据</li>
    <li>Customer c0 =
    (Customer)results.get(0);&nbsp; </li>
    <li>c0.getOrders().size();&nbsp; </li>
</ol>
<p>这个时候再来看看 Hibernate 发出了什么样的 SQL 语句 :</p>
<ol>
    <li>Hibernate:&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; select&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    customer0_.id as id0_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; customer0_.Cus_Name as
    Cus2_0_&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; from&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Customer_Table
    customer0_&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; where&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; customer0_.id
    in (&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 11 , 14 , 17 , 20&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    )&nbsp; </li>
    <li>Hibernate:&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; select&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    orders0_.Cus_ID as Cus3_1_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; orders0_.id as id1_,&nbsp;
    </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; orders0_.id as id1_0_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    orders0_.Order_Name as Order2_1_0_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    orders0_.Cus_ID as Cus3_1_0_&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; from&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    Order_Table orders0_&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; where&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    orders0_.Cus_ID in (&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; select&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    customer0_.id&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    Customer_Table customer0_&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; where&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    customer0_.id in (&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 11 , 14 , 17 , 20&nbsp;
    </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )&nbsp; </li>
</ol>
<p>是
不是发出的 SQL 语句形式与这个抓取策略的名字一样? Hibernate 的命名很清晰的;</p>
<h4><em>批量抓取（Batch
fetching）</em></h4>
<p>批量抓取:"对查询抓取的
优化方案,通过指定一个主键或外键列表，Hibernate使用</p>
<p>单条SELECT语句获取一批对象实例或集合", 也就是说其本质与
select fetching 是</p>
<p>一样的,只不过将一次一条的 select 策略改为一次 N 条的批量 select 查询;
举个例</p>
<p>子 : 还是借用 Subselect fetching 的例子,我查询出了 4 个 Customer 实体,</p>
<p>Orders
开启了懒加载, 所以我现在来手动初始化一个 Customer 的 orders 属性,</p>
<p>这种策略本质上就是 select
fetching,所以如此设置 :</p>
<p>&lt;set name="orders" fetch="select"
batch-size="3" /&gt; 那么此时我初始化</p>
<p>一个 Customer 的 orders 集合的时候,
Hibernate 还是发出了一条 SQL 语句,</p>
<p>不过这条 SQL 与是通过指定了 Order 表中的 Customer_ID
外键列表(2个), 这个</p>
<p>时候 Hibernate 会以一条 SQL 语句初始化 batch-size 指定的数量的
orders 集合;</p>
<p>[他是如何做到的呢? 通过一个主键或外键 列表 做到的, 他将 4 个 Customer 根据</p>
<p>batch-size
分成了两组, 一组有三个 Customer id 值的列表,第二组只有一个,</p>
<p>在初始化 orders
集合的时候就是根据这两个列表来初始化的]</p>
<p>配置文件 :</p>
<ol>
    <li>&lt;hibernate-mapping
    package="com.purking.strategys.endUpOne"&gt;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; &lt;class
    name="Customer" table="Customer_Table" lazy="true"&gt;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    &lt;id name="id"&gt;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;generator
    class="native" /&gt;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/id&gt;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    &lt;property name="name" length="20" column="Cus_Name" /&gt;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    &lt;set name="orders"</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inverse="true"</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    fetch="select"</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lazy="true"</li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    batch-size="3"&gt;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;key column="Cus_ID"
    /&gt;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;one-to-many class="Order" /&gt;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/set&gt;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; &lt;/class&gt;&nbsp; </li>
    <li>&lt;/hibernate-mapping&gt;&nbsp;
    </li>
</ol>
<p>在此,我关闭了集合默认的懒加载, 更有利于试验结果测试代码不变,</p>
<p>再来看看 Hibernate
发出的 SQL 语句 :</p>
<ol>
    <li>Hibernate:&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; select&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    customer0_.id as id0_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; customer0_.Cus_Name as
    Cus2_0_&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; from&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Customer_Table
    customer0_&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; where&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; customer0_.id
    in (&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 11 , 14 , 17 , 20&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    )&nbsp; </li>
    <li>Hibernate:&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; select&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    orders0_.Cus_ID as Cus3_1_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; orders0_.id as id1_,&nbsp;
    </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; orders0_.id as id1_0_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    orders0_.Order_Name as Order2_1_0_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    orders0_.Cus_ID as Cus3_1_0_&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; from&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    Order_Table orders0_&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; where&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    orders0_.Cus_ID in (&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ?, ?, ?&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    )&nbsp; </li>
    <li>Hibernate:&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; select&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    orders0_.Cus_ID as Cus3_1_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; orders0_.id as id1_,&nbsp;
    </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; orders0_.id as id1_0_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    orders0_.Order_Name as Order2_1_0_,&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    orders0_.Cus_ID as Cus3_1_0_&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; from&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    Order_Table orders0_&nbsp;&nbsp; </li>
    <li>&nbsp;&nbsp;&nbsp; where&nbsp; </li>
    <li>orders0_.Cus_ID=?&nbsp;
    </li>
</ol>
<p>原本需要四次 Select 的查询, 由于 Batch-size=3 只用了两次</p>
<p>就完成了;</p>
<hr />
<h5>总结: </h5>
<p>&nbsp;&nbsp;&nbsp; 好了, 这里的四种抓取策略说明完了, 来全局看一下, 通过例子可以看出, 这四种抓取</p>
<p>策略并不是所有的情况都合适的, 例如, 如果我需要初始化的是一个单独的实体, 那</p>
<p>么 subselect
对其就没有效果,因为其本身就只需要查询一个对象, 所以 : </p>
<ol>
    <li>Join fetching , Select
    fetching 与 Batch-size 可以为单个实体的抓取进     <br />
    行性能优化; </li>
    <li>Join
    fetching , Select fetching ,Subselect fetching , Batch fetching     <br />
    都
    可以为集合的抓取进行性能优化;</li>
</ol>
<p>注: 这里对于单个实体可以使用 Batch-size 可能会有点疑惑, 其实在
&lt;class &gt; 上是</p>
<p>具有 Batch-size 抓取策略的; 试想, 使用一个如果是一对一关系呢? 例如
Customer </p>
<p>与 IdCard, 利用 HQL 查询出 4 个 Customer , 我们想一次性初始化 4 个
Customer </p>
<p>的 IdCard 怎么办, 设置 &lt;class name="IdCard"
batch-size="4" &gt; , 可能我们</p>
<p>想设置的地方是 &lt;one-to-one batch-size&gt;
但是这里没有提供这个属性, 可能是因为</p>
<p>如果设置了不好理解吧..</p>
<img src ="http://www.blogjava.net/lzn1446/aggbug/339558.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lzn1446/" target="_blank">blue_leo</a> 2010-12-02 00:05 <a href="http://www.blogjava.net/lzn1446/articles/339558.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转】hibernate中unsaved_value的作用 </title><link>http://www.blogjava.net/lzn1446/articles/307124.html</link><dc:creator>blue_leo</dc:creator><author>blue_leo</author><pubDate>Thu, 24 Dec 2009 04:02:00 GMT</pubDate><guid>http://www.blogjava.net/lzn1446/articles/307124.html</guid><wfw:comment>http://www.blogjava.net/lzn1446/comments/307124.html</wfw:comment><comments>http://www.blogjava.net/lzn1446/articles/307124.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lzn1446/comments/commentRss/307124.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lzn1446/services/trackbacks/307124.html</trackback:ping><description><![CDATA[<p>当你显式的使用session.save()或者session.update()操作一个对象的时候，实际上是用不到unsaved-value
的。某些情况下(父子表关联保存)，当你在程序中并没有显式的使用save或者update一个持久对象，那么Hibernate需要判断被操作的对象究
竟是一个已经持久化过的持久对象，是一个尚未被持久化过的内存临时对象。例如：</p>
<div class="code_title">代码</div>
<div class="code_div">
<div class="dp-highlighter">
<ol class="dp-j">
    <li class="alt"><span><span>Session&nbsp;session&nbsp;=&nbsp;...; &nbsp;&nbsp;</span></span></li>
    <li class=""><span>Transaction&nbsp;tx&nbsp;=&nbsp;...; &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>Parent&nbsp;parent&nbsp;=&nbsp;(Parent)&nbsp;session.load(Parent.</span><span class="keyword">class</span><span>,&nbsp;id); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>Child&nbsp;child&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;Child(); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>child.setParent(parent); &nbsp;&nbsp;</span></li>
    <li class=""><span>child.setName(</span><span class="string">"sun"</span><span>); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>parent.addChild(child); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>s.update(parent); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>s.flush(); &nbsp;&nbsp;</span></li>
    <li class=""><span>tx.commit(); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>s.close();&nbsp;&nbsp;</span></li>
</ol>
</div>
</div>
<script type="text/javascript">render_code();</script>
<p>在上例中，程序并没有显式的session.save(child);
那么Hibernate需要知道child究竟是一个临时对象，还是已经在数据库中有的持久对象。如果child是一个新创建的临时对象(本例中就是这种
情况)，那么Hibernate应该自动产生session.save(child)这样的操作，如果child是已经在数据库中有的持久对象，那么
Hibernate应该自动产生session.update(child)这样的操作。</p>
<p>因此我们需要暗示一下Hibernate，究竟child对象应该对它自动save还是update。在上例中，显然我们应该暗示
Hibernate对child自动save，而不是自动update。那么Hibernate如何判断究竟对child是save还是update呢？
它会取一下child的主键属性 child.getId() ，这里假设id是
java.lang.Integer类型的。如果取到的Id值和hbm映射文件中指定的unsave-value相等，那么Hibernate认为
child是新的内存临时对象，发送save，如果不相等，那么Hibernate认为child是已经持久过的对象，发送update。</p>
<p>unsaved-value="null" (默认情况，适用于大多数对象类型主键 Integer/Long/String/...)</p>
<p>当Hibernate取一下child的Id，取出来的是null(在上例中肯定取出来的是null)，和unsaved-value设定值相等，发送save(child)</p>
<p>当Hibernate取一下child的id，取出来的不是null，那么和unsaved-value设定值不相等，发送update(child)</p>
<p>例如下面的情况：</p>
<div class="code_title">代码</div>
<div class="code_div">
<div class="dp-highlighter">
<ol class="dp-j">
    <li class="alt"><span><span>Session&nbsp;session&nbsp;=&nbsp;...; &nbsp;&nbsp;</span></span></li>
    <li class=""><span>Transaction&nbsp;tx&nbsp;=&nbsp;...; &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>Parent&nbsp;parent&nbsp;=&nbsp;(Parent)&nbsp;session.load(Parent.</span><span class="keyword">class</span><span>,&nbsp;id); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>Child&nbsp;child&nbsp;=&nbsp;(Child)&nbsp;session.load(Child.</span><span class="keyword">class</span><span>,&nbsp;childId); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>child.setParent(parent); &nbsp;&nbsp;</span></li>
    <li class=""><span>child.setName(</span><span class="string">"sun"</span><span>); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li class=""><span>parent.addChild(child); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>s.update(parent); &nbsp;&nbsp;</span></li>
    <li class=""><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>s.flush(); &nbsp;&nbsp;</span></li>
    <li class=""><span>tx.commit(); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>s.close();&nbsp;&nbsp;</span></li>
</ol>
</div>
</div>
<script type="text/javascript">render_code();</script>
<p>child已经在数据库中有了，是一个持久化的对象，不是新创建的，因此我们希望Hibernate发送update(child)，在该例
中，Hibernate取一下child.getId()，和unsave-value指定的null比对一下，发现不相等，那么发送
update(child)。</p>
<p>BTW:
parent对象不需要操心，因为程序显式的对parent有load操作和update的操作，不需要Hibernate自己来判断究竟是save还是
update了。我们要注意的只是child对象的操作。另外unsaved-value是定义在Child类的主键属性中的。</p>
<div class="code_title">代码</div>
<div class="code_div">
<div class="dp-highlighter">
<ol class="dp-xml">
    <li class="alt"><span><span class="tag">&lt;</span><span class="tag-name">class</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">"Child"</span><span>&nbsp;</span><span class="attribute">table</span><span>=</span><span class="attribute-value">"child"</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></span></li>
    <li class=""><span class="tag">&lt;</span><span class="tag-name">id</span><span>&nbsp;</span><span class="attribute">column</span><span>=</span><span class="attribute-value">"id"</span><span>&nbsp;</span><span class="attribute">name</span><span>=</span><span class="attribute-value">"id"</span><span>&nbsp;</span><span class="attribute">type</span><span>=</span><span class="attribute-value">"integer"</span><span>&nbsp;</span><span class="attribute">unsaved-value</span><span>=</span><span class="attribute-value">"null"</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span><span class="tag">&lt;</span><span class="tag-name">generator</span><span>&nbsp;</span><span class="attribute">class</span><span>=</span><span class="attribute-value">"identity"</span><span class="tag">/&gt;</span><span>&nbsp;&nbsp;</span></li>
    <li class=""><span class="tag">&lt;/</span><span class="tag-name">id</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>... &nbsp;&nbsp;</span></li>
    <li class=""><span class="tag">&lt;/</span><span class="tag-name">class</span><span class="tag">&gt;</span><span>&nbsp;&nbsp;</span></li>
</ol>
</div>
</div>
<script type="text/javascript">render_code();</script>
<p>如果主键属性不是对象型，而是基本类型，如int/long/double/...，那么你需要指定一个数值型的unsaved-value，例如：</p>
<div class="code_title">代码</div>
<div class="code_div">
<div class="dp-highlighter">
<ol class="dp-j">
    <li class="alt"><span><span>unsaved-</span><span class="keyword">null</span><span>=</span><span class="string">"0"</span><span>&nbsp;&nbsp;</span></span></li>
</ol>
</div>
</div>
<script type="text/javascript">render_code();</script>
<p>在此提醒大家，很多人以为对主键属性定义为int/long，比定义为Integer/Long运行效率来得高，认为基本类型不需要进行对象的封装
和解构操作，因此喜欢把主键定义为int/long的。但实际上，Hibernate内部总是把主键转换为对象型进行操作的，就算你定义为int
/long型的，Hibernate内部也要进行一次对象构造操作，返回给你的时候，还要进行解构操作，效率可能反而低也说不定。因此大家一定要扭转一个
观点，在Hibernate中，主键属性定义为基本类型，并不能够比定义为对象型效率来的高，而且也多了很多麻烦，因此建议大家使用对象型的
Integer/Long定义主键。</p>
<p>unsaved-value="none"和 <br />
unsaved-value="any"</p>
<p>主主要用在主键属性不是通过Hibernate生成，而是程序自己setId()的时候。</p>
<p>在这里多说一句，强烈建议使用Hibernate的id generator，或者你可以自己扩展Hibernate的id
generator，特别注意不要使用有实际含义的字段当做主键来用！例如用户类User，很多人喜欢用用户登陆名称做为主键，这是一个很不好的习惯，当
用户类和其他实体类有关联关系的时候，万一你需要修改用户登陆名称，一改就需要改好几张表中的数据。偶合性太高，而如果你使用无业务意义的id
generator，那么修改用户名称，就只修改user表就行了。</p>
<p>由这个问题引申出来，如果你严格按照这个原则来设计数据库，那么你基本上是用不到手工来setId()的，你用Hibernate的id generator就OK了。因此你也不需要了解当</p>
<p>unsaved-value="none"和 <br />
unsaved-value="any"</p>
<p>究竟有什么含义了。如果你非要用assigned不可，那么继续解释一下：</p>
<p>unsaved-value="none" 的时候，由于不论主键属性为任何值，都不可能为none，因此Hibernate总是对child对象发送update(child)</p>
<p>unsaved-value="any" 的时候，由于不论主键属性为任何值，都肯定为any，因此Hibernate总是对child对象发送save(child)</p>
<p>大多数情况下，你可以避免使用assigned，只有当你使用复合主键的时候不得不手工setId()，这时候需要你自己考虑究竟怎么设置unsaved-value了，根据你自己的需要来定。</p>
<p>BTW: Gavin King强烈不建议使用composite-id，强烈建议使用UserType。</p>
<p>因此，如果你在系统设计的时候，遵循如下原则：</p>
<p><span style="color: red;">1、使用Hibernate的id generator来生成无业务意义的主键，不使用有业务含义的字段做主键，不使用assigned。</span></p>
<p>2、使用对象类型(String/Integer/Long/...)来做主键，而不使用基础类型(int/long/...)做主键</p>
<p>3、不使用composite-id来处理复合主键的情况，而使用UserType来处理该种情况。</p>
<p>那么你永远用的是unsaved-value="null" ，不可能用到any/none/..了。</p>
<img src ="http://www.blogjava.net/lzn1446/aggbug/307124.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lzn1446/" target="_blank">blue_leo</a> 2009-12-24 12:02 <a href="http://www.blogjava.net/lzn1446/articles/307124.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转】hibernate集合映射inverse和cascade </title><link>http://www.blogjava.net/lzn1446/articles/307122.html</link><dc:creator>blue_leo</dc:creator><author>blue_leo</author><pubDate>Thu, 24 Dec 2009 03:56:00 GMT</pubDate><guid>http://www.blogjava.net/lzn1446/articles/307122.html</guid><wfw:comment>http://www.blogjava.net/lzn1446/comments/307122.html</wfw:comment><comments>http://www.blogjava.net/lzn1446/articles/307122.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lzn1446/comments/commentRss/307122.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lzn1446/services/trackbacks/307122.html</trackback:ping><description><![CDATA[<font size="2">4. hibernate如何根据pojo来更新数据库<br />
<br />
4.0 在commit/flush之前，hibernate不会对pojo对象作神秘的处理。<br />
4.0.1 在select查询出pojo时，hibernate根据&#8220;字段--属性&#8221;的对应关系，用字段的值填充pojo的属性；<br />
然后根据&#8220;关系标记&#8221;生成sql语句从relationTable中查询出满足条件的relationPojo，并把这些relatinPojo<br />
放到&#8220;关系属性&#8221;中。这个过程是机械的。<br />
<br />
4.0.2 在pojo对象被查出来后，到commit(或flush)之前，它将是一个普通的java对象，hibernate不会做额外的手脚。<br />
比如，不会限制你设置一个属性的值为null或其它任何值<br />
在集合类Set的add(object)操作时， 不会改变object的值，不会检查参数object是否是一个pojo对象<br />
设置mainPojo的一个&#8220;桥属性&#8221;的值，不会自动设置relationPojo的对应的&#8220;桥属性&#8221;的值。<br />
执行session.delete(pojo)时，pojo本身没有变化，他的属性值也没有变化。<br />
执行session.save(pojo)时，如果pojo的id不是hibernate或数据库生成,则它的值没有变化。<br />
如果pojo的id是hibernate或数据库生成，则hibernate会把id给pojo设上去。<br />
<br />
extend: 对lazy=true的set，hibernate在进行set的操作(调用java.util.Set中声明的方法)时<br />
会先inialize这个set，仅此而已。而inialize仅仅是从数据库中捞出set的数据。 <br />
如果一个set已经被inialize了，那么对它进行的操作就是java.util.Set接口中定义的语义。<br />
<br />
另外，如果id由hibernate来生成，那么在save(pojo)时，hibernate会改变该pojo，会设置它的id，这<br />
可能改变该pojo的hashCode，详细地讨论见帖《》<br />
<br />
mapping文件中标记的某些属性及pojo对象的操作会对数据库操作产生影响，这些影响都是在commit时才会起作用。<br />
而在commit前pojo的状态不受它们的影响。<br />
<br />
不过，待commit之时，将由hibernate完全掌控，它好像知道pojo对象从创建到commit这中间的所有变化。<br />
<br />
<br />
4.01. 关联更新<br />
"关系标记"对应的属性是一个pojo或一个pojo的集合，修改&#8220;关系属性&#8221;的值能会导致更新mainTable表，也可能会更新relationTable表。<br />
<br />
这种更新暂叫&#8220;关联更新&#8221;。<br />
<br />
<br />
4.1.inverse属性的作用（假定没有设置cascade属性） <br />
4.1.1 &#8220;只有集合标记（set/map/list/array/bag）才有inverse属性&#8221;。<br />
————不妨以标记set为例，具体为&#8220;一个地区（Address表）的学校（School表）&#8221; -- address.schoolSet。<br />
<br />
4.1.2 &#8220;set的inverse属性决定是否把对set的改动反映到数据库中去。<br />
inverse=false————反映；inverse=true————不反映&#8221;<br />
inverse属性默认为false<br />
<br />
对&lt;one-to-many&gt;和&lt;many-to-many&gt;子标记，这两条都适用。<br />
不管是对set做什么操作，4.1.2都适用。<br />
<br />
4.1.3当inverse=false时，hibernate如何将对set的改动反映到数据库中：<br />
<br />
对set的操作主要有：（1）新增元素 address.getSchoolSet().add(oneSchool);<br />
（2）删除元素 address.getSchoolSet().remove(oneSchool);<br />
（3）删除set address.setSchoolSet(null);<br />
（4）设新set address.setSchoolSet( newSchoolSet);<br />
（5）转移set otherSchoolSet = otherAddress.getSchoolSet();<br />
otherAddress.setSchoolSet(null);<br />
address.setSchoolSet(otherSchoolSet);<br />
（6）改变set中元素的属性的值 如果是改变key属性，这会导致异常<br />
如果改变的是普通的属性，则hibernate认为set没有变化（在后面可以看出缘由）。<br />
所以这种情形不予考虑。<br />
<br />
改变set后，hibernate对数据库的操作根据是&lt;one-to-many&gt;关系还是&lt;many-to-many&gt;关系而有不同。<br />
<br />
对one-to-many，对school set的改动，会改变表SCHOOL中的数据:<br />
#SCHOOL_ID是school表的主键，SCHOOL_ADDRESS是school表中的地址栏位<br />
#表School的外键为SCHOOL_ADDRESS，它对应表Address的主键ADDRESS_ID<br />
（11）insert oneSchool———— sqlInsertRowString: <br />
update SCHOOL set SCHOOL_ADDRESS=? where SCHOOL_ID=? <br />
(仅仅update foreign-key的值。)<br />
（22）delete oneSchool———— sqlDeleteRowString: <br />
update SCHOOL set SCHOOL_ADDRESS=null where SCHOOL_ID=?<br />
（很奇怪，把foreign-key设置为null不知道有什么实际意义？）<br />
（33）delete 属于某一address的所有school ————sqlDeleteString：<br />
update SCHOOL set SCHOOL_ADDRESS=null where SCHOOL_ADDRESS=?<br />
（44）update ————sqlUpdateRowString：""， no need<br />
<br />
对many-to-many，对school set的改动，会改变关系表ADDRESS_SCHOOL中的数据:<br />
#&#8220;地区————学校&#8221;的关系为多对多的关系有点牵强，只是为了方便与上面的one-to-many作比较<br />
#假设有一个关系表ADDRESS_SCHOOL，有两个字段ADDRESS_ID, SCHOOL_ID，<br />
#这两个字段分别对应ADDRESS和SCHOOL两表的key<br />
（11）insert的SQL语句为： insert into ADDRESS_SCHOOL(ADDRESS_ID, SCHOOL_ID) <br />
values(?,?)<br />
（22）delete的SQL语句为： delete from ADDRESS_SCHOOL <br />
where ADDRESS_ID=? AND SCHOOL_ID=?<br />
（33）delete all的SQL语句为： delete from ADDRESS_SCHOOL<br />
where ADDRESS_ID=?<br />
（44）update的sql语句为 ————sqlUpdateRowString：<br />
update ADDRESS_SCHOOL set ADDRESS_ID=?<br />
where ADDRESS_ID=? AND SCHOOL_ID=?<br />
<br />
对set的操作(1),hibernate会执行(11)sqlInsertRowString<br />
对set的操作(2),hibernate会执行(22)sqlDeleteRowString<br />
对set的操作(3),hibernate会执行(33)sqlDeleteString<br />
对set的操作(4),老的schoolSet因为没有所属的address,所以被全部delete掉，即先执行(33)sqlDeleteString<br />
然后新增新的schoolSet,即再执行sqlInsertRowString<br />
对set的操作(5)，实际上就是将set从一个pojo转移到另一pojo：<br />
首先，执行sqlDeleteString，删除掉otherAddress所属的school<br />
然后，执行sqlDeleteString，删除掉address原先的school<br />
最后，执行sqlInsertRowString，将otherSchoolSet新增给address<br />
<br />
总结：（1）对one-to-many而言，改变set，会让hibernate执行一系列的update语句， 不会delete/insert数据<br />
（2）对many-to-many而言，改变set,只修改关系表的数据，不会影响many-to-many的另一方。<br />
（3）虽然one-to-many和many-to-many的数据库操作不一样，但目的都是一个：维护数据的一致性。执行的sql都<br />
只涉及到&#8220;桥字段&#8221;，不会考虑或改变其他的字段，所以对set的操作(6)是没有效果地。<br />
extend:对list,可能还会维护index字段。<br />
<br />
4.1.4 &#8220;inverse与cascade没有什么关系，互无牵扯。&#8221;<br />
commit后，这两个属性发挥作用的时机不同，hibernate会根据对pojo对象的改动，及cascade属性的设置，<br />
生成一系列的Action，比如UpdateAction,DeleteAction,InsertAction等，每个Action都有execute方法以执行对应的sql语句。<br />
待所有这些Action都生成好了后，hibernate再一起执行它们，在执行sql前，inverse属性起作用，<br />
当inverse=true时，不执行sql；当inverse=false时，执行sql。<br />
<br />
4.1.5 inverse的默认值为false，所以inverse属性默认会进行&#8220;关联更新&#8221;。<br />
<br />
4.1.6 建议：只对set + many-to-many设置inverse=false，其他的标记不考虑inverse属性。<br />
&nbsp; 糟糕的是，不设置inverse属性时，inverse默认为false。<br />
<br />
4.2. 级联（cascade）属性的作用： <br />
4.2.1 只有&#8220;关系标记&#8221;才有cascade属性：many-to-one，one-to-one ，any, <br />
set(map, bag, idbag, list, array) + one-to-many(many-to-many)<br />
<br />
4.2.2 级联指的是当主控方执行操作时，关联对象（被动方）是否同步执行同一操作。<br />
pojo和它的关系属性的关系就是&#8220;主控方 -- 被动方&#8221;的关系，如果关系属性是一个set，那么被动方就是set中的一个一个元素，。<br />
比如：学校（School）有三个属性：地区(Address),校长（TheMaster）和学生(Set， 元素为Student)<br />
执行session.delete(school)时，级联决定是否执行session.delete(Address),session.delete(theMaster)，<br />
是否对每个aStudent执行session.delete(aStudent)。<br />
<br />
extend:这点和inverse属性是有区别的。见4.3.<br />
<br />
4.2.3 一个操作因级联cascade可能触发多个关联操作。前一个操作叫&#8220;主控操作&#8221;，后一个操作叫&#8220;关联操作&#8221;。<br />
cascade属性的可选值：<br />
all : 所有情况下均进行关联操作。<br />
none：所有情况下均不进行关联操作。这是默认值。<br />
save-update:在执行save/update/saveOrUpdate时进行关联操作。<br />
delete：在执行delete时进行关联操作。 <br />
<br />
具体执行什么&#8220;关联操作&#8221;是根据&#8220;主控操作&#8221;来的：<br />
&#8220;主控操作&#8221; &nbsp; &nbsp; &nbsp; &#8220;关联操作&#8221;<br />
session.saveOrUpdate --&gt; session.saveOrUpdate (执行saveOrUpdate实际上会执行save或者update)<br />
session.save ----&gt; session.saveOrUpdate<br />
session.udpate --&gt; session.saveOrUpdate<br />
session.delete --&gt; session.delete<br />
<br />
4.2.4 主控操作和关联操作的先后顺序是&#8220;先保存one，再保存many；先删除many，再删除one；先update主控方，再update被动方&#8221;<br />
对于one-to-one，当其属性constrained="false"（默认值）时，它可看作one-to-many关系；<br />
&nbsp; 当其属性constrained="true"时，它可看作many-to-one关系；<br />
对many-to-many，它可看作one-to-many。<br />
<br />
比如：学校（School）有三个属性：地区(Address),校长（TheMaster，其constrained="false"）和学生(Set， 元素为Student) <br />
当执行session.save(school)时，<br />
实际的执行顺序为：session.save(Address);<br />
session.save(school);<br />
session.save(theMaster);<br />
for( 对每一个student ){<br />
session.save(aStudent);<br />
}<br />
<br />
当执行session.delete(school)时，<br />
实际的执行顺序为：session.delete(theMaster);<br />
for( 对每一个student ){<br />
session.delete(aStudent);<br />
}<br />
session.delete(school);<br />
session.delete(Address);<br />
<br />
当执行session.update(school)时，<br />
实际的执行顺序为：session.update(school);<br />
session.saveOrUpdate(Address);<br />
session.saveOrUpdate(theMaster);<br />
for( 对每一个student ){<br />
session.saveOrUpdate(aStudent);<br />
}<br />
注意：update操作因级联引发的关联操作为saveOrUpdate操作，而不是update操作。<br />
saveOrUpdate与update的区别是：前者根据操作对象是保存了还是没有保存，而决定执行update还是save<br />
<br />
extends: 实际中，删除学校不会删除地区，即地区的cascade一般设为false<br />
另外，many-to-many关系很少设置cascade=true，而是设置inverse=false。这个反映了cascade和inverse的区别。见4.3<br />
<br />
4.2.6 cascade的默认值为false，所以inverse属性默认会进行&#8220;关联更新&#8221;。<br />
<br />
4.2.7 总结：级联（cascade）就是操作一个对象时，对它的属性（其cascade=true）也进行这个操作。<br />
<br />
<br />
4.3 inverse和cascade的比较<br />
这两个属性本身互不影响，但起的作用有些类似，都能引发对关系表的更新。<br />
<br />
4.3.1 inverse只对set+one-to-many(或many-to-many)有效，对many-to-one, one-to-one无效。<br />
cascade对关系标记都有效。<br />
<br />
4.3.2 inverse对集合对象整体起作用，cascade对集合对象中的一个一个元素起作用，如果集合为空，那么cascade不会引发关联操作。<br />
比如将集合对象置为null， school.setStudentSet(null)<br />
inverse导致hibernate执行:udpate STUDENT set SCHOOL_ID=null where SCHOOL_ID=?<br />
cascade则不会执行对STUDENT表的关联更新， 因为集合中没有元素。<br />
<br />
再比新增一个school, session.save(school)<br />
inverse导致hibernate执行：<br />
for( 对(school的每一个student ){<br />
udpate STUDENT set SCHOOL_ID=? where STUDENT_ID=? //将学生的school_id改为新的school的id<br />
}<br />
cascade导致hibernate执行：<br />
for( 对school的每一个student ){<br />
session.save(aStudent); //对学生执行save操作<br />
}<br />
<br />
extends:如果改变集合中的部分元素（比如新增一个元素），<br />
inverse: hibernate先判断哪些元素改变了，对改变的元素执行相应的sql<br />
cascade: 它总是对集合中的每个元素执行关联操作。<br />
（在关联操作中，hibernate会判断操作的对象是否改变）<br />
<br />
4.3.2 两个起作用的时机不同：<br />
cascade：在对主控方操作时，级联发生。<br />
inverse: 在flush时（commit会自动执行flush)，对session中的所有set，hibernate判断每个set是否有变化，<br />
对有变化的set执行相应的sql，执行之前，会有个判断：if( inverse == true ) return;<br />
<br />
可以看出cascade在先，inverse在后。<br />
<br />
4.3.3 inverse 对set + one-to-many 和 set + many-to-many 起的作用不同。hibernate生成的sql不同。<br />
对one-to-many，hibernate对many方的数据库表执行update语句。<br />
对many-to-many, hibernate对关系表执行insert/update/delte语句，注意不是对many方的数据库表而是关系表。<br />
<br />
cascase 对set都是一致的，不管one-to-many还是many-to-many。都简单地把操作传递到set中的每个元素。所以它总是更新many<br />
方的数据库表。<br />
<br />
4.3.4 建议：只对set + many-to-many设置inverse=false，其他的标记不考虑inverse属性，都设为inverse=true。<br />
&nbsp; <br />
&nbsp; 对cascade，一般对many-to-one，many-to-many，constrained=true的one-to-one 不设置级联删除。</font>
<img src ="http://www.blogjava.net/lzn1446/aggbug/307122.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lzn1446/" target="_blank">blue_leo</a> 2009-12-24 11:56 <a href="http://www.blogjava.net/lzn1446/articles/307122.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>