﻿<?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-Koska's BLOG-文章分类-Hibernate</title><link>http://www.blogjava.net/baxiaopeng/category/12069.html</link><description>Tell me and I forget, Teache me and I remember, Involve me and I  learn.</description><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 04:19:44 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 04:19:44 GMT</pubDate><ttl>60</ttl><item><title>Don’t repeat the DAO!</title><link>http://www.blogjava.net/baxiaopeng/articles/56104.html</link><dc:creator>Koska</dc:creator><author>Koska</author><pubDate>Sat, 01 Jul 2006 10:18:00 GMT</pubDate><guid>http://www.blogjava.net/baxiaopeng/articles/56104.html</guid><wfw:comment>http://www.blogjava.net/baxiaopeng/comments/56104.html</wfw:comment><comments>http://www.blogjava.net/baxiaopeng/articles/56104.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/baxiaopeng/comments/commentRss/56104.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/baxiaopeng/services/trackbacks/56104.html</trackback:ping><description><![CDATA[
		<div class="entry">
				<p>译者：Nicholas @ Nirvana Studio<br />
原文地址：http://www-128.ibm.com/developerworks/java/library/j-genericdao.html
</p>
				<p>使用Hibernate和Spring AOP购建一个范型类型安全的DAO<br />
2006年五月12日<br /></p>
				<blockquote>在采用了Java
5的范型之后，要实现一个基于范型类型安全的数据访问对象（DAO）就变得切实可行了。在这篇文章里，系统架构师Per
Mellqvist展示了一个基于Hibernate的范型DAO实现。然后将介绍如何使用Spring
AOP的introduction为一个类增加一个类型安全的接口以便于执行查询。</blockquote>
				<p>对于大多数开发者来说，在系统中为每一个DAO编写几乎一样的代码已经成为了一种习惯。同时大家也都认可这种重复就是“代码的味道”，我们中的大多
数已经习惯如此。当然也有另外的办法。你可以使用很多ORM工具来避免代码的重复编写。举个例子，用Hibernate，你可以简单的使用session
操作直接控制你的持久化领域对象。这种方式的负面影响就是丢失了类型安全。
</p>
				<p>为什么你的数据访问代码需要一个类型安全的接口？我认为它减少了编程错误，提高了生产率，尤其是在使用现代高级IDE的时候。首先，一个
类型安全的接口清晰的制定了哪些领域对象具有持久化功能。其次，它消除了类型转换带来的潜在问题。最后，它平衡了IDE的自动完成功能。使用自动完成功能
是最快的方式来记住对于适当的领域类哪些查询是可用的。
</p>
				<p>在这篇文章里，我将展示给大家如何避免一次次地重复编写DAO代码，但同时还收益于类型安全的接口。事实上，所有内需要编写的是为新的DAO编写一个Hibernate映射文件，一个POJO的Java接口，并且10行Spring配置文件。

</p>
				<h2>DAO实现</h2>
				<p>DAO模式对于任何Java开发人员来说都是耳熟能详的。这个模式的实现相当多，所以让我们仔细推敲一下我这篇文章里面对于DAO实现的一些假设：
</p>
				<ul>
						<li>所有系统中的数据库访问都是通过DAO来完成封装</li>
						<li>每一个DAO实例对一个主要的领域对象或者实体负责。如果一个领域对象具有独立的生命周期，那么它需要具有自己的DAO。</li>
						<li>DAO具有CRUD操作</li>
						<li>DAO可以允许基于criteria方式的查询而不仅仅是通过主键查询。我将这些成为finder方法或者finders。这个finder的返回值通常是DAO所负责的领域对象的集合。</li>
				</ul>
				<h2>范型DAO接口</h2>
				<p>范型DAO的基础就是CRUD操作。下面的接口定义了范型DAO的方法：

</p>
				<blockquote>
						<pre>public interface GenericDao &lt;T, PK extends Serializable&gt; {<br /><br />    /** Persist the newInstance object into database */<br />    PK create(T newInstance);<br /><br />    /** Retrieve an object that was previously persisted to the database using<br />     *   the indicated id as primary key<br />     */<br />    T read(PK id);<br /><br />    /** Save changes made to a persistent object.  */<br />    void update(T transientObject);<br /><br />    /** Remove an object from persistent storage in the database */<br />    void delete(T persistentObject);<br />}<br /></pre>
				</blockquote>
				<h3>实现这个接口</h3>
				<p>使用Hibernate实现上面的接口是非常简单的。也就是调用一下Hibernate的方法和增加一些类型转换。Spring负责session和transaction管理。

</p>
				<blockquote>
						<pre>public class GenericDaoHibernateImpl &lt;T, PK extends Serializable&gt;<br />    implements GenericDao&lt;T, PK&gt;, FinderExecutor {<br />    private Class&lt;T&gt; type;<br /><br />    public GenericDaoHibernateImpl(Class&lt;T&gt; type) {<br />        this.type = type;<br />    }<br /><br />    public PK create(T o) {<br />        return (PK) getSession().save(o);<br />    }<br /><br />    public T read(PK id) {<br />        return (T) getSession().get(type, id);<br />    }<br /><br />    public void update(T o) {<br />        getSession().update(o);<br />    }<br /><br />    public void delete(T o) {<br />        getSession().delete(o);<br />    }<br /><br />    // Not showing implementations of getSession() and setSessionFactory()<br />}<br /></pre>
				</blockquote>
				<h3>Spring 配置</h3>
				<p>最后，Spring配置，我创建了一个GenericDaoHibernateImpl的实例。GenericDaoHibernateImpl的
构造器必须被告知领域对象的类型，这样DAO实例才能为之负责。这个同样需要Hibernate运行时知道这个对象的类型。下面的代码中，我将领域类
Person传递给构造器并且将Hibernate的session工厂作为一个参数用来实例化DAO：
</p>
				<blockquote>
						<pre>&lt;bean id="personDao" class="genericdao.impl.GenericDaoHibernateImpl"&gt;<br />        &lt;constructor-arg&gt;<br />            &lt;value&gt;genericdaotest.domain.Person&lt;/value&gt;<br />        &lt;/constructor-arg&gt;<br />        &lt;property name="sessionFactory"&gt;<br />            &lt;ref bean="sessionFactory"/&gt;<br />        &lt;/property&gt;<br />&lt;/bean&gt;<br /></pre>
				</blockquote>
				<h2>可用的范型DAO</h2>
				<p>我还没有全部完成，但我现在已经有了一个可供作的代码。下面的代码展示了范型DAO如何使用：

</p>
				<blockquote>
						<pre>public void someMethodCreatingAPerson() {<br />    ...<br />    GenericDao dao = (GenericDao)<br />     beanFactory.getBean("personDao"); // This should normally be injected<br /><br />    Person p = new Person("Per", 90);<br />    dao.create(p);<br />}<br /></pre>
				</blockquote>
				<p>这时候，我有一个范型DAO有能力进行类型安全的CRUD操作。同时也有理由编写GenericDaoHibernateImpl的子类来为每个领
域对象增加查询功能。但是这篇文章的主旨在于展示如何完成这项功能而不是为每个查询编写明确的代码，然而，我将会使用多个工具来介绍DAO的查询，这就是
Spring AOP和Hibernate命名查询。
</p>
				<h2>Spring AOP介绍</h2>
				<p>你可以使用Spring
AOP提供的introduction功能将一个现存的对象包装到一个代理里面来增加新的功能，定义它需要实现的新接口，并且将之前所有不支持的方法委派
到一个处理机。在我的DAO实现里面，我用introduction将一定数量的finder方法增加到现存的范型DAO类里面。因为finder方法针
对特定的领域对象，所以它们被应用到表明接口的范型DAO中。
</p>
				<blockquote>
						<pre>&lt;bean id="finderIntroductionAdvisor" class="genericdao.impl.FinderIntroductionAdvisor"/&gt;<br /><br />&lt;bean id="abstractDaoTarget"<br />        class="genericdao.impl.GenericDaoHibernateImpl" abstract="true"&gt;<br />        &lt;property name="sessionFactory"&gt;<br />            &lt;ref bean="sessionFactory"/&gt;<br />        &lt;/property&gt;<br />&lt;/bean&gt;<br /><br />&lt;bean id="abstractDao"<br />        class="org.springframework.aop.framework.ProxyFactoryBean" abstract="true"&gt;<br />        &lt;property name="interceptorNames"&gt;<br />            &lt;list&gt;<br />                &lt;value&gt;finderIntroductionAdvisor&lt;/value&gt;<br />            &lt;/list&gt;<br />        &lt;/property&gt;<br />&lt;/bean&gt;<br /></pre>
				</blockquote>
				<p>在上面的配置中，我定义了三个Spring
bean，第一个bean，FinderIntroductionAdvisor，处理那些introduce到DAO中但是不属于
GenericDaoHibernateImpl类的方法。一会我再介绍Advisor bean的详细情况。
</p>
				<p>第二个bean定义为“abstract”。在Spring中，这个bean可以被其他bean重用但是它自己不会被实例化。不同于抽象
属性，bean的定义简单的指出了我需要一个GenericDaoHibernateImpl的实例同时需要一个SessionFactory的引用。注
意GenericDaoHibernateImpl类只定义了一个构造器接受领域类作为参数。因为这个bean是抽象的，我可以无限次的重用并且设定合适
的领域类。
</p>
				<p>最后，第三个，也是最有意思的是bean将GenericDaoHibernateImpl的实例包装进了一个代理，给予了它执行finder方法的能力。这个bean定义同样是抽象的并且没有指定任何接口。这个接口不同于任何具体的实例。

</p>
				<h3>扩展通用DAO</h3>
				<p>每个DAO的接口，都是基于GenericDAO接口的。我需要将为特定的领域类适配接口并且将其扩展包含我的finder方法。

</p>
				<blockquote>
						<pre>public interface PersonDao extends GenericDao&lt;Person, Long&gt; {<br />    List&lt;Person&gt; findByName(String name);<br />}<br /></pre>
				</blockquote>
				<p>上面的代码清晰的展示了通过用户名查找Person对象列表。所需的Java实现类不需要包含任何的更新操作，因为这些已经包含在了通用DAO里。

</p>
				<h3>配置PersonDao</h3>
				<p>因为Spring配置依赖之前的那些抽象bean，所以它变得很紧凑。我需要指定DAO负责的领域类，并且我需要告诉Spring我这个DAO需要实现的接口。


</p>
				<blockquote>
						<pre>&lt;bean id="personDao" parent="abstractDao"&gt;<br />    &lt;property name="proxyInterfaces"&gt;<br />        &lt;value&gt;genericdaotest.dao.PersonDao&lt;/value&gt;<br />    &lt;/property&gt;<br />    &lt;property name="target"&gt;<br />        &lt;bean parent="abstractDaoTarget"&gt;<br />            &lt;constructor-arg&gt;<br />                &lt;value&gt;genericdaotest.domain.Person&lt;/value&gt;<br />            &lt;/constructor-arg&gt;<br />        &lt;/bean&gt;<br />    &lt;/property&gt;<br />&lt;/bean&gt;<br /></pre>
				</blockquote>
				<p>你可以这样使用：
</p>
				<blockquote>
						<pre>public void someMethodCreatingAPerson() {<br />    ...<br />    PersonDao dao = (PersonDao)<br />     beanFactory.getBean("personDao"); // This should normally be injected<br /><br />    Person p = new Person("Per", 90);<br />    dao.create(p);<br /><br />    List&lt;Person&gt; result = dao.findByName("Per"); // Runtime exception<br />}<br /></pre>
				</blockquote>
				<p>上面的代码是使用类型安全接口PersonDao的一种正确途径，但是DAO的实现并没有完成。当调用findByName()的时候导致了一个运
行时异常。这个问题是我还没有findByName()。剩下的工作就是指定查询语句。要完成这个，我使用Hibernate命名查询。
</p>
				<h3>Hibernate命名查询</h3>
				<p>使用Hibernate，你可以定义任何HQL查询在映射文件里，并且给它一个名字。你可以在之后的代码里面方便的通过名字引用这个查询。这么做的
一个优点就是能够在部署的时候调节查询而不需要改变代码。正如你一会将看到的，另一个好处就是实现一个“完整”的DAO而不需要编写任何Java实现代
码。
</p>
				<blockquote>
						<pre>&lt;hibernate-mapping package="genericdaotest.domain"&gt;<br />     &lt;class name="Person"&gt;<br />         &lt;id name="id"&gt;<br />             &lt;generator class="native"/&gt;<br />         &lt;/id&gt;<br />         &lt;property name="name" /&gt;<br />         &lt;property name="weight" /&gt;<br />     &lt;/class&gt;<br /><br />     &lt;query name="Person.findByName"&gt;<br />         &lt;![CDATA[select p from Person p where p.name = ? ]]&gt;<br />     &lt;/query&gt;<br />&lt;/hibernate-mapping&gt;<br /></pre>
				</blockquote>
				<p>上面的代码定义了领域类Person的Hibernate映射文件，有两个属性：name和weight。Person是一个具有上面属性的简单的
POJO。这个文件同时包含了一个查询，通过提供的name属性从数据库查找Person实例。Hibernate为命名查询提供了不真实的命名空间功
能。为了便于讨论，我将所有的查询名字的前缀变成领域类的的名称。在现实场景中，使用完整的类名，包含包名，是一个更好的主意。
</p>
				<h2>总览</h2>
				<p>你已经看到了为任何领域对象创建并配置DAO的所需步骤了。这三个简单的步骤就是：
</p>
				<ol>
						<li>定义一个接口继承GenericDao并且包含任何所需的finder方法</li>
						<li>在映射文件中为每个领域类的finder方法增加一个命名查询。</li>
						<li>为DAO增加10行Spring配置</li>
				</ol>
				<h2>可重用的DAO类</h2>
				<p>Spring advisor和interceptor的功能比较琐碎，事实上他们的工作都引用回了GenericDaoHibernateImpl类。所有带有“find”开头的方法都被传递给DAO的单一方法executeFinder()。
</p>
				<blockquote>
						<pre>public class FinderIntroductionAdvisor extends DefaultIntroductionAdvisor {<br />    public FinderIntroductionAdvisor() {<br />        super(new FinderIntroductionInterceptor());<br />    }<br />}<br /><br />public class FinderIntroductionInterceptor implements IntroductionInterceptor {<br /><br />    public Object invoke(MethodInvocation methodInvocation) throws Throwable {<br /><br />        FinderExecutor genericDao = (FinderExecutor) methodInvocation.getThis();<br /><br />        String methodName = methodInvocation.getMethod().getName();<br />        if (methodName.startsWith("find")) {<br />            Object[] arguments = methodInvocation.getArguments();<br />            return genericDao.executeFinder(methodInvocation.getMethod(), arguments);<br />        } else {<br />            return methodInvocation.proceed();<br />        }<br />    }<br /><br />    public boolean implementsInterface(Class intf) {<br />        return intf.isInterface() &amp;&amp; FinderExecutor.class.isAssignableFrom(intf);<br />    }<br />}<br /></pre>
				</blockquote>
				<h3>executeFinder() 方法</h3>
				<p>上面的代码唯一缺的就是executeFinder的实现。这个代码观察被调用的类的名字和方法，并且将他们与Hibernate的查询名相匹配。
你可以使用一个FinderNamingStrategy来激活其他方式的命名查询。默认的实现查找一个名为
“ClassName.methodName”的查询，ClassName是除包名之外的类名。
</p>
				<blockquote>
						<pre>public List&lt;T&gt; executeFinder(Method method, final Object[] queryArgs) {<br />     final String queryName = queryNameFromMethod(method);<br />     final Query namedQuery = getSession().getNamedQuery(queryName);<br />     String[] namedParameters = namedQuery.getNamedParameters();<br />     for(int i = 0; i &lt; queryArgs.length; i++) {<br />             Object arg = queryArgs[i];<br />             Type argType =  namedQuery.setParameter(i, arg);<br />      }<br />      return (List&lt;T&gt;) namedQuery.list();<br />}<br /><br />public String queryNameFromMethod(Method finderMethod) {<br />     return type.getSimpleName() + "." + finderMethod.getName();<br />}<br /></pre>
				</blockquote>
				<h2>总结</h2>
				<p>在Java 5之前，Java语言并不支持代码同时具有类型安全和范性的特性；你不得不二者选一。在这篇文章里，你可以看到使用Java
5范型支持并且结合Spring和Hibernate（和AOP）一起来提高生产力。一个范型类型安全的DAO类非常容易编写，所有你需要做的就是一个接
口，一些命名查询，并且10行Spring配置，并且可以极大的减少错误，同时节省时间。
</p>
				<p>代码下载：<a href="http://www.ibm.com/developerworks/views/download.jsp?contentid=111659&amp;filename=j-genericdao.zip&amp;method=http&amp;locale=worldwide">
j-genericdao.zip</a></p>
		</div>
<img src ="http://www.blogjava.net/baxiaopeng/aggbug/56104.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/baxiaopeng/" target="_blank">Koska</a> 2006-07-01 18:18 <a href="http://www.blogjava.net/baxiaopeng/articles/56104.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>结合struts和hibernate谈J2EE架构的数据表示</title><link>http://www.blogjava.net/baxiaopeng/articles/55757.html</link><dc:creator>Koska</dc:creator><author>Koska</author><pubDate>Thu, 29 Jun 2006 07:08:00 GMT</pubDate><guid>http://www.blogjava.net/baxiaopeng/articles/55757.html</guid><wfw:comment>http://www.blogjava.net/baxiaopeng/comments/55757.html</wfw:comment><comments>http://www.blogjava.net/baxiaopeng/articles/55757.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/baxiaopeng/comments/commentRss/55757.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/baxiaopeng/services/trackbacks/55757.html</trackback:ping><description><![CDATA[
		<span class="postbody">在 struts+ hibernate 这种结构中，是不应该把Hibernate产生的PO直接传递给JSP的，不管他是Iterator，还是List，这是一个设计错误。
<br /><br />
我来谈谈在J2EE架构中各层的数据表示方法：
<br /><br />
Web层的数据表示是FormBean，数据来源于HTML Form POST 
<br />
业务层的数据表示是VO
<br />
持久层的数据表示是PO，其数据来源于数据库，持久层的数据表示例如CMP
<br /><br />
在一个规范的J2EE架构中，不同层的数据表示应该被限制在层内，而不应该扩散到其它层，这样可以降低层间的耦合性，提高J2EE架构整体的可维护性和可
扩展性。比如说Web层的逻辑进行了修改，那么只需要修改FormBean的结构，而不需要触动业务层和持久层的代码修改。同样滴，当数据库表进行了小的
调整，那么也只需要修改持久层数据表示，而不需要触动业务层代码和Web层代码。
<br /><br />
不过由于Hibernate的强大功能，例如动态生成PO，PO的状态管理可以脱离Session，使得在应用了Hibernate的J2EE框架中，PO完全可以充当VO，因此我们下面把PO和VO合并，统称为PO。
<br /><br />
先来谈谈ActionFormBean和持久层的PO之间的重大区别。
<br /><br />在简单的应用中，ActionFormBean和PO几乎是没有区别，所以很多人干脆就是用ActionFormBean来充当PO，于是
ActionFormBean从JSP页面到Servlet控制层再到业务层，然后穿过持久层，最后一直映射到数据库表。真是一竿子捅到了底！
<br /><br />但是在复杂的应用中，ActionFormBean和PO是分离的，他们也不可能一样。ActionFormBean是和网页里面的Form表单
一一对应的，Form里面有什么元素，Bean里面就有什么属性。而PO和数据库表对应，因此如果数据库表不修改，那么PO也不会修改，如果页面的流程和
数据库表字段对应关系不一致，那么你又如何能够使用ActionFormBean来取代PO呢？
<br /><br />
比如说吧，用户注册页面要求注册用户的基本信息，因此HTML Form里面包含了基本信息属性，于是你需要一个ActionFormBean来一一对应(注意：是一一对应)，每个Bean属性对应一个文本框或者选择框什么的。
<br /><br />而用户这个持久对象呢？他的属性和ActionFormBean有什么明显不同呢？他会有一些ActionFormBean所没有的集合属性，比
如说用户的权限属性，用户的组属性，用户的帖子等等。另外还有可能的是在ActionFormBean里面有3个属性，分别是用户的First
Name, Middle Name, Last Name，而在我的User这个持久对象中就是一个 Name 对象属性。
<br /><br />
假设我的注册页面原来只要你提供First Name，那么ActionFormBean就这一个属性，后来我要你提供全名，你要改ActionFormBean，加两个属性。但是这个时候PO是不应该修改滴，因为数据库没有改。
<br /><br />
那么在一个完整的J2EE系统中应该如何进行合理的设计呢？
<br /><br />
JSP(View) ---&gt; ActionFormBean(Module) ---&gt; Action(Control)
<br /><br />ActionFormBean是Web层的数据表示，它和HTML页面Form对应，只要Web页面的操作流程发生改变，它就要相应的进行修改，
它不应该也不能被传递到业务层和持久层，否则一旦页面修改，会一直牵连到业务层和持久层的大面积的代码进行修改，对于软件的可维护性和可扩展性而言，是一
个灾难，Actiont就是他的边界，到此为止！
<br /><br />
Action(Web Control) ---&gt; Business Bean ---&gt; DAO ---&gt; ORM ---&gt;DB
<br /><br />
而PO则是业务层和持久层的数据表示，它在业务层和持久层之间进行流动，他不应该也不能被传递到Web层的View中去，而ActionServlet就是他的边界，到此为止！
<br /><br />
然后来看一看整个架构的流程：
<br /><br />当用户通过浏览器访问网页，提交了一个页面。于是Action拿到了这个FormBean，他会把FormBean属性读出来，然后构造一个PO
对象，再调用业务层的Bean类，完成了注册操作，重定向到成功页面。而业务层Bean收到这个PO对象之后，调用DAO接口方法，进行持久对象的持久化
操作。
<br /><br />当用户查询某个会员的信息的时候，他用全名进行查询，于是Action得到一个UserNameFormBean包括了3个属性，分别是
first name, middle name, last
name，然后Action把UserNameFormBean的3个属性读出来，构造Name对象，再调用业务Bean，把Name对象传递给业务
Bean，进行查询。
<br /><br />
业务Bean取得Name(注意: Name对象只是User的一个属性)对象之后调用DAO接口，返回一个User的PO对象，注意这个User不同于在Web层使用的UserFormBean，他有很多集合属性滴。然后业务Bean把User对象返回给Action。
<br /><br />
Action拿到User之后，把User的基本属性取出(集合属性如果不需要就免了)，构造UserFormBean，然后把UserFormBean  request.setAttribute(...)，然后重定向到查询结果页面。
<br /><br />
查询页面拿到request对象里面的ActionFormBean，自动调用tag显示之。
<br /><br />
总结：
<br /><br />FormBean是Web层的数据表示，他不能被传递到业务层；PO是持久层的数据表示，在特定情况下，例如Hibernate中，他可以取代
VO出现在业务层，但是不管PO还是VO都必须限制在业务层内使用，最多到达Web层的Control，绝不能被扩散到View去。
<br /><br />
FormBean和PO之间的数据转化是在Action中进行滴。
<br /><br />
BTW:
<br /><br />
JDO1.x还不能像Hibernate功能这样强大，PO不能脱离持久层，所以必须在业务层使用VO，因此必须在业务层进行大量的VO和PO的转化操作，相对于Hibernate来说，编程比较烦琐。
<br /><br />
当然咯，理论是一回事，实际操作也不一定非要这样干，你可以自行取舍，在实际项目中灵活一点，增加一点bad smell，提高开发效率。只不过在大型项目中最好还是严丝合缝，不然的话，改版的时候会痛苦的很滴。</span>
<img src ="http://www.blogjava.net/baxiaopeng/aggbug/55757.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/baxiaopeng/" target="_blank">Koska</a> 2006-06-29 15:08 <a href="http://www.blogjava.net/baxiaopeng/articles/55757.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate3的DetachedCriteria支持</title><link>http://www.blogjava.net/baxiaopeng/articles/55713.html</link><dc:creator>Koska</dc:creator><author>Koska</author><pubDate>Thu, 29 Jun 2006 03:54:00 GMT</pubDate><guid>http://www.blogjava.net/baxiaopeng/articles/55713.html</guid><wfw:comment>http://www.blogjava.net/baxiaopeng/comments/55713.html</wfw:comment><comments>http://www.blogjava.net/baxiaopeng/articles/55713.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/baxiaopeng/comments/commentRss/55713.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/baxiaopeng/services/trackbacks/55713.html</trackback:ping><description><![CDATA[Hibernate3支持DetachedCriteria，这是一个非常有意义的特性！我们知道，在常规的Web编程中，有大量的动态条件查询，即用户在网页上面<a class="bluekey" href="http://www.yesky.com/key/3583/168583.html" target="_blank">自由选择</a>某些条件，程序根据用户的选择条件，动态生成<a class="bluekey" href="http://www.yesky.com/key/2815/182815.html" target="_blank">SQL语句</a>，进行查询。 <br /><br />　　针对这种需求，对于分层应用程序来说，Web层需要传递一个查询的条件列表给业务层对象，业务层对象获得这个条件列表之后，然后依次取出条件，<a class="bluekey" href="http://www.yesky.com/key/4434/164434.html" target="_blank">构造</a>查询语句。这里的一个难点是条件列表用什么来构造？传统上使用Map，但是这种方式缺陷很大，Map可以传递的信息非常有限，只能传递name和value，无法传递究竟要做怎样的条件运算，究竟是大于，小于，like，还是其它的什么，业务层对象必须确切掌握每条entry的隐含条件。因此一旦隐含条件改变，业务层对象的查询构造算法必须相应修改，但是这种查询条件的改变是隐式约定的，而不是程序代码约束的，因此非常容易出错。 <br /><br />　　DetachedCriteria可以解决这个问题，即在web层，程序员使用DetachedCriteria来构造查询条件，然后将这个DetachedCriteria作为方法调用参数传递给业务层对象。而业务层对象获得DetachedCriteria之后，可以在session范围内直接构造Criteria，进行查询。就此，查询语句的构造完全被搬离到web层实现，而业务层则只负责完成持久化和查询的封装即可，与查询条件构造完全解耦，非常完美！这恐怕也是以前很多企图在web层代码中构造HQL语句的人想实现的<a class="bluekey" href="http://www.yesky.com/key/3419/173419.html" target="_blank">梦想</a>吧！ <br /><br />　　示例代码片段如下： <br /><br />　　web层程序构造查询条件： <br /><br />　　java代码: <br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Department.class); <br />detachedCriteria.add(Restrictions.<a class="bluekey" href="http://www.yesky.com/key/4213/169213.html" target="_blank">eq</a>("name", "department")).createAlias("employees", "e").add(Restrictions.gt(("e.age"), new Integer(20)));</td></tr></tbody></table><br />　　Department和Employee是一对多关联，查询条件为： <br /><br />　　名称是“department”开发部门； <br />　　部门里面的雇员年龄大于20岁； <br /><br />　　业务层对象使用该条件执行查询： <br /><br />　　java代码: <br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>detachedCriteria.getExecutableCriteria(session).list();</td></tr></tbody></table><br />　　最大的意义在于，业务层代码是固定不变的，所有查询条件的构造都在web层完成，业务层只负责在session内执行之。这样代码就可放之四海而皆准，都无须修改了。<br /><br />　　然而Spring和Hibernate3的DetachedCriteria有不兼容的问题，因此在Spring环境下面使用Hibernate3需要注意： <br /><br />　　Spring的HibernateTemplate提供了Hibernate的完美封装，即通过匿名类实现回调，来保证Session的自动资源管理和事务的管理。其中核心方法是： <br /><br />　　java代码: <br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>HibernateTemplate.execute(new HibernateCallback() { <br />　public Object doInHibernate(Session session) throws HibernateException { <br />　　.... <br />　} <br />}</td></tr></tbody></table><br />　　回调方法提供了session作为参数，有了session，就可以自由的使用Hibernate API编程了。使用了spring的之后，代码修改如下： <br /><b><br /></b>　　web层代码： <br /><br />　　java代码: <br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Department.class); <br />detachedCriteria.createAlias("employees", "e").add(Restrictions.eq("name", "department")).add(Restrictions.gt(("e.age"), new Integer(20))); <br />departmentManager.findByCriteria(detachedCriteria);</td></tr></tbody></table><br />　　构造detachedCriteria，作为参数传递给departmentManager <br /><br />　　业务层代码使用spring，DepartmentManager的findByCriteria如下： <br /><br />　　java代码: <br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>public List findByCriteria(final DetachedCriteria detachedCriteria) { <br />　return (List) getHibernateTemplate().execute(new HibernateCallback() { <br />　　public Object doInHibernate(Session session) throws HibernateException { <br />　　　Criteria criteria = detachedCriteria.getExecutableCriteria(session); <br />　　　return criteria.list(); <br />　　} <br />　}); <br />}</td></tr></tbody></table><br />　　实际上也就是： <br /><br />　　java代码: <br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>Criteria criteria = detachedCriteria.getExecutableCriteria(session); <br />return criteria.list(); </td></tr></tbody></table><br />　　而已 <br /><br />　　但是该程序代码执行，会抛出强制类型转换异常！ <br /><br />　　我跟踪了一下spring和Hibernate源代码，原因如下： <br /><br />　　spring的HibernateTemplate的execute方法提供的回调接口具有Session作为参数，但是实际上，默认情况下，HibernateTemplate传递给回调接口的session并不是org.hibernate.impl.SessionImpl类，而是SessionImpl类的一个Proxy类。之所以替换成为一个Proxy类，HibernateTemplate的注释说明，Proxy提供了一些额外的功能，包括自动设置Cachable，Transaction的超时时间，Session资源的更积极的关闭等等。 <br /><br />　　java代码: <br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>private boolean exposeNativeSession = false; <br />... </td></tr></tbody></table><br />　　execute方法内部： <br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>Session sessionToExpose = (exposeNativeSession ? session : createSessionProxy(session));</td></tr></tbody></table><br />　　但是遗憾的是，Hibernate的DetachedCriteria的setExecutableCriteria方法却要求将session参数强制转为SessionImpl，但是spring传过来的却是一个Proxy类，因此就报错了。 <br /><br />　　java代码: <br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>public Criteria getExecutableCriteria(Session session) { <br />　impl.setSession( (SessionImpl) session ); // 要求SessionImpl，Spring传递的是Proxy <br />　return impl; <br />}</td></tr></tbody></table><br />　　解决方法，禁止Spring的HibernateTemplate传递Proxy类，强制要求它传递真实的SessionImpl类，即给exexute方法增加一个参数，提供参数为true，如下： <br /><br />　　java代码: <br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>public List findByCriteria(final DetachedCriteria detachedCriteria) { <br />　return (List) getHibernateTemplate().execute(new HibernateCallback() { <br />　　public Object doInHibernate(Session session) throws HibernateException { <br />　　　Criteria criteria = detachedCriteria.getExecutableCriteria(session); <br />　　　return criteria.list(); <br />　　} <br />　}, true); <br />}</td></tr></tbody></table><img src ="http://www.blogjava.net/baxiaopeng/aggbug/55713.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/baxiaopeng/" target="_blank">Koska</a> 2006-06-29 11:54 <a href="http://www.blogjava.net/baxiaopeng/articles/55713.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>inverse和cascade在关联更新中的作用</title><link>http://www.blogjava.net/baxiaopeng/articles/53018.html</link><dc:creator>Koska</dc:creator><author>Koska</author><pubDate>Thu, 15 Jun 2006 08:07:00 GMT</pubDate><guid>http://www.blogjava.net/baxiaopeng/articles/53018.html</guid><wfw:comment>http://www.blogjava.net/baxiaopeng/comments/53018.html</wfw:comment><comments>http://www.blogjava.net/baxiaopeng/articles/53018.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/baxiaopeng/comments/commentRss/53018.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/baxiaopeng/services/trackbacks/53018.html</trackback:ping><description><![CDATA[4. hibernate如何根据pojo来更新数据库<br /><br />4.0  在commit/flush之前，hibernate不会对pojo对象作神秘的处理。<br />4.0.1 在select查询出pojo时，hibernate根据“字段--属性”的对应关系，用字段的值填充pojo的属性；<br />然后根据“关系标记”生成sql语句从relationTable中查询出满足条件的relationPojo，并把这些relatinPojo<br />放到“关系属性”中。这个过程是机械的。<br /><br />4.0.2 在pojo对象被查出来后，到commit(或flush)之前，它将是一个普通的java对象，hibernate不会做额外的手脚。<br />比如，不会限制你设置一个属性的值为null或其它任何值<br />在集合类Set的add(object)操作时， 不会改变object的值，不会检查参数object是否是一个pojo对象<br />设置mainPojo的一个“桥属性”的值，不会自动设置relationPojo的对应的“桥属性”的值。<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的集合，修改“关系属性”的值能会导致更新mainTable表，也可能会更新relationTable表。<br /><br />这种更新暂叫“关联更新”。<br /><br /><br />4.1.inverse属性的作用（假定没有设置cascade属性）            <br />4.1.1 “只有集合标记（set/map/list/array/bag）才有inverse属性”。<br />————不妨以标记set为例，具体为“一个地区（Address表）的学校（School表）” -- address.schoolSet。<br /><br />4.1.2 “set的inverse属性决定是否把对set的改动反映到数据库中去。<br />inverse=false————反映；inverse=true————不反映”<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 />#“地区————学校”的关系为多对多的关系有点牵强，只是为了方便与上面的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 />只涉及到“桥字段”，不会考虑或改变其他的字段，所以对set的操作(6)是没有效果地。<br />extend:对list,可能还会维护index字段。<br /><br />4.1.4 “inverse与cascade没有什么关系，互无牵扯。”<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属性默认会进行“关联更新”。<br /><br />4.1.6 建议：只对set + many-to-many设置inverse=false，其他的标记不考虑inverse属性。<br />糟糕的是，不设置inverse属性时，inverse默认为false。<br /><br />4.2. 级联（cascade）属性的作用：        <br />4.2.1 只有“关系标记”才有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和它的关系属性的关系就是“主控方 -- 被动方”的关系，如果关系属性是一个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可能触发多个关联操作。前一个操作叫“主控操作”，后一个操作叫“关联操作”。<br />cascade属性的可选值：<br />all : 所有情况下均进行关联操作。<br />none：所有情况下均不进行关联操作。这是默认值。<br />save-update:在执行save/update/saveOrUpdate时进行关联操作。<br />delete：在执行delete时进行关联操作。        <br /><br />具体执行什么“关联操作”是根据“主控操作”来的：<br />“主控操作”                 “关联操作”<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 主控操作和关联操作的先后顺序是“先保存one，再保存many；先删除many，再删除one；先update主控方，再update被动方”<br />对于one-to-one，当其属性constrained="false"（默认值）时，它可看作one-to-many关系；<br />当其属性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属性默认会进行“关联更新”。<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 /><br />对cascade，一般对many-to-one，many-to-many，constrained=true的one-to-one 不设置级联删除。<br /><br /><img src ="http://www.blogjava.net/baxiaopeng/aggbug/53018.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/baxiaopeng/" target="_blank">Koska</a> 2006-06-15 16:07 <a href="http://www.blogjava.net/baxiaopeng/articles/53018.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个简单的复合主键的做关联类的例子</title><link>http://www.blogjava.net/baxiaopeng/articles/52610.html</link><dc:creator>Koska</dc:creator><author>Koska</author><pubDate>Tue, 13 Jun 2006 17:18:00 GMT</pubDate><guid>http://www.blogjava.net/baxiaopeng/articles/52610.html</guid><wfw:comment>http://www.blogjava.net/baxiaopeng/comments/52610.html</wfw:comment><comments>http://www.blogjava.net/baxiaopeng/articles/52610.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/baxiaopeng/comments/commentRss/52610.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/baxiaopeng/services/trackbacks/52610.html</trackback:ping><description><![CDATA[
		<span class="postbody">
场景是这样的：
<br /><br />
用户类User，物品类Goods，每次记录用户使用物品的情况，情况包括谁在什么时间借了什么物品。其中有一个约束条件就是用户只能对同一物品使用一
次。使用记录类为Record类。我们可以看出User对Record是1:n多的关系，Record对Goods是n:1的关系，而User和
Goods之间没有之间的关系。
<br /><br />
RecordId类是复合主键类，分别以n:1关联User类，n:1关联Goods类。RecordId类需要实现equals方法，需要实现Serializable。而Record类用RecordId来做主键。
<br /><br />
类定义如下：
<br /><br /></span>
		<table align="center" border="0" cellpadding="3" cellspacing="1" width="90%">
				<tbody>
						<tr>
								<td>
										<span class="genmed">
												<b>java代码: </b>
										</span>
								</td>
						</tr>
						<tr>
								<td class="code">
										<div style="font-family: 'Courier New',Courier,monospace;">
												<br />
												<br />
												<span style="color: rgb(102, 102, 255);">/*
<br />
 * Created on 2004-10-20
<br />
 *
<br />
 */</span>
												<br />
												<span style="color: rgb(153, 0, 102); font-weight: bold;">package</span> com.<span style="color: rgb(0, 0, 0);">javaeye</span>;
<br /><br /><span style="color: rgb(153, 0, 102); font-weight: bold;">import</span> java.<span style="color: rgb(0, 0, 0);">util</span>.<span style="color: rgb(0, 0, 0);">Calendar</span>;
<br /><span style="color: rgb(153, 0, 102); font-weight: bold;">import</span> java.<span style="color: rgb(0, 0, 0);">util</span>.<span style="color: rgb(0, 0, 0);">HashSet</span>;
<br /><span style="color: rgb(153, 0, 102); font-weight: bold;">import</span> java.<span style="color: rgb(0, 0, 0);">util</span>.<span style="color: rgb(0, 0, 0);">Set</span>;
<br /><br /><span style="color: rgb(102, 102, 255);">/**
<br />
 * @author robbin
<br />
 * 
<br />
 */</span><br /><span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(153, 0, 102); font-weight: bold;">class</span> User <span style="color: rgb(0, 0, 0);">{</span><br /><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">private</span><span style="color: rgb(170, 170, 221);">Long</span> id;
<br /><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">private</span><span style="color: rgb(170, 170, 221);">String</span> name;
<br /><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">private</span><span style="color: rgb(170, 170, 221);">Set</span> usingRecords = <span style="color: rgb(153, 0, 102); font-weight: bold;">new</span><span style="color: rgb(170, 170, 221);">HashSet</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br /><br />
    <span style="color: rgb(102, 102, 255);">/**
<br />
     * @return Returns the id.
<br />
     */</span><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(170, 170, 221);">Long</span> getId<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        <span style="color: rgb(153, 0, 102); font-weight: bold;">return</span> id;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><br />
    <span style="color: rgb(102, 102, 255);">/**
<br />
     * @param id
<br />
     *            The id to set.
<br />
     */</span><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(153, 0, 102); font-weight: bold;">void</span> setId<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(170, 170, 221);">Long</span> id<span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        this.<span style="color: rgb(0, 0, 0);">id</span> = id;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><br />
    <span style="color: rgb(102, 102, 255);">/**
<br />
     * @return Returns the name.
<br />
     */</span><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(170, 170, 221);">String</span> getName<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        <span style="color: rgb(153, 0, 102); font-weight: bold;">return</span> name;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><br />
    <span style="color: rgb(102, 102, 255);">/**
<br />
     * @param name
<br />
     *            The name to set.
<br />
     */</span><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(153, 0, 102); font-weight: bold;">void</span> setName<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(170, 170, 221);">String</span> name<span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        this.<span style="color: rgb(0, 0, 0);">name</span> = name;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><br />
    <span style="color: rgb(102, 102, 255);">/**
<br />
     * @return Returns the usingRecords.
<br />
     */</span><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(170, 170, 221);">Set</span> getUsingRecords<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        <span style="color: rgb(153, 0, 102); font-weight: bold;">return</span> usingRecords;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><br />
    <span style="color: rgb(102, 102, 255);">/**
<br />
     * @param usingRecords
<br />
     *            The usingRecords to set.
<br />
     */</span><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(153, 0, 102); font-weight: bold;">void</span> setUsingRecords<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(170, 170, 221);">Set</span> usingRecords<span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        this.<span style="color: rgb(0, 0, 0);">usingRecords</span> = usingRecords;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(153, 0, 102); font-weight: bold;">void</span> useGoods<span style="color: rgb(0, 0, 0);">(</span>Goods goods<span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        RecordId id = <span style="color: rgb(153, 0, 102); font-weight: bold;">new</span> RecordId<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
        id.<span style="color: rgb(0, 0, 0);">setUser</span><span style="color: rgb(0, 0, 0);">(</span>this<span style="color: rgb(0, 0, 0);">)</span>;
<br />
        id.<span style="color: rgb(0, 0, 0);">setGoods</span><span style="color: rgb(0, 0, 0);">(</span>goods<span style="color: rgb(0, 0, 0);">)</span>;
<br />
        Record record = <span style="color: rgb(153, 0, 102); font-weight: bold;">new</span> Record<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
        record.<span style="color: rgb(0, 0, 0);">setRecordId</span><span style="color: rgb(0, 0, 0);">(</span>id<span style="color: rgb(0, 0, 0);">)</span>;
<br />
        record.<span style="color: rgb(0, 0, 0);">setRecordTime</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(170, 170, 221);">Calendar</span>.<span style="color: rgb(0, 0, 0);">getInstance</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
        usingRecords.<span style="color: rgb(0, 0, 0);">add</span><span style="color: rgb(0, 0, 0);">(</span>record<span style="color: rgb(0, 0, 0);">)</span>;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(153, 0, 102); font-weight: bold;">void</span> removeRecord<span style="color: rgb(0, 0, 0);">(</span>Record record<span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        usingRecords.<span style="color: rgb(0, 0, 0);">remove</span><span style="color: rgb(0, 0, 0);">(</span>record<span style="color: rgb(0, 0, 0);">)</span>;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><span style="color: rgb(0, 0, 0);">}</span></div>
										<br />
								</td>
						</tr>
				</tbody>
		</table>
		<span class="postbody">
				<br />
				<br />
		</span>
		<table align="center" border="0" cellpadding="3" cellspacing="1" width="90%">
				<tbody>
						<tr>
								<td>
										<span class="genmed">
												<b>java代码: </b>
										</span>
								</td>
						</tr>
						<tr>
								<td class="code">
										<div style="font-family: 'Courier New',Courier,monospace;">
												<br />
												<br />
												<br />
												<span style="color: rgb(102, 102, 255);">/*
<br />
 * Created on 2004-10-20
<br />
 *
<br />
 */</span>
												<br />
												<span style="color: rgb(153, 0, 102); font-weight: bold;">package</span> com.<span style="color: rgb(0, 0, 0);">javaeye</span>;
<br /><br /><span style="color: rgb(153, 0, 102); font-weight: bold;">import</span> java.<span style="color: rgb(0, 0, 0);">util</span>.<span style="color: rgb(0, 0, 0);">HashSet</span>;
<br /><span style="color: rgb(153, 0, 102); font-weight: bold;">import</span> java.<span style="color: rgb(0, 0, 0);">util</span>.<span style="color: rgb(0, 0, 0);">Set</span>;
<br /><br /><span style="color: rgb(102, 102, 255);">/**
<br />
 * @author robbin
<br />
 * 
<br />
 */</span><br /><span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(153, 0, 102); font-weight: bold;">class</span> Goods <span style="color: rgb(0, 0, 0);">{</span><br /><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">private</span><span style="color: rgb(170, 170, 221);">Long</span> id;
<br /><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">private</span><span style="color: rgb(170, 170, 221);">String</span> name;
<br /><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">private</span><span style="color: rgb(170, 170, 221);">Set</span> usedRecords = <span style="color: rgb(153, 0, 102); font-weight: bold;">new</span><span style="color: rgb(170, 170, 221);">HashSet</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br /><br />
    <span style="color: rgb(102, 102, 255);">/**
<br />
     * @return Returns the id.
<br />
     */</span><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(170, 170, 221);">Long</span> getId<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        <span style="color: rgb(153, 0, 102); font-weight: bold;">return</span> id;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><br />
    <span style="color: rgb(102, 102, 255);">/**
<br />
     * @param id
<br />
     *            The id to set.
<br />
     */</span><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(153, 0, 102); font-weight: bold;">void</span> setId<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(170, 170, 221);">Long</span> id<span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        this.<span style="color: rgb(0, 0, 0);">id</span> = id;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><br />
    <span style="color: rgb(102, 102, 255);">/**
<br />
     * @return Returns the name.
<br />
     */</span><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(170, 170, 221);">String</span> getName<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        <span style="color: rgb(153, 0, 102); font-weight: bold;">return</span> name;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><br />
    <span style="color: rgb(102, 102, 255);">/**
<br />
     * @param name
<br />
     *            The name to set.
<br />
     */</span><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(153, 0, 102); font-weight: bold;">void</span> setName<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(170, 170, 221);">String</span> name<span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        this.<span style="color: rgb(0, 0, 0);">name</span> = name;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><br />
    <span style="color: rgb(102, 102, 255);">/**
<br />
     * @return Returns the usedRecords.
<br />
     */</span><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(170, 170, 221);">Set</span> getUsedRecords<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        <span style="color: rgb(153, 0, 102); font-weight: bold;">return</span> usedRecords;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><br />
    <span style="color: rgb(102, 102, 255);">/**
<br />
     * @param usedRecords
<br />
     *            The usedRecords to set.
<br />
     */</span><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(153, 0, 102); font-weight: bold;">void</span> setUsedRecords<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(170, 170, 221);">Set</span> usedRecords<span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        this.<span style="color: rgb(0, 0, 0);">usedRecords</span> = usedRecords;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><br /><span style="color: rgb(0, 0, 0);">}</span></div>
										<br />
								</td>
						</tr>
				</tbody>
		</table>
		<span class="postbody">
				<br />
				<br />
		</span>
		<table align="center" border="0" cellpadding="3" cellspacing="1" width="90%">
				<tbody>
						<tr>
								<td>
										<span class="genmed">
												<b>java代码: </b>
										</span>
								</td>
						</tr>
						<tr>
								<td class="code">
										<div style="font-family: 'Courier New',Courier,monospace;">
												<br />
												<br />
												<span style="color: rgb(102, 102, 255);">/*
<br />
 * Created on 2004-10-20
<br />
 *
<br />
 */</span>
												<br />
												<span style="color: rgb(153, 0, 102); font-weight: bold;">package</span> com.<span style="color: rgb(0, 0, 0);">javaeye</span>;
<br /><br /><span style="color: rgb(153, 0, 102); font-weight: bold;">import</span> java.<span style="color: rgb(0, 0, 0);">io</span>.<span style="color: rgb(0, 0, 0);">Serializable</span>;
<br /><br /><span style="color: rgb(102, 102, 255);">/**
<br />
 * @author robbin
<br />
 * 
<br />
 */</span><br /><span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(153, 0, 102); font-weight: bold;">class</span> RecordId <span style="color: rgb(153, 0, 102); font-weight: bold;">implements</span><span style="color: rgb(170, 170, 221);">Serializable</span><span style="color: rgb(0, 0, 0);">{</span><br /><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">private</span> User user;
<br /><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">private</span> Goods goods;
<br /><br />
    <span style="color: rgb(102, 102, 255);">/**
<br />
     * @return Returns the goods.
<br />
     */</span><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span> Goods getGoods<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        <span style="color: rgb(153, 0, 102); font-weight: bold;">return</span> goods;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><br />
    <span style="color: rgb(102, 102, 255);">/**
<br />
     * @param goods
<br />
     *            The goods to set.
<br />
     */</span><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(153, 0, 102); font-weight: bold;">void</span> setGoods<span style="color: rgb(0, 0, 0);">(</span>Goods goods<span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        this.<span style="color: rgb(0, 0, 0);">goods</span> = goods;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><br />
    <span style="color: rgb(102, 102, 255);">/**
<br />
     * @return Returns the user.
<br />
     */</span><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span> User getUser<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        <span style="color: rgb(153, 0, 102); font-weight: bold;">return</span> user;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><br />
    <span style="color: rgb(102, 102, 255);">/**
<br />
     * @param user
<br />
     *            The user to set.
<br />
     */</span><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(153, 0, 102); font-weight: bold;">void</span> setUser<span style="color: rgb(0, 0, 0);">(</span>User user<span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        this.<span style="color: rgb(0, 0, 0);">user</span> = user;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(153, 0, 102); font-weight: bold;">boolean</span> equals<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(170, 170, 221);">Object</span> obj<span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        <span style="color: rgb(153, 0, 102); font-weight: bold;">return</span><span style="color: rgb(0, 0, 0);">(</span>obj instanceof RecordId<span style="color: rgb(0, 0, 0);">)</span><br />
                &amp;&amp; <span style="color: rgb(0, 0, 0);">(</span>this.<span style="color: rgb(0, 0, 0);">getUser</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>.<span style="color: rgb(0, 0, 0);">equals</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">(</span>RecordId<span style="color: rgb(0, 0, 0);">)</span> obj<span style="color: rgb(0, 0, 0);">)</span>.<span style="color: rgb(0, 0, 0);">getUser</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">)</span><br />
                &amp;&amp; <span style="color: rgb(0, 0, 0);">(</span>this.<span style="color: rgb(0, 0, 0);">getGoods</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>.<span style="color: rgb(0, 0, 0);">equals</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">(</span>RecordId<span style="color: rgb(0, 0, 0);">)</span> obj<span style="color: rgb(0, 0, 0);">)</span>.<span style="color: rgb(0, 0, 0);">getGoods</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(153, 0, 102); font-weight: bold;">int</span> hashCode<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        <span style="color: rgb(153, 0, 102); font-weight: bold;">return</span> this.<span style="color: rgb(0, 0, 0);">getUser</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>.<span style="color: rgb(0, 0, 0);">hashCode</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span> ^ this.<span style="color: rgb(0, 0, 0);">getGoods</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>.<span style="color: rgb(0, 0, 0);">hashCode</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><span style="color: rgb(0, 0, 0);">}</span></div>
										<br />
								</td>
						</tr>
				</tbody>
		</table>
		<span class="postbody">
				<br />
				<br />
		</span>
		<table align="center" border="0" cellpadding="3" cellspacing="1" width="90%">
				<tbody>
						<tr>
								<td>
										<span class="genmed">
												<b>java代码: </b>
										</span>
								</td>
						</tr>
						<tr>
								<td class="code">
										<div style="font-family: 'Courier New',Courier,monospace;">
												<br />
												<br />
												<span style="color: rgb(102, 102, 255);">/*
<br />
 * Created on 2004-10-20
<br />
 *
<br />
 */</span>
												<br />
												<span style="color: rgb(153, 0, 102); font-weight: bold;">package</span> com.<span style="color: rgb(0, 0, 0);">javaeye</span>;
<br /><br /><span style="color: rgb(153, 0, 102); font-weight: bold;">import</span> java.<span style="color: rgb(0, 0, 0);">util</span>.<span style="color: rgb(0, 0, 0);">Calendar</span>;
<br /><br /><span style="color: rgb(102, 102, 255);">/**
<br />
 * @author robbin
<br />
 * 
<br />
 */</span><br /><span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(153, 0, 102); font-weight: bold;">class</span> Record <span style="color: rgb(0, 0, 0);">{</span><br /><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">private</span> RecordId recordId;
<br /><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">private</span><span style="color: rgb(170, 170, 221);">Calendar</span> recordTime;
<br /><br />
    <span style="color: rgb(102, 102, 255);">/**
<br />
     * @return Returns the recordId.
<br />
     */</span><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span> RecordId getRecordId<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        <span style="color: rgb(153, 0, 102); font-weight: bold;">return</span> recordId;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><br />
    <span style="color: rgb(102, 102, 255);">/**
<br />
     * @param recordId
<br />
     *            The recordId to set.
<br />
     */</span><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(153, 0, 102); font-weight: bold;">void</span> setRecordId<span style="color: rgb(0, 0, 0);">(</span>RecordId recordId<span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        this.<span style="color: rgb(0, 0, 0);">recordId</span> = recordId;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><br />
    <span style="color: rgb(102, 102, 255);">/**
<br />
     * @return Returns the recordTime.
<br />
     */</span><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(170, 170, 221);">Calendar</span> getRecordTime<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        <span style="color: rgb(153, 0, 102); font-weight: bold;">return</span> recordTime;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><br />
    <span style="color: rgb(102, 102, 255);">/**
<br />
     * @param recordTime
<br />
     *            The recordTime to set.
<br />
     */</span><br />
    <span style="color: rgb(153, 0, 102); font-weight: bold;">public</span><span style="color: rgb(153, 0, 102); font-weight: bold;">void</span> setRecordTime<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(170, 170, 221);">Calendar</span> recordTime<span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
        this.<span style="color: rgb(0, 0, 0);">recordTime</span> = recordTime;
<br />
    <span style="color: rgb(0, 0, 0);">}</span><br /><span style="color: rgb(0, 0, 0);">}<br /></span><span class="postbody">映射文件配置如下：
<br /><br /></span><table align="center" border="0" cellpadding="3" cellspacing="1" width="90%"><tbody><tr><td><span class="genmed"><b>xml代码: </b></span></td></tr><tr><td class="code"><div style="font-family: 'Courier New',Courier,monospace;"><br /><br /><span style="color: rgb(221, 187, 0);">&lt;</span>?xml version="1.0"?<span style="color: rgb(221, 187, 0);">&gt;</span><br /><span style="color: rgb(221, 187, 0);">&lt;</span>!DOCTYPE hibernate-mapping PUBLIC
<br />
        "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
<br />
        "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"<span style="color: rgb(221, 187, 0);">&gt;</span><br /><br /><span style="color: rgb(221, 187, 0);">&lt;</span>hibernate-mapping<span style="color: rgb(221, 187, 0);">&gt;</span><br />
        <span style="color: rgb(221, 187, 0);">&lt;</span>class name="com.javaeye.User"<span style="color: rgb(221, 187, 0);">&gt;</span><br />
                <span style="color: rgb(221, 187, 0);">&lt;</span>id name="id" unsaved-value="null"<span style="color: rgb(221, 187, 0);">&gt;</span><br />
                        <span style="color: rgb(221, 187, 0);">&lt;</span>generator class="native"/<span style="color: rgb(221, 187, 0);">&gt;</span><br />
                <span style="color: rgb(221, 187, 0);">&lt;</span>/id<span style="color: rgb(221, 187, 0);">&gt;</span><br />
               
<br />
                <span style="color: rgb(221, 187, 0);">&lt;</span>property name="name"/<span style="color: rgb(221, 187, 0);">&gt;</span><br />
               
<br />
                <span style="color: rgb(221, 187, 0);">&lt;</span>set name="usingRecords" inverse="true" lazy="true" cascade="all-delete-orphan"<span style="color: rgb(221, 187, 0);">&gt;</span><br />
                        <span style="color: rgb(221, 187, 0);">&lt;</span>key column="user_id" /<span style="color: rgb(221, 187, 0);">&gt;</span><br />
                        <span style="color: rgb(221, 187, 0);">&lt;</span>one-to-many class="com.javaeye.Record"/<span style="color: rgb(221, 187, 0);">&gt;</span><br />
                <span style="color: rgb(221, 187, 0);">&lt;</span>/set<span style="color: rgb(221, 187, 0);">&gt;</span><br />
        <span style="color: rgb(221, 187, 0);">&lt;</span>/class<span style="color: rgb(221, 187, 0);">&gt;</span><br />
       
<br /><span style="color: rgb(221, 187, 0);">&lt;</span>/hibernate-mapping<span style="color: rgb(221, 187, 0);">&gt;</span></div><br /></td></tr></tbody></table><span class="postbody"><br /><br /><br /></span><table align="center" border="0" cellpadding="3" cellspacing="1" width="90%"><tbody><tr><td><span class="genmed"><b>xml代码: </b></span></td></tr><tr><td class="code"><div style="font-family: 'Courier New',Courier,monospace;"><br /><span style="color: rgb(221, 187, 0);">&lt;</span>?xml version="1.0"?<span style="color: rgb(221, 187, 0);">&gt;</span><br /><span style="color: rgb(221, 187, 0);">&lt;</span>!DOCTYPE hibernate-mapping PUBLIC
<br />
        "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
<br />
        "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"<span style="color: rgb(221, 187, 0);">&gt;</span><br /><br /><span style="color: rgb(221, 187, 0);">&lt;</span>hibernate-mapping<span style="color: rgb(221, 187, 0);">&gt;</span><br />
        <span style="color: rgb(221, 187, 0);">&lt;</span>class name="com.javaeye.Goods"<span style="color: rgb(221, 187, 0);">&gt;</span><br />
                <span style="color: rgb(221, 187, 0);">&lt;</span>id name="id" unsaved-value="null"<span style="color: rgb(221, 187, 0);">&gt;</span><br />
                        <span style="color: rgb(221, 187, 0);">&lt;</span>generator class="native"/<span style="color: rgb(221, 187, 0);">&gt;</span><br />
                <span style="color: rgb(221, 187, 0);">&lt;</span>/id<span style="color: rgb(221, 187, 0);">&gt;</span><br />
               
<br />
                <span style="color: rgb(221, 187, 0);">&lt;</span>property name="name"/<span style="color: rgb(221, 187, 0);">&gt;</span><br />
               
<br />
                <span style="color: rgb(221, 187, 0);">&lt;</span>set name="usedRecords" inverse="false" lazy="true" cascade="all-delete-orphan"<span style="color: rgb(221, 187, 0);">&gt;</span><br />
                        <span style="color: rgb(221, 187, 0);">&lt;</span>key column="goods_id" /<span style="color: rgb(221, 187, 0);">&gt;</span><br />
                        <span style="color: rgb(221, 187, 0);">&lt;</span>one-to-many class="com.javaeye.Record"/<span style="color: rgb(221, 187, 0);">&gt;</span><br />
                <span style="color: rgb(221, 187, 0);">&lt;</span>/set<span style="color: rgb(221, 187, 0);">&gt;</span><br />
        <span style="color: rgb(221, 187, 0);">&lt;</span>/class<span style="color: rgb(221, 187, 0);">&gt;</span><br />
       
<br /><span style="color: rgb(221, 187, 0);">&lt;</span>/hibernate-mapping<span style="color: rgb(221, 187, 0);">&gt;</span><br /></div><br /></td></tr></tbody></table><span class="postbody"><br /><br /></span><table align="center" border="0" cellpadding="3" cellspacing="1" width="90%"><tbody><tr><td><span class="genmed"><b>xml代码: </b></span></td></tr><tr><td class="code"><div style="font-family: 'Courier New',Courier,monospace;"><br /><br /><span style="color: rgb(221, 187, 0);">&lt;</span>?xml version="1.0"?<span style="color: rgb(221, 187, 0);">&gt;</span><br /><span style="color: rgb(221, 187, 0);">&lt;</span>!DOCTYPE hibernate-mapping PUBLIC
<br />
        "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
<br />
        "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"<span style="color: rgb(221, 187, 0);">&gt;</span><br /><br /><span style="color: rgb(221, 187, 0);">&lt;</span>hibernate-mapping<span style="color: rgb(221, 187, 0);">&gt;</span><br />
        <span style="color: rgb(221, 187, 0);">&lt;</span>class name="com.javaeye.Record"<span style="color: rgb(221, 187, 0);">&gt;</span><br />
                <span style="color: rgb(221, 187, 0);">&lt;</span>composite-id name="recordId" class="com.javaeye.RecordId" unsaved-value="any" <span style="color: rgb(221, 187, 0);">&gt;</span><br />
                        <span style="color: rgb(221, 187, 0);">&lt;</span>key-many-to-one name="user" column="user_id" class="com.javaeye.User" /<span style="color: rgb(221, 187, 0);">&gt;</span><br />
                        <span style="color: rgb(221, 187, 0);">&lt;</span>key-many-to-one name="goods" column="goods_id" class="com.javaeye.Goods" /<span style="color: rgb(221, 187, 0);">&gt;</span><br />
                <span style="color: rgb(221, 187, 0);">&lt;</span>/composite-id<span style="color: rgb(221, 187, 0);">&gt;</span><br />
               
<br />
                <span style="color: rgb(221, 187, 0);">&lt;</span>property name="recordTime" type="calendar"/<span style="color: rgb(221, 187, 0);">&gt;</span><br /><br />
        <span style="color: rgb(221, 187, 0);">&lt;</span>/class<span style="color: rgb(221, 187, 0);">&gt;</span><br />
       
<br /><span style="color: rgb(221, 187, 0);">&lt;</span>/hibernate-mapping<span style="color: rgb(221, 187, 0);">&gt;</span></div></td></tr></tbody></table><span class="postbody">记录物品使用情况的代码书写如下：
<br /><br /></span><table align="center" border="0" cellpadding="3" cellspacing="1" width="90%"><tbody><tr><td><span class="genmed"><b>java代码: </b></span></td></tr><tr><td class="code"><div style="font-family: 'Courier New',Courier,monospace;"><br /><br />
Goods goods = <span style="color: rgb(153, 0, 102); font-weight: bold;">new</span> Goods<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
goods.<span style="color: rgb(0, 0, 0);">setName</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 255);">"book"</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
s.<span style="color: rgb(0, 0, 0);">save</span><span style="color: rgb(0, 0, 0);">(</span>goods<span style="color: rgb(0, 0, 0);">)</span>;
<br />
User user = <span style="color: rgb(153, 0, 102); font-weight: bold;">new</span> User<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
user.<span style="color: rgb(0, 0, 0);">setName</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 255);">"robbin"</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
s.<span style="color: rgb(0, 0, 0);">save</span><span style="color: rgb(0, 0, 0);">(</span>user<span style="color: rgb(0, 0, 0);">)</span>;
<br /><br /><span style="color: rgb(102, 102, 255);">// 用户使用物品</span><br />
user.<span style="color: rgb(0, 0, 0);">useGoods</span><span style="color: rgb(0, 0, 0);">(</span>goods<span style="color: rgb(0, 0, 0);">)</span>;
<br /></div><br /></td></tr></tbody></table><span class="postbody"><br /><br />
当用户重复使用物品的时候，调用userGoods方法，Hibernate会抛出主键重复的错误。
<br /><br />
判断用户是否使用某物品的办法如下：
<br /><br /></span><table align="center" border="0" cellpadding="3" cellspacing="1" width="90%"><tbody><tr><td><span class="genmed"><b>java代码: </b></span></td></tr><tr><td class="code"><div style="font-family: 'Courier New',Courier,monospace;"><br /><br />
RecordId id = <span style="color: rgb(153, 0, 102); font-weight: bold;">new</span> RecordId<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
id.<span style="color: rgb(0, 0, 0);">setUser</span><span style="color: rgb(0, 0, 0);">(</span>user<span style="color: rgb(0, 0, 0);">)</span>;
<br />
id.<span style="color: rgb(0, 0, 0);">setGoods</span><span style="color: rgb(0, 0, 0);">(</span>goods<span style="color: rgb(0, 0, 0);">)</span>;
<br /><br />
Record record = <span style="color: rgb(0, 0, 0);">(</span>Record<span style="color: rgb(0, 0, 0);">)</span> session.<span style="color: rgb(0, 0, 0);">get</span><span style="color: rgb(0, 0, 0);">(</span>Record.<span style="color: rgb(0, 0, 0);">class</span>, id<span style="color: rgb(0, 0, 0);">)</span>;
<br /><br /><span style="color: rgb(153, 0, 102); font-weight: bold;">if</span><span style="color: rgb(0, 0, 0);">(</span>record == <span style="color: rgb(153, 0, 102); font-weight: bold;">null</span><span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
    user.<span style="color: rgb(0, 0, 0);">usGoods</span><span style="color: rgb(0, 0, 0);">(</span>goods<span style="color: rgb(0, 0, 0);">)</span>;
<br /><span style="color: rgb(0, 0, 0);">}</span><span style="color: rgb(153, 0, 102); font-weight: bold;">else</span><span style="color: rgb(0, 0, 0);">{</span><br />
    throw <span style="color: rgb(153, 0, 102); font-weight: bold;">new</span> UsedGoodsException<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 255);">"..."</span><span style="color: rgb(0, 0, 0);">)</span>;
<br /><span style="color: rgb(0, 0, 0);">}</span><br /></div><br /></td></tr></tbody></table><span class="postbody"><br /><br />
然而需要指出的是，Gavin
King并不提倡使用composite-id，如果你不是基于已有的数据库编程，而是重新设计数据库结构，那么建议使用UserType。你可以自定义
一个UserType，包括User和Goods，并且在hbm中定义该UserType为unique的，同样可以达到目的。而这种方式的好处则是不需
要你来手工维护id，而由Hibernate自动维护。UserType的使用方法参考手册5.2.4节和Hibernate自带的示例中的
net.sf.hibernate.test.DoubleStringType。</span></div>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/baxiaopeng/aggbug/52610.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/baxiaopeng/" target="_blank">Koska</a> 2006-06-14 01:18 <a href="http://www.blogjava.net/baxiaopeng/articles/52610.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于unsaved-value</title><link>http://www.blogjava.net/baxiaopeng/articles/52609.html</link><dc:creator>Koska</dc:creator><author>Koska</author><pubDate>Tue, 13 Jun 2006 16:59:00 GMT</pubDate><guid>http://www.blogjava.net/baxiaopeng/articles/52609.html</guid><wfw:comment>http://www.blogjava.net/baxiaopeng/comments/52609.html</wfw:comment><comments>http://www.blogjava.net/baxiaopeng/articles/52609.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/baxiaopeng/comments/commentRss/52609.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/baxiaopeng/services/trackbacks/52609.html</trackback:ping><description><![CDATA[
		<span class="postbody">当你显式的使用session.save()或者session.update()操作一个对象的时候，
实际上是用不到unsaved-value的。某些情况下(父子表关联保存)，当你在程序中并没有显式的使用save或者update一个持久对象，那么
Hibernate需要判断被操作的对象究竟是一个已经持久化过的持久对象，是一个尚未被持久化过的内存临时对象。例如：
<br /><br /></span>
		<table align="center" border="0" cellpadding="3" cellspacing="1" width="90%">
				<tbody>
						<tr>
								<td>
										<span class="genmed">
												<b>java代码: </b>
										</span>
								</td>
						</tr>
						<tr>
								<td class="code">
										<div style="font-family: 'Courier New',Courier,monospace;">
												<br />
Session session = ...;
<br />
Transaction tx = ...;
<br /><br />
Parent parent = <span style="color: rgb(0, 0, 0);">(</span>Parent<span style="color: rgb(0, 0, 0);">)</span> session.<span style="color: rgb(0, 0, 0);">load</span><span style="color: rgb(0, 0, 0);">(</span>Parent.<span style="color: rgb(0, 0, 0);">class</span>, id<span style="color: rgb(0, 0, 0);">)</span>;
<br /><br />
Child child = <span style="color: rgb(153, 0, 102); font-weight: bold;">new</span> Child<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
child.<span style="color: rgb(0, 0, 0);">setParent</span><span style="color: rgb(0, 0, 0);">(</span>parent<span style="color: rgb(0, 0, 0);">)</span>;
<br />
child.<span style="color: rgb(0, 0, 0);">setName</span><span style="color: rgb(0, 0, 0);">(</span>"sun"<span style="color: rgb(0, 0, 0);">)</span>;
<br /><br />
parent.<span style="color: rgb(0, 0, 0);">addChild</span><span style="color: rgb(0, 0, 0);">(</span>child<span style="color: rgb(0, 0, 0);">)</span>;
<br />
s.<span style="color: rgb(0, 0, 0);">update</span><span style="color: rgb(0, 0, 0);">(</span>parent<span style="color: rgb(0, 0, 0);">)</span>;
<br /><br />
s.<span style="color: rgb(0, 0, 0);">flush</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
tx.<span style="color: rgb(0, 0, 0);">commit</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
s.<span style="color: rgb(0, 0, 0);">close</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;</div>
										<br />
								</td>
						</tr>
				</tbody>
		</table>
		<span class="postbody">
				<br />
				<br />在上例中，程序并没有显式的session.save(child);
那么Hibernate需要知道child究竟是一个临时对象，还是已经在数据库中有的持久对象。如果child是一个新创建的临时对象(本例中就是这种
情况)，那么Hibernate应该自动产生session.save(child)这样的操作，如果child是已经在数据库中有的持久对象，那么
Hibernate应该自动产生session.update(child)这样的操作。
<br /><br />因此我们需要暗示一下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。
<br /><br />
unsaved-value="null" (默认情况，适用于大多数对象类型主键 Integer/Long/String/...)
<br /><br />
当Hibernate取一下child的Id，取出来的是null(在上例中肯定取出来的是null)，和unsaved-value设定值相等，发送save(child)
<br /><br />
当Hibernate取一下child的id，取出来的不是null，那么和unsaved-value设定值不相等，发送update(child)
<br /><br />
例如下面的情况：
<br /><br /></span>
		<table align="center" border="0" cellpadding="3" cellspacing="1" width="90%">
				<tbody>
						<tr>
								<td>
										<span class="genmed">
												<b>java代码: </b>
										</span>
								</td>
						</tr>
						<tr>
								<td class="code">
										<div style="font-family: 'Courier New',Courier,monospace;">
												<br />
Session session = ...;
<br />
Transaction tx = ...;
<br /><br />
Parent parent = <span style="color: rgb(0, 0, 0);">(</span>Parent<span style="color: rgb(0, 0, 0);">)</span> session.<span style="color: rgb(0, 0, 0);">load</span><span style="color: rgb(0, 0, 0);">(</span>Parent.<span style="color: rgb(0, 0, 0);">class</span>, id<span style="color: rgb(0, 0, 0);">)</span>;
<br />
Child child = <span style="color: rgb(0, 0, 0);">(</span>Child<span style="color: rgb(0, 0, 0);">)</span> session.<span style="color: rgb(0, 0, 0);">load</span><span style="color: rgb(0, 0, 0);">(</span>Child.<span style="color: rgb(0, 0, 0);">class</span>, childId<span style="color: rgb(0, 0, 0);">)</span>;
<br /><br />
child.<span style="color: rgb(0, 0, 0);">setParent</span><span style="color: rgb(0, 0, 0);">(</span>parent<span style="color: rgb(0, 0, 0);">)</span>;
<br />
child.<span style="color: rgb(0, 0, 0);">setName</span><span style="color: rgb(0, 0, 0);">(</span>"sun"<span style="color: rgb(0, 0, 0);">)</span>;
<br /><br />
parent.<span style="color: rgb(0, 0, 0);">addChild</span><span style="color: rgb(0, 0, 0);">(</span>child<span style="color: rgb(0, 0, 0);">)</span>;
<br />
s.<span style="color: rgb(0, 0, 0);">update</span><span style="color: rgb(0, 0, 0);">(</span>parent<span style="color: rgb(0, 0, 0);">)</span>;
<br /><br />
s.<span style="color: rgb(0, 0, 0);">flush</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
tx.<span style="color: rgb(0, 0, 0);">commit</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
s.<span style="color: rgb(0, 0, 0);">close</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;</div>
										<br />
								</td>
						</tr>
				</tbody>
		</table>
		<span class="postbody">
				<br />
				<br />child已经在数据库中有了，是一个持久化的对象，不是新创建的，因此我们希望Hibernate发送update(child)，在该例中，
Hibernate取一下child.getId()，和unsave-value指定的null比对一下，发现不相等，那么发送update
(child)。
<br /><br />BTW:
parent对象不需要操心，因为程序显式的对parent有load操作和update的操作，不需要Hibernate自己来判断究竟是save还是
update了。我们要注意的只是child对象的操作。另外unsaved-value是定义在Child类的主键属性中的。
<br /><br /></span>
		<table align="center" border="0" cellpadding="3" cellspacing="1" width="90%">
				<tbody>
						<tr>
								<td>
										<span class="genmed">
												<b>java代码: </b>
										</span>
								</td>
						</tr>
						<tr>
								<td class="code">
										<div style="font-family: 'Courier New',Courier,monospace;">
												<br />
&lt;<span style="color: rgb(153, 0, 102); font-weight: bold;">class</span> name="Child" table="child"&gt;
<br />
&lt;id column="id" name="id" type="integer" unsaved-value="<span style="color: rgb(153, 0, 102); font-weight: bold;">null</span>"&gt;
<br />
  &lt;generator <span style="color: rgb(153, 0, 102); font-weight: bold;">class</span>="identity"/&gt;
<br />
&lt;/id&gt;
<br />
...
<br />
&lt;/<span style="color: rgb(153, 0, 102); font-weight: bold;">class</span>&gt;</div>
										<br />
								</td>
						</tr>
				</tbody>
		</table>
		<span class="postbody">
				<br />
				<br />
如果主键属性不是对象型，而是基本类型，如int/long/double/...，那么你需要指定一个数值型的unsaved-value，例如：
<br /><br /></span>
		<table align="center" border="0" cellpadding="3" cellspacing="1" width="90%">
				<tbody>
						<tr>
								<td>
										<span class="genmed">
												<b>java代码: </b>
										</span>
								</td>
						</tr>
						<tr>
								<td class="code">
										<div style="font-family: 'Courier New',Courier,monospace;">
												<br />
unsaved-<span style="color: rgb(153, 0, 102); font-weight: bold;">null</span>="<span style="color: rgb(0, 0, 0);">0</span>"</div>
										<br />
								</td>
						</tr>
				</tbody>
		</table>
		<span class="postbody">
				<br />
				<br />在此提醒大家，很多人以为对主键属性定义为int/long，比定义为Integer/Long运行效率来得高，认为基本类型不需要进行对象的封
装和解构操作，因此喜欢把主键定义为int/long的。但实际上，Hibernate内部总是把主键转换为对象型进行操作的，就算你定义为
int/long型的，Hibernate内部也要进行一次对象构造操作，返回给你的时候，还要进行解构操作，效率可能反而低也说不定。因此大家一定要扭
转一个观点，在Hibernate中，主键属性定义为基本类型，并不能够比定义为对象型效率来的高，而且也多了很多麻烦，因此建议大家使用对象型的
Integer/Long定义主键。
<br /><br />
unsaved-value="none"和
<br />
unsaved-value="any"
<br /><br />
主主要用在主键属性不是通过Hibernate生成，而是程序自己setId()的时候。
<br /><br />在这里多说一句，强烈建议使用Hibernate的id generator，或者你可以自己扩展Hibernate的id
generator，特别注意不要使用有实际含义的字段当做主键来用！例如用户类User，很多人喜欢用用户登陆名称做为主键，这是一个很不好的习惯，当
用户类和其他实体类有关联关系的时候，万一你需要修改用户登陆名称，一改就需要改好几张表中的数据。偶合性太高，而如果你使用无业务意义的id
generator，那么修改用户名称，就只修改user表就行了。
<br /><br />
由这个问题引申出来，如果你严格按照这个原则来设计数据库，那么你基本上是用不到手工来setId()的，你用Hibernate的id generator就OK了。因此你也不需要了解当
<br /><br />
unsaved-value="none"和
<br />
unsaved-value="any"
<br /><br />
究竟有什么含义了。如果你非要用assigned不可，那么继续解释一下：
<br /><br />
unsaved-value="none" 的时候，由于不论主键属性为任何值，都不可能为none，因此Hibernate总是对child对象发送update(child)
<br /><br />
unsaved-value="any" 的时候，由于不论主键属性为任何值，都肯定为any，因此Hibernate总是对child对象发送save(child)
<br /><br />
大多数情况下，你可以避免使用assigned，只有当你使用复合主键的时候不得不手工setId()，这时候需要你自己考虑究竟怎么设置unsaved-value了，根据你自己的需要来定。
<br /><br />
BTW: Gavin King强烈不建议使用composite-id，强烈建议使用UserType。
<br /><br />
因此，如果你在系统设计的时候，遵循如下原则：
<br /><br /><span style="color: red;">1、使用Hibernate的id generator来生成无业务意义的主键，不使用有业务含义的字段做主键，不使用assigned。
<br /><br />
2、使用对象类型(String/Integer/Long/...)来做主键，而不使用基础类型(int/long/...)做主键
<br /><br />
3、不使用composite-id来处理复合主键的情况，而使用UserType来处理该种情况。</span><br /><br />
那么你永远用的是unsaved-value="null" ，不可能用到any/none/..了。</span>
<img src ="http://www.blogjava.net/baxiaopeng/aggbug/52609.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/baxiaopeng/" target="_blank">Koska</a> 2006-06-14 00:59 <a href="http://www.blogjava.net/baxiaopeng/articles/52609.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>update和saveOrUpdate详解</title><link>http://www.blogjava.net/baxiaopeng/articles/52300.html</link><dc:creator>Koska</dc:creator><author>Koska</author><pubDate>Mon, 12 Jun 2006 15:55:00 GMT</pubDate><guid>http://www.blogjava.net/baxiaopeng/articles/52300.html</guid><wfw:comment>http://www.blogjava.net/baxiaopeng/comments/52300.html</wfw:comment><comments>http://www.blogjava.net/baxiaopeng/articles/52300.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/baxiaopeng/comments/commentRss/52300.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/baxiaopeng/services/trackbacks/52300.html</trackback:ping><description><![CDATA[
		<span class="postbody">转载自 javaeye 原作者 Robbin<br /><br />先来点概念：
<br /><br />
在Hibernate中，最核心的概念就是对PO的状态管理。一个PO有三种状态：
<br /><br />
1、未被持久化的VO
<br />
此时就是一个内存对象VO，由JVM管理生命周期
<br /><br />
2、已被持久化的PO，并且在Session生命周期内
<br />
此时映射数据库数据，由数据库管理生命周期
<br /><br />
3、曾被持久化过，但现在和Session已经detached了，以VO的身份在运行
<br />
这种和Session已经detached的PO还能够进入另一个Session，继续进行PO状态管理，此时它就成为PO的第二种状态了。<span style="color: red;">这种PO实际上是跨了Session进行了状态维护的。</span><br /><br />
在传统的JDO1.x中，PO只有前面两种状态，一个PO一旦脱离PM，就丧失了状态了，不再和数据库数据关联，成为一个纯粹的内存VO，它即使进入一个新的PM，也不能恢复它的状态了。
<br /><br />
Hibernate强的地方就在于，一个PO脱离Session之后，还能保持状态，再进入一个新的Session之后，就恢复状态管理的能力，但此时状
态管理需要使用session.update或者session.saveOrUpdate，这就是Hibernate
Reference中提到的“requires a slightly different programming model ”
<br /><br />
现在正式进入本话题：
<br /><br /><span style="color: red;">简单的来说，update和saveOrUpdate是用来对跨Session的PO进行状态管理的。</span><br /><br />
假设你的PO不需要跨Session的话，那么就不需要用到，例如你打开一个Session，对PO进行操作，然后关闭，之后这个PO你也不会再用到了，那么就不需要用update。
<br /><br />
因此，我们来看看：
<br /></span>
		<table align="center" border="0" cellpadding="3" cellspacing="1" width="90%">
				<tbody>
						<tr>
								<td>
										<span class="genmed">
												<b>java代码: </b>
										</span>
								</td>
						</tr>
						<tr>
								<td class="code">
										<div style="font-family: 'Courier New',Courier,monospace;">
												<br />
Foo foo=sess.<span style="color: rgb(0, 0, 0);">load</span><span style="color: rgb(0, 0, 0);">(</span>Foo.<span style="color: rgb(0, 0, 0);">class</span>,id<span style="color: rgb(0, 0, 0);">)</span>;
<br />
foo.<span style="color: rgb(0, 0, 0);">setXXX</span><span style="color: rgb(0, 0, 0);">(</span>xxx<span style="color: rgb(0, 0, 0);">)</span>;
<br />
sess.<span style="color: rgb(0, 0, 0);">flush</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
sess.<span style="color: rgb(0, 0, 0);">commit</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;</div>
										<br />
								</td>
						</tr>
				</tbody>
		</table>
		<span class="postbody">
				<br />
				<br />PO对象foo的操作都在一个Session生命周期内完成，因此不需要显式的进行sess.update(foo)这样的操作。
Hibernate会自动监测到foo对象已经被修改过，因此就向数据库发送一个update的sql。当然如果你非要加上sess.update
(foo)也不会错，只不过这样做没有任何必要。
<br /><br />
而跨Session的意思就是说这个PO对象在Session关闭之后，你还把它当做一个VO来用，后来你在Session外面又修改了它的属性，然后你又想打开一个Session，把VO的属性修改保存到数据库里面，那么你就需要用update了。
<br /><br /></span>
		<table align="center" border="0" cellpadding="3" cellspacing="1" width="90%">
				<tbody>
						<tr>
								<td>
										<span class="genmed">
												<b>java代码: </b>
										</span>
								</td>
						</tr>
						<tr>
								<td class="code">
										<div style="font-family: 'Courier New',Courier,monospace;">
												<br />
												<span style="color: rgb(102, 102, 255);">// in the first session </span>
												<br />
Cat cat = <span style="color: rgb(0, 0, 0);">(</span>Cat<span style="color: rgb(0, 0, 0);">)</span> firstSession.<span style="color: rgb(0, 0, 0);">load</span><span style="color: rgb(0, 0, 0);">(</span>Cat.<span style="color: rgb(0, 0, 0);">class</span>, catId<span style="color: rgb(0, 0, 0);">)</span>;
<br />
Cat potentialMate = <span style="color: rgb(153, 0, 102); font-weight: bold;">new</span> Cat<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
firstSession.<span style="color: rgb(0, 0, 0);">save</span><span style="color: rgb(0, 0, 0);">(</span>potentialMate<span style="color: rgb(0, 0, 0);">)</span>;
<br /><br /><span style="color: rgb(102, 102, 255);">// in a higher tier of the application </span><br />
cat.<span style="color: rgb(0, 0, 0);">setMate</span><span style="color: rgb(0, 0, 0);">(</span>potentialMate<span style="color: rgb(0, 0, 0);">)</span>;
<br /><br /><span style="color: rgb(102, 102, 255);">// later, in a new session </span><br />
secondSession.<span style="color: rgb(0, 0, 0);">update</span><span style="color: rgb(0, 0, 0);">(</span>cat<span style="color: rgb(0, 0, 0);">)</span>;  <span style="color: rgb(102, 102, 255);">// update cat </span><br />
secondSession.<span style="color: rgb(0, 0, 0);">update</span><span style="color: rgb(0, 0, 0);">(</span>mate<span style="color: rgb(0, 0, 0);">)</span>; <span style="color: rgb(102, 102, 255);">// update mate</span><br /></div>
										<br />
								</td>
						</tr>
				</tbody>
		</table>
		<span class="postbody">
				<br />
				<br />cat和mate对象是在第一个session中取得的，在第一个session关闭之后，他们就成了PO的第三种状态，和Session已经
detached的PO，此时他们的状态信息仍然被保留下来了。当他们进入第二个session之后，立刻就可以进行状态的更新。但是由于对cat的修改
操作：cat.setMate(potentialMate);
是在Session外面进行的，Hibernate不可能知道cat对象已经被改过了，第二个Session并不知道这种修改，因此一定要显式的调用
secondSession.update(cat); 通知Hibernate，cat对象已经修改了，你必须发送update的sql了。
<br /><br />
所以update的作用就在于此，它只会被用于当一个PO对象跨Session进行状态同步的时候才需要写。而一个PO对象当它不需要跨Session进行状态管理的时候，是不需要写update的。
<br /><br />
再谈谈saveOrUpdate的用场：
<br /><br />
saveOrUpdate和update的区别就在于在跨Session的PO状态管理中，Hibernate对PO采取何种策略。
<br /><br />
例如当你写一个DAOImpl的时候，让cat对象增加一个mate，如下定义：
<br /></span>
		<table align="center" border="0" cellpadding="3" cellspacing="1" width="90%">
				<tbody>
						<tr>
								<td>
										<span class="genmed">
												<b>java代码: </b>
										</span>
								</td>
						</tr>
						<tr>
								<td class="code">
										<div style="font-family: 'Courier New',Courier,monospace;">
												<br />
												<span style="color: rgb(153, 0, 102); font-weight: bold;">public</span>
												<span style="color: rgb(153, 0, 102); font-weight: bold;">void</span> addMate<span style="color: rgb(0, 0, 0);">(</span>Cat cat, Mate mate<span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
    Session session = ...;
<br />
    Transacton tx = ...;
<br />
    session.<span style="color: rgb(0, 0, 0);">update</span><span style="color: rgb(0, 0, 0);">(</span>cat<span style="color: rgb(0, 0, 0);">)</span>;
<br />
    cat.<span style="color: rgb(0, 0, 0);">addMate</span><span style="color: rgb(0, 0, 0);">(</span>mate<span style="color: rgb(0, 0, 0);">)</span>;
<br />
    tx.<span style="color: rgb(0, 0, 0);">commit</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
    session.<span style="color: rgb(0, 0, 0);">close</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br /><span style="color: rgb(0, 0, 0);">}</span>;</div>
										<br />
								</td>
						</tr>
				</tbody>
		</table>
		<span class="postbody">
				<br />
				<br />
显然你是需要把Hibernate的操作封装在DAO里面的，让业务层的程序员和Web层的程序员不需要了解Hibernate，直接对DAO进行调用。
<br /><br />此时问题就来了：上面的代码运行正确有一个必要的前提，那就是方法调用参数cat对象必须是一个已经被持久化过的PO，也就是来说，它应该首先从
数据库查询出来，然后才能这样用。但是业务层的程序员显然不知道这种内部的玄妙，如果他的业务是现在增加一个cat，然后再增加它的mate，他显然会这
样调用，new一个cat对象出来，然后就addMate：
<br /><br /></span>
		<table align="center" border="0" cellpadding="3" cellspacing="1" width="90%">
				<tbody>
						<tr>
								<td>
										<span class="genmed">
												<b>java代码: </b>
										</span>
								</td>
						</tr>
						<tr>
								<td class="code">
										<div style="font-family: 'Courier New',Courier,monospace;">
												<br />
Cat cat = <span style="color: rgb(153, 0, 102); font-weight: bold;">new</span> Cat<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
cat.<span style="color: rgb(0, 0, 0);">setXXX</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
daoimpl.<span style="color: rgb(0, 0, 0);">addMate</span><span style="color: rgb(0, 0, 0);">(</span>cat,mate<span style="color: rgb(0, 0, 0);">)</span>;</div>
										<br />
								</td>
						</tr>
				</tbody>
		</table>
		<span class="postbody">
				<br />
				<br />但是请注意看，这个cat对象只是一个VO，它没有被持久化过，它还不是PO，它没有资格调用addMate方法，因此调用addMate方法不
会真正往数据库里面发送update的sql，这个cat对象必须先被save到数据库，在真正成为一个PO之后，才具备addMate的资格。
<br /><br />
你必须这样来操作：
<br /><br /></span>
		<table align="center" border="0" cellpadding="3" cellspacing="1" width="90%">
				<tbody>
						<tr>
								<td>
										<span class="genmed">
												<b>java代码: </b>
										</span>
								</td>
						</tr>
						<tr>
								<td class="code">
										<div style="font-family: 'Courier New',Courier,monospace;">
												<br />
Cat cat = <span style="color: rgb(153, 0, 102); font-weight: bold;">new</span> Cat<span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
cat.<span style="color: rgb(0, 0, 0);">setXXX</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
daoimpl.<span style="color: rgb(0, 0, 0);">addCat</span><span style="color: rgb(0, 0, 0);">(</span>cat<span style="color: rgb(0, 0, 0);">)</span>;
<br />
daoimpl.<span style="color: rgb(0, 0, 0);">addMate</span><span style="color: rgb(0, 0, 0);">(</span>cat, mate<span style="color: rgb(0, 0, 0);">)</span>;</div>
										<br />
								</td>
						</tr>
				</tbody>
		</table>
		<span class="postbody">
				<br />
				<br />
先持久化cat，然后才能对cat进行其他的持久化操作。因此要求业务层的程序员必须清楚cat对象处于何种状态，到底是第一种，还是第三种。如果是第一种，就要先save，再addMate；如果是第三种，就直接addMate。
<br /><br />
但是最致命的是，如果整个软件分层很多，业务层的程序员他拿到这个cat对象也可能是上层Web应用层传递过来的cat，他自己也不知道这个cat究竟是VO，没有被持久化过，还是已经被持久化过，那么他根本就没有办法写程序了。
<br /><br />所以这样的DAOImpl显然是有问题的，它会对业务层的程序员造成很多编程上的陷阱，业务层的程序员必须深刻的了解他调用的每个DAO对PO对
象进行了何种状态管理，必须深刻的了解他的PO对象在任何时候处于什么确切的状态，才能保证编程的正确性，显然这是做不到的，但是有了
saveOrUpdate，这些问题就迎刃而解了。
<br /><br />
现在你需要修改addMate方法：
<br /><br /></span>
		<table align="center" border="0" cellpadding="3" cellspacing="1" width="90%">
				<tbody>
						<tr>
								<td>
										<span class="genmed">
												<b>java代码: </b>
										</span>
								</td>
						</tr>
						<tr>
								<td class="code">
										<div style="font-family: 'Courier New',Courier,monospace;">
												<br />
												<span style="color: rgb(153, 0, 102); font-weight: bold;">public</span>
												<span style="color: rgb(153, 0, 102); font-weight: bold;">void</span> addMate<span style="color: rgb(0, 0, 0);">(</span>Cat cat, Mate mate<span style="color: rgb(0, 0, 0);">)</span><span style="color: rgb(0, 0, 0);">{</span><br />
    Session session = ...;
<br />
    Transacton tx = ...;
<br />
    session.<span style="color: rgb(0, 0, 0);">saveOrUpdate</span><span style="color: rgb(0, 0, 0);">(</span>cat<span style="color: rgb(0, 0, 0);">)</span>;
<br />
    cat.<span style="color: rgb(0, 0, 0);">addMate</span><span style="color: rgb(0, 0, 0);">(</span>mate<span style="color: rgb(0, 0, 0);">)</span>;
<br />
    tx.<span style="color: rgb(0, 0, 0);">commit</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br />
    session.<span style="color: rgb(0, 0, 0);">close</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(0, 0, 0);">)</span>;
<br /><span style="color: rgb(0, 0, 0);">}</span>;</div>
										<br />
								</td>
						</tr>
				</tbody>
		</table>
		<span class="postbody">
				<br />
				<br />
如上，如果业务层的程序员传进来的是一个已经持久化过的PO对象，那么Hibernate会更新cat对象(假设业务层的程序员在Session外面修改过cat的属性)，如果传进来的是一个新new出来的对象，那么向数据库save这个PO对象。
<br /><br />
BTW: Hibernate此时究竟采取更新cat对象，还是save cat对象，取决于unsave-value的设定。
<br /><br />
这样，业务层的程序员就不必再操心PO的状态问题了，对于他们来说，不管cat是new出来的对象，只是一个VO也好；还是从数据库查询出来的的PO对象也好，全部都是直接addMate就OK了：
<br /><br /></span>
		<table align="center" border="0" cellpadding="3" cellspacing="1" width="90%">
				<tbody>
						<tr>
								<td>
										<span class="genmed">
												<b>java代码: </b>
										</span>
								</td>
						</tr>
						<tr>
								<td class="code">
										<div style="font-family: 'Courier New',Courier,monospace;">
												<br />
daoimple.<span style="color: rgb(0, 0, 0);">addMate</span><span style="color: rgb(0, 0, 0);">(</span>cat, mate<span style="color: rgb(0, 0, 0);">)</span>;</div>
										<br />
								</td>
						</tr>
				</tbody>
		</table>
		<span class="postbody">
				<br />
				<br />
这便是saveOrUpdate的作用。</span>
<img src ="http://www.blogjava.net/baxiaopeng/aggbug/52300.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/baxiaopeng/" target="_blank">Koska</a> 2006-06-12 23:55 <a href="http://www.blogjava.net/baxiaopeng/articles/52300.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>