﻿<?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-花香蝶自来-随笔分类-Hibernate</title><link>http://www.blogjava.net/ltc603/category/14100.html</link><description>&lt;font size="3"&gt;学无止境&lt;/font&gt;
&lt;br&gt;

&lt;script type="text/javascript" src="http://wujunlove.googlepages.com/bigstaticeyes.js"&gt;&lt;/script&gt;</description><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 21:18:19 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 21:18:19 GMT</pubDate><ttl>60</ttl><item><title>Hibernate配置文件在单元测试中的灵活运用</title><link>http://www.blogjava.net/ltc603/archive/2006/08/31/66778.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Thu, 31 Aug 2006 01:47:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2006/08/31/66778.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/66778.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2006/08/31/66778.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/66778.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/66778.html</trackback:ping><description><![CDATA[转载－<a href="http://blog.csdn.net/ufoer23/archive/2005/02/24/299619.aspx">http://blog.csdn.net/ufoer23/archive/2005/02/24/299619.aspx</a><br /><br /><p>2005 年 1 月 </p><blockquote>Hibernate 是一个流行的开源对象关系映射工具，单元测试和持续集成的重要性也得到了广泛的推广和认同，在采用了Hibernate的项目中如何保证测试的自动化和持续性呢？本文讨论了Hibernate加载其配置文件hibernate.properties和hibernate.cfg.xml的过程，以及怎么样将hibernate提供的配置文件的访问方法灵活运用到单元测试中。</blockquote><p><a name="IDA3RYV"><span class="atitle2">1 介绍</span></a><br />Hibernate 是一个流行的开源对象关系映射工具，单元测试和持续集成的重要性也得到了广泛的推广和认同，在采用了Hibernate的项目中如何保证测试的自动化和持续性呢？本文讨论了Hibernate加载其配置文件hibernate.properties和hibernate.cfg.xml的过程，以及怎么样将hibernate提供的配置文件的访问方法灵活运用到单元测试中。注意：本文以hibernate2.1作为讨论的基础，不保证本文的观点适合于其他版本。</p><p><a name="IDADSYV"><span class="atitle2">2 读者</span></a><br />Java开发人员，要求熟悉JUnit和掌握Hibernate的基础知识</p><p><a name="IDAJSYV"><span class="atitle2">3 内容</span></a><br /></p><p><a name="IDAOSYV"><span class="atitle3">3.1.准备</span></a><br />对于hibernate的初学者来说，第一次使用hibernate的经验通常是：</p><p>1， 安装配置好Hibernate，我们后面将%HIBERNATE_HOME%作为对Hibernate安装目录的引用，</p><p>2， 开始创建好自己的第一个例子，例如hibernate手册里面的类Cat，</p><p>3， 配置好hbm映射文件（例如Cat.hbm.xml，本文不讨论这个文件内配置项的含义）和数据库（如hsqldb），</p><p>4， 在项目的classpath路径下添加一个hibernate.cfg.xml文件，如下(第一次使用hibernate最常见的配置内容)：</p><a name="IDAYSYV"><b></b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;!DOCTYPE hibernate-configuration
    PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd"&gt;

&lt;hibernate-configuration&gt;
&lt;session-factory&gt;

&lt;property name="connection.url"&gt;jdbc:hsqldb:hsql://localhost&lt;/property&gt;
&lt;property name="connection.driver_class"&gt;org.hsqldb.jdbcDriver&lt;/property&gt;
&lt;property name="connection.username"&gt;sa&lt;/property&gt;
&lt;property name="connection.password"&gt;&lt;/property&gt;
&lt;property name="dialect"&gt;net.sf.hibernate.dialect.HSQLDialect&lt;/property&gt;

        &lt;property name="hibernate.show_sql"&gt;false&lt;/property&gt;

        &lt;mapping resource="Cat.hbm.xml"/&gt;

&lt;/session-factory&gt;
&lt;/hibernate-configuration&gt;
</code></pre></td></tr></tbody></table><p>5， 然后还需要提供一个类来测试一下创建，更新，删除和查询Cat，对于熟悉JUnit的开发人员，可以创建一个单元测试类来进行测试，如下：</p><a name="IDAATYV"><b></b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
import junit.framework.TestCase;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.Transaction;
import net.sf.hibernate.cfg.Configuration;


public class CatTest extends TestCase {

    private Session session;
    private Transaction tx;

    protected void setUp() throws Exception {
        Configuration cfg = new Configuration().configure();////注意这一行，这是本文重点讨论研究的地方。
        session = cfg.buildSessionFactory().openSession();
        tx = session.beginTransaction();
    }

    protected void tearDown() throws Exception {
  tx.commit();
  session.close();
    }
    
    public void testCreate()  {
     //请在此方法内添加相关的代码，本文不讨论怎么样使用Hibernate API。
    }
    public void testUpdate()  {
     //请在此方法内添加相关的代码，本文不讨论怎么样使用Hibernate API。
    }
    public void testDelete()  {
     //请在此方法内添加相关的代码，本文不讨论怎么样使用Hibernate API。
    }
    public void testQuery()  {
     //请在此方法内添加相关的代码，本文不讨论怎么样使用Hibernate API。
    }

}
</code></pre></td></tr></tbody></table><p><a name="IDAHTYV"><span class="atitle3">3.2 new Configuration()都做了什么？</span></a><br />对于第一次使用hibernate的新手来说，下面的这段代码可以说是最常见的使用Configuration方式。</p><a name="IDANTYV"><b></b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
Configuration cfg = new Configuration().configure();
</code></pre></td></tr></tbody></table><p>Configuration是hibernate的入口，在新建一个Configuration的实例的时候，hibernate会在classpath里面查找hibernate.properties文件，如果该文件存在，则将该文件的内容加载到一个Properties的实例GLOBAL_PROPERTIES里面，如果不存在，将打印信息</p><a name="IDAVTYV"><b></b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
hibernate.properties not found
</code></pre></td></tr></tbody></table><p>然后是将所有系统环境变量（System.getProperties()）也添加到GLOBAL_PROPERTIES里面（<a href="http://www-900.ibm.com/developerWorks/cn/java/j-hibernate-ut/index.shtml#1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">注1</a>）。如果hibernate.properties文件存在，系统还会验证一下这个文件配置的有效性，对于一些已经不支持的配置参数，系统将打印警告信息。</p><p><a name="IDACUYV"><span class="atitle3">3.3. configure()在做什么？</span></a><br />new Configuration()讨论至此，下面讨论configure()方法。</p><p>configure()方法默认会在classpath下面寻找hibernate.cfg.xml文件，如果没有找到该文件，系统会打印如下信息并抛出HibernateException异常。</p><a name="IDAJUYV"><b></b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
hibernate.cfg.xml not found
</code></pre></td></tr></tbody></table><p>如果找到该文件，configure()方法会首先访问&lt; session-factory &gt;，并获取该元素的name属性，如果非空，将用这个配置的值来覆盖hibernate.properties的hibernate.session_factory_name的配置的值，从这里我们可以看出，hibernate.cfg.xml里面的配置信息可以覆盖hibernate.properties的配置信息。</p><p>接着configure()方法访问&lt;session-factory&gt;的子元素，首先将使用所有的&lt;property&gt;元素配置的信息（<a href="http://www-900.ibm.com/developerWorks/cn/java/j-hibernate-ut/index.shtml#2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">注2</a>），如前面我们使用的配置文件</p><a name="IDAXUYV"><b></b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
    &lt;property name="connection.url"&gt;jdbc:hsqldb:hsql://localhost&lt;/property&gt;
  &lt;property name="connection.driver_class"&gt;org.hsqldb.jdbcDriver&lt;/property&gt;
  &lt;property name="connection.username"&gt;sa&lt;/property&gt;
  &lt;property name="connection.password"&gt;&lt;/property&gt;
  &lt;property name="dialect"&gt;net.sf.hibernate.dialect.HSQLDialect&lt;/property&gt;
  </code></pre></td></tr></tbody></table><p>会覆盖hibernate.properties里面对应的配置，hibernate2.1发布包里面自带的hibernate.properties文件（位于%HIBERNATE_HOME%/etc下面）里面的值，如下：</p><a name="IDA5UYV"><b></b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
hibernate.dialect net.sf.hibernate.dialect.HSQLDialect
hibernate.connection.driver_class org.hsqldb.jdbcDriver
hibernate.connection.username sa
hibernate.connection.password
hibernate.connection.url jdbc:hsqldb:hsql://localhost
</code></pre></td></tr></tbody></table><p>然后configure()会顺序访问以下几个元素的内容</p><a name="IDAHVYV"><b></b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
&lt;mapping&gt;
&lt;jcs-class-cache&gt;
&lt;jcs-collection-cache&gt;
&lt;collection-cache&gt;
</code></pre></td></tr></tbody></table><p>其中&lt;mapping&gt;是必不可少的，必须通过配置&lt;mapping&gt;，configure()才能访问到我们定义的java对象和关系数据库表的映射文件（hbm.xml），例如：</p><a name="IDAPVYV"><b></b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
&lt;mapping resource="Cat.hbm.xml"/&gt;
</code></pre></td></tr></tbody></table><p>通过以上的分析，我们对hibernate配置文件hibernate.properties和hibernate.cfg.xml的默认的加载过程就比较清楚了。</p><p><a name="IDAXVYV"><span class="atitle3">3.4 Configuration的其他用法</span></a><br />Configuration的configure ()方法还支持带参数的访问方式，你可以指定hbm.xml文件的位置，而不是使用默认的classpath下面的hibernate.cfg.xml这种方式，例如：</p><a name="IDA3VYV"><b></b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
Configuration cfg = new Configuration().configure("myexample.xml");
</code></pre></td></tr></tbody></table><p>同时Configuration还提供了一系列方法用来定制hibernate的加载配置文件的过程，让你的应用更加灵活，常用的是以下几种：</p><a name="IDAFWYV"><b></b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
addProperties(Element)
addProperties(Properties)
setProperties(Properties)
setProperty(String, String)
</code></pre></td></tr></tbody></table><p>通过以上几个方法，除了使用默认的hibernate.properties文件，你还可以提供多个.properties配置文件，使用Hibernate的时候根据不同的情况使用不同的配置文件，例如：</p><a name="IDANWYV"><b></b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
Properties properties = Properties.load("my.properties");
Configuration config = new Configuration().setProperties(properties).configure();
</code></pre></td></tr></tbody></table><p>除了指定.properties文件之外，还可以指定.hbm.xml文件，下面列出几个常用的方法：</p><a name="IDAVWYV"><b></b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
addClass(Class)
addFile(File)
addFile(String)
addURL(URL)
</code></pre></td></tr></tbody></table><p>前面我们已经讲了，configure()方法默认是通过访问hibernate.cfg.xml的&lt;mapping&gt;元素来加载我们提供的.hbm.xml文件，上面列出的方法可以直接指定hbm.xml文件，例如addClass()方法可以直接通过指定class来加载对应的映射文件，hibernate会将提供的class的全名（包括package）自动转化为文件路径，如net.sf.hibernate.examples.quickstart.Cat.class对应了net/sf/hibernate/examples/quickstart/Cat.hbm.xml，还可以用addFile方法直接指定映射文件。</p><p>例一：</p><a name="IDA4WYV"><b></b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
Configuration config = new Configuration().addClass(Cat.class);
</code></pre></td></tr></tbody></table><p>例二：</p><a name="IDAGXYV"><b></b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
Configuration config = new Configuration().addURL(Configuration.class.getResource ("Cat.hbm.xml"));
</code></pre></td></tr></tbody></table><p>例三：</p><a name="IDAOXYV"><b></b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
Configuration config = new Configuration().addFile("Cat.hbm.xml");
</code></pre></td></tr></tbody></table><p><a name="IDAVXYV"><span class="atitle3">3.5 总结</span></a><br />Configuration提供的这些方法的好处如下：</p><p>1． 一个应用中往往有很多.hbm.xml映射文件，开发的过程中如果只是为了测试某个或几个Java PO(Persistence Object)，我们没有必要把所有的.hbm.xml都加载到内存，这样可以通过addClass或者addFile直接，显得非常灵活。</p><p>2． 学习Hibernate的过程中，往往需要通过练习来体会Hibernate提供的各种特征，而很多特征是需要修改配置文件的，如果要观察相同的代码在不同的特征下的表现，就需要手工改配置文件，这样太麻烦了，而且容易出错，我们可以提供多个配置文件，每个配置文件针对需要的特征而配置，这样我们在调用程序的时候，把不同的配置文件作为参数传递进去，而程序代码里面使用setProperties和addFile指定传入的配置文件参数就可以了。</p><p>3． 在单元测试中，特别是在集成测试里面，整个过程是自动化的，我们不能手工干预测试过程，往往需要准备多个配置文件针对不同的测试案例，这个时候setProperties和addFile方法就显得特别有用了，在不同的测试案例中用这些方法来指定相应的配置文件，这样就可以做到自动化测试，保证了持续性。</p><p><a name="IDA4XYV"><span class="atitle3">3.6 应用举例</span></a><br />在刚开始学习hibernate的时候，对于hibernate的hbm映射文件里的各种配置参数没有一个感性的认识，例如inverse="true",lazy="true"这样的配置参数，不通过实践是无法体会到其作用的，传统的方法就是每需要测试一种参数的效果就更改相应的配置文件，然后运行测试来观察结果，如果能够灵活的运用Configuration提供的定制配置的方法，我们可以提供多个配置文件，每个配置文件里面有不同的配置参数，配合相应的测试案例就方便多了。</p><p>例如针对ono-to-many和many-to-one的双向关联的映射关系，我们想测试在one-to-many一方使用inverse="false"和inverse="true"的不同效果，假设已经正确的配置好了hibernate.properties，那么还需要提供两个不同的hbm.xml文件，假设分别名为bidirect.inverse.false.hbm.xml和bidirect.inverse.true.hbm.xml。</p><p>然后需要写两个不同的测试案例，分别针对两个不同的配置文件进行测试就可以了，这样的好处是，不用针对不同的测试案例修改配置文件，特别是在集成测试的时候，一切都是自动化的，如果每测试一个案例就需要手工去更改配置文件，这肯定是一个失败的测试。</p><p>代码模板如下：</p><a name="IDAKY3V"><b></b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
FalseInverseTest.java文件
import junit.framework.TestCase;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.Transaction;
import net.sf.hibernate.cfg.Configuration;

/**
 * test false inverse
 */
public class FalseInverseTest extends TestCase {

    private Session session;
    private Transaction tx;

    protected void setUp() throws Exception {
        Configuration cfg = new Configuration().addFile("bidirect.inverse.false.hbm.xml");
        session = cfg.buildSessionFactory().openSession();
        tx = session.beginTransaction();
    }

    protected void tearDown() throws Exception {
tx.commit();
      session.close();
    }
    
    public void testLogic()  {
     //在此编写测试代码
    }

}
</code></pre></td></tr></tbody></table><p>TrueInverseTest.java文件</p><a name="IDASY3V"><b></b></a><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"><tbody><tr><td><pre><code>
import junit.framework.TestCase;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.Transaction;
import net.sf.hibernate.cfg.Configuration;

/**
 * test true inverse
 */
public class TrueInverseTest extends TestCase {

    private Session session;
    private Transaction tx;

    protected void setUp() throws Exception {
        Configuration cfg = new Configuration().addFile("bidirect.inverse.true.hbm.xml");
        session = cfg.buildSessionFactory().openSession();
        tx = session.beginTransaction();
    }

    protected void tearDown() throws Exception {
      tx.commit();
      session.close();
    }
    
    public void testLogic()  {
     //在此编写测试代码
    }

}
</code></pre></td></tr></tbody></table><p><a name="IDAZY3V"><span class="atitle2">结束语</span></a><br />通过对Hibernate默认的配置文件的加载顺序和Hibernate提供的加载配置文件的方法的讨论，我们对在使用到Hibernate的项目的单元测试中使用多个Hibernate配置文件有了比较清楚的认识。</p><p>持续集成中的测试的特征是自动化和持续性，不能手工干预其过程，在使用到Hibernate的项目如果要实现持续集成，就要为不同的测试案例提供不同的配置文件，而不是针对不同的测试案例进行手工调整，因此，在使用到Hibernate的项目中灵活的运用多配置文件，可以提高测试的效率，保证自动化和持续性。</p><p><a name="1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></a>注1：有关的代码请参考Environment类的static{}。</p><p><a name="2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></a>注2：如果在hibernate.cfg.xml的&lt;property/&gt;配置的name没有以hibernate开头，那么configure()内部会自动在前面添加hibernate，例如connection.url，hibernate会自动将其转化为hibernate.connection.url。</p><p><a name="resources"><span class="atitle2">参考资料 </span></a></p><ul><li>官方hibernate reference 地址：<a href="http://hibernate.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">http://hibernate.org</a><br /></li></ul><img src ="http://www.blogjava.net/ltc603/aggbug/66778.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ltc603/" target="_blank">阿成</a> 2006-08-31 09:47 <a href="http://www.blogjava.net/ltc603/archive/2006/08/31/66778.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于Hibernate缓存的使用－－（ZT－http://blog.csdn.net/woshichenxu/archive/2006/01/22/586361.aspx）</title><link>http://www.blogjava.net/ltc603/archive/2006/08/22/64954.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Tue, 22 Aug 2006 01:23:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2006/08/22/64954.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/64954.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2006/08/22/64954.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/64954.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/64954.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 1.														     																																																												关于																						hibernate																																														缓存的问题：...&nbsp;&nbsp;<a href='http://www.blogjava.net/ltc603/archive/2006/08/22/64954.html'>阅读全文</a><img src ="http://www.blogjava.net/ltc603/aggbug/64954.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ltc603/" target="_blank">阿成</a> 2006-08-22 09:23 <a href="http://www.blogjava.net/ltc603/archive/2006/08/22/64954.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate学习笔记</title><link>http://www.blogjava.net/ltc603/archive/2006/08/16/63913.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Wed, 16 Aug 2006 07:37:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2006/08/16/63913.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/63913.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2006/08/16/63913.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/63913.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/63913.html</trackback:ping><description><![CDATA[
	一、一对多映射<br />1、在映射一对多的双相关联关系时，应该在one方把inverse属性设为true，可以提高应用的性能。<br />2、建立两个对象的双向关联时，应该同时修改关联两端的对象的应用属性，这样使程序更加健壮，提高业务逻辑层的独立性，使业务逻辑层的程序代码不受hibernate实现的影响；同理，当解除双相关联关系时，也应该修改关联两端的对象的相应属性。<br />eg://添加<br />customer.getOrders().add(order);<br />order.setCustomer(customer);<br />//删除<br />customer.getOrders().remove(order);<br />order.setCustomer(null);<br />3、在定义一对多映射中“一”的POJO类时，注意要private Set orders = new HashSet();//通常把它初始化为集合实现类的一个实例，这样避免访问取值为null，引发NullPointerException异常，提高健壮性。<br />二、Session三种检索方法：<br />1.load():根据给定OID从数据库中加载一个持久化对象，如数据库中没有则抛出net.sf.hibernate.ObjectNotFoundException异常。<br />2.get():根据给定OID从数据库中加载一个持久化对象，如数据库中没有则返回null。<br />3.find():按照参数指定的HQL语句加载一个或多个持久化对象，实际是HQL检索方式的一种简写形式。<br />三、hql查询：<br />在数组和Collection中的查询：<br />String hql = "select  u from User u where u in (:users)";<br />query.setParameterList("users", users);<br />//括号千万别忘写，否则出现如下错误：<br />2006-07-07 11:07:35 WARN [org.hibernate.util.JDBCExceptionReporter] - SQL Error: 907, SQLState: 42000<br />2006-07-07 11:07:35 ERROR [org.hibernate.util.JDBCExceptionReporter] - ORA-00907: 缺失右括号<img src ="http://www.blogjava.net/ltc603/aggbug/63913.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ltc603/" target="_blank">阿成</a> 2006-08-16 15:37 <a href="http://www.blogjava.net/ltc603/archive/2006/08/16/63913.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate入门 - 包作用详解 </title><link>http://www.blogjava.net/ltc603/archive/2006/03/14/35293.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Tue, 14 Mar 2006 12:42:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2006/03/14/35293.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/35293.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2006/03/14/35293.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/35293.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/35293.html</trackback:ping><description><![CDATA[
		<span class="postbody">Hibernate一共包括了23个jar包，令人眼花缭乱。本文将详细讲解Hibernate每个jar包的作用，便于你在应用中根据自己的需要进行取舍。 <br /><br />下载Hibernate，例如2.0.3稳定版本，解压缩，可以看到一个hibernate2.jar和lib目录下有22个jar包： <br /><br />hibernate2.jar: <br /><br />Hibernate的库，没有什么可说的，必须使用的jar包 <br /><br />cglib-asm.jar: <br /><br />CGLIB库，Hibernate用它来实现PO字节码的动态生成，非常核心的库，必须使用的jar包 <br /><br />dom4j.jar: <br /><br />dom4j是一个Java的XML API，类似于jdom，用来读写XML文件的。dom4j是一个非常非常优秀的Java XML API，具有性能优异、功能强大和极端易用使用的特点，同时它也是一个开放源代码的软件，可以在SourceForge上找到它。在IBM developerWorks上面可以找到一篇文章，对主流的Java XML API进行的性能、功能和易用性的评测，dom4j无论在那个方面都是非常出色的。我早在将近两年之前就开始使用dom4j，直到现在。如今你可以看到越来越多的Java软件都在使用dom4j来读写XML，特别值得一提的是连Sun的JAXM也在用dom4j。这是必须使用的jar包，Hibernate用它来读写配置文件。 <br /><br />odmg.jar: <br /><br />ODMG是一个ORM的规范，Hibernate实现了ODMG规范，这是一个核心的库，必须使用的jar包。 <br /><br />commons-collections.jar： <br /><br />Apache Commons包中的一个，包含了一些Apache开发的集合类，功能比java.util.*强大。必须使用的jar包。 <br /><br />commons-beanutils.jar： <br /><br />Apache Commons包中的一个，包含了一些Bean工具类类。必须使用的jar包。 <br /><br />commons-lang.jar: <br /><br />Apache Commons包中的一个，包含了一些数据类型工具类，是java.lang.*的扩展。必须使用的jar包。 <br /><br />commons-logging.jar: <br /><br />Apache Commons包中的一个，包含了日志功能，必须使用的jar包。这个包本身包含了一个Simple Logger，但是功能很弱。在运行的时候它会先在CLASSPATH找log4j，如果有，就使用log4j，如果没有，就找JDK1.4带的java.util.logging，如果也找不到就用Simple Logger。commons-logging.jar的出现是一个历史的的遗留的遗憾，当初Apache极力游说Sun把log4j加入JDK1.4，然而JDK1.4项目小组已经接近发布JDK1.4产品的时间了，因此拒绝了Apache的要求，使用自己的java.util.logging，这个包的功能比log4j差的很远，性能也一般。后来Apache就开发出来了commons-logging.jar用来兼容两个logger。因此用commons-logging.jar写的log程序，底层的Logger是可以切换的，你可以选择log4j，java.util.logging或者它自带的Simple Logger。不过我仍然强烈建议使用log4j，因为log4j性能很高，log输出信息时间几乎等于System.out，而处理一条log平均只需要5us。你可以在Hibernate的src目录下找到Hibernate已经为你准备好了的log4j的配置文件，你只需要到Apache 网站去下载log4j就可以了。commons-logging.jar也是必须的jar包。 <br /><br />使用Hibernate必须的jar包就是以上的这几个，剩下的都是可选的。 <br /><br /><br /><br />ant.jar: <br /><br />Ant编译工具的jar包，用来编译Hibernate源代码的。如果你不准备修改和编译Hibernate源代码，那么就没有什么用，可选的jar包 <br /><br />optional.jar： <br /><br />Ant的一个辅助包。 <br /><br /><br /><br />c3p0.jar： <br /><br />C3PO是一个数据库连接池，Hibernate可以配置为使用C3PO连接池。如果你准备用这个连接池，就需要这个jar包。 <br /><br />proxool.jar： <br /><br />也是一个连接池，同上。 <br /><br />commons-pool.jar, commons-dbcp.jar: <br /><br />DBCP数据库连接池，Apache的Jakarta组织开发的，Tomcat4的连接池也是DBCP。 <br /><br />实际上Hibernate自己也实现了一个非常非常简单的数据库连接池，加上上面3个，你实际上可以在Hibernate上选择4种不同的数据库连接池，选择哪一个看个人的偏好，不过DBCP可能更通用一些。另外强调一点，如果在EJB中使用Hibernate，一定要用App Server的连接池，不要用以上4种连接池，否则容器管理事务不起作用。 <br /><br /><br />connector.jar: <br /><br />JCA 规范，如果你在App Server上把Hibernate配置为Connector的话，就需要这个jar。不过实际上一般App Server肯定会带上这个包，所以实际上是多余的包。 <br /><br />jaas.jar: <br /><br />JAAS是用来进行权限验证的，已经包含在JDK1.4里面了。所以实际上是多余的包。 <br /><br />jcs.jar： <br /><br />如果你准备在Hibernate中使用JCS的话，那么必须包括它，否则就不用。 <br /><br />jdbc2_0-stdext.jar: <br /><br />JDBC2.0的扩展包，一般来说数据库连接池会用上它。不过App Server都会带上，所以也是多余的。 <br /><br />jta.jar： <br /><br />JTA规范，当Hibernate使用JTA的时候需要，不过App Server都会带上，所以也是多余的。 <br /><br />junit.jar: <br /><br />Junit包，当你运行Hibernate自带的测试代码的时候需要，否则就不用。 <br /><br />xalan.jar, xerces.jar, xml-apis.jar: <br /><br />Xerces是XML解析器，Xalan是格式化器，xml-apis实际上是JAXP。一般App Server都会带上，JDK1.4也包含了解析器，不过不是Xerces，是Crimson，效率比较差，不过Hibernate用XML只不过是读取配置文件，性能没什么紧要的，所以也是多余的。</span>
<img src ="http://www.blogjava.net/ltc603/aggbug/35293.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ltc603/" target="_blank">阿成</a> 2006-03-14 20:42 <a href="http://www.blogjava.net/ltc603/archive/2006/03/14/35293.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>hibernate二级缓存攻略 -------转载java视线上Areyouok?的文章</title><link>http://www.blogjava.net/ltc603/archive/2006/03/09/34548.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Thu, 09 Mar 2006 12:56:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2006/03/09/34548.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/34548.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2006/03/09/34548.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/34548.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/34548.html</trackback:ping><description><![CDATA[
		<span class="postbody">很多人对二级缓存都不太了解，或者是有错误的认识，我一直想写一篇文章介绍一下hibernate的二级缓存的，今天终于忍不住了。 <br />我的经验主要来自hibernate2.1版本，基本原理和3.0、3.1是一样的，请原谅我的顽固不化。 <br /><br />hibernate的session提供了一级缓存，每个session，对同一个id进行两次load，不会发送两条sql给数据库，但是session关闭的时候，一级缓存就失效了。 <br /><br />二级缓存是SessionFactory级别的全局缓存，它底下可以使用不同的缓存类库，比如ehcache、oscache等，需要设置hibernate.cache.provider_class，我们这里用ehcache，在2.1中就是 <br />hibernate.cache.provider_class=net.sf.hibernate.cache.EhCacheProvider <br />如果使用查询缓存，加上 <br />hibernate.cache.use_query_cache=true <br /><br /><br />缓存可以简单的看成一个Map，通过key在缓存里面找value。 <br /><br /><span style="font-weight: bold;">Class的缓存</span><br />对于一条记录，也就是一个PO来说，是根据ID来找的，缓存的key就是ID，value是POJO。无论list，load还是iterate，只要读出一个对象，都会填充缓存。但是list不会使用缓存，而iterate会先取数据库select id出来，然后一个id一个id的load，如果在缓存里面有，就从缓存取，没有的话就去数据库load。假设是读写缓存，需要设置： <br />&lt;cache usage="read-write"/&gt; <br />如果你使用的二级缓存实现是ehcache的话，需要配置ehcache.xml <br />&lt;cache name="com.xxx.pojo.Foo" maxElementsInMemory="500" eternal="false" timeToLiveSeconds="7200" timeToIdleSeconds="3600" overflowToDisk="true" /&gt; <br />其中eternal表示缓存是不是永远不超时，timeToLiveSeconds是缓存中每个元素（这里也就是一个POJO）的超时时间，如果eternal="false"，超过指定的时间，这个元素就被移走了。timeToIdleSeconds是发呆时间，是可选的。当往缓存里面put的元素超过500个时，如果overflowToDisk="true"，就会把缓存中的部分数据保存在硬盘上的临时文件里面。 <br />每个需要缓存的class都要这样配置。如果你没有配置，hibernate会在启动的时候警告你，然后使用defaultCache的配置，这样多个class会共享一个配置。 <br />当某个ID通过hibernate修改时，hibernate会知道，于是移除缓存。 <br />这样大家可能会想，同样的查询条件，第一次先list，第二次再iterate，就可以使用到缓存了。实际上这是很难的，因为你无法判断什么时候是第一次，而且每次查询的条件通常是不一样的，假如数据库里面有100条记录，id从1到100，第一次list的时候出了前50个id，第二次iterate的时候却查询到30至70号id，那么30-50是从缓存里面取的，51到70是从数据库取的，共发送1+20条sql。所以我一直认为iterate没有什么用，总是会有1+N的问题。 <br />（题外话：有说法说大型查询用list会把整个结果集装入内存，很慢，而iterate只select id比较好，但是大型查询总是要分页查的，谁也不会真的把整个结果集装进来，假如一页20条的话，iterate共需要执行21条语句，list虽然选择若干字段，比iterate第一条select id语句慢一些，但只有一条语句，不装入整个结果集hibernate还会根据数据库方言做优化，比如使用mysql的limit，整体看来应该还是list快。） <br />如果想要对list或者iterate查询的结果缓存，就要用到查询缓存了 <br /><br /><span style="font-weight: bold;">查询缓存</span><br />首先需要配置hibernate.cache.use_query_cache=true <br />如果用ehcache，配置ehcache.xml，注意hibernate3.0以后不是net.sf的包名了 <br />&lt;cache name="net.sf.hibernate.cache.StandardQueryCache" <br />maxElementsInMemory="50" eternal="false" timeToIdleSeconds="3600" <br />timeToLiveSeconds="7200" overflowToDisk="true"/&gt; <br />&lt;cache name="net.sf.hibernate.cache.UpdateTimestampsCache" <br />maxElementsInMemory="5000" eternal="true" overflowToDisk="true"/&gt; <br />然后 <br />query.setCacheable(true);//激活查询缓存 <br />query.setCacheRegion("myCacheRegion");//指定要使用的cacheRegion，可选 <br />第二行指定要使用的cacheRegion是myCacheRegion，即你可以给每个查询缓存做一个单独的配置，使用setCacheRegion来做这个指定，需要在ehcache.xml里面配置它： <br />&lt;cache name="myCacheRegion" maxElementsInMemory="10" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200" overflowToDisk="true" /&gt; <br />如果省略第二行，不设置cacheRegion的话，那么会使用上面提到的标准查询缓存的配置，也就是net.sf.hibernate.cache.StandardQueryCache <br /><br />对于查询缓存来说，缓存的key是根据hql生成的sql，再加上参数，分页等信息（可以通过日志输出看到，不过它的输出不是很可读，最好改一下它的代码）。 <br />比如hql： <br />from Cat c where c.name like ? <br />生成大致如下的sql： <br />select * from cat c where c.name like ? <br />参数是"tiger%"，那么查询缓存的key*大约*是这样的字符串（我是凭记忆写的，并不精确，不过看了也该明白了）： <br />select * from cat c where c.name like ? , parameter:tiger% <br />这样，保证了同样的查询、同样的参数等条件下具有一样的key。 <br />现在说说缓存的value，如果是list方式的话，value在这里并不是整个结果集，而是查询出来的这一串ID。也就是说，不管是list方法还是iterate方法，第一次查询的时候，它们的查询方式很它们平时的方式是一样的，list执行一条sql，iterate执行1+N条，多出来的行为是它们填充了缓存。但是到同样条件第二次查询的时候，就都和iterate的行为一样了，根据缓存的key去缓存里面查到了value，value是一串id，然后在到class的缓存里面去一个一个的load出来。这样做是为了节约内存。 <br />可以看出来，查询缓存需要打开相关类的class缓存。list和iterate方法第一次执行的时候，都是既填充查询缓存又填充class缓存的。 <br /><span style="font-weight: bold;">这里还有一个很容易被忽视的重要问题，即打开查询缓存以后，即使是list方法也可能遇到1+N的问题！</span>相同条件第一次list的时候，因为查询缓存中找不到，不管class缓存是否存在数据，总是发送一条sql语句到数据库获取全部数据，然后填充查询缓存和class缓存。但是第二次执行的时候，问题就来了，如果你的class缓存的超时时间比较短，现在class缓存都超时了，但是查询缓存还在，那么list方法在获取id串以后，将会一个一个去数据库load！因此，class缓存的超时时间一定不能短于查询缓存设置的超时时间！如果还设置了发呆时间的话，保证class缓存的发呆时间也大于查询的缓存的生存时间。这里还有其他情况，比如class缓存被程序强制evict了，这种情况就请自己注意了。 <br /><br />另外，如果hql查询包含select字句，那么查询缓存里面的value就是整个结果集了。 <br /><br />当hibernate更新数据库的时候，它怎么知道更新哪些查询缓存呢？ <br />hibernate在一个地方维护每个表的最后更新时间，其实也就是放在上面net.sf.hibernate.cache.UpdateTimestampsCache所指定的缓存配置里面。 <br />当通过hibernate更新的时候，hibernate会知道这次更新影响了哪些表。然后它更新这些表的最后更新时间。每个缓存都有一个生成时间和这个缓存所查询的表，当hibernate查询一个缓存是否存在的时候，如果缓存存在，它还要取出缓存的生成时间和这个缓存所查询的表，然后去查找这些表的最后更新时间，如果有一个表在生成时间后更新过了，那么这个缓存是无效的。 <br />可以看出，只要更新过一个表，那么凡是涉及到这个表的查询缓存就失效了，因此查询缓存的命中率可能会比较低。 <br /><br /><span style="font-weight: bold;">Collection缓存</span><br />需要在hbm的collection里面设置 <br />&lt;cache usage="read-write"/&gt; <br />假如class是Cat，collection叫children，那么ehcache里面配置 <br />&lt;cache name="com.xxx.pojo.Cat.children" <br />maxElementsInMemory="20" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200" <br />overflowToDisk="true" /&gt; <br />Collection的缓存和前面查询缓存的list一样，也是只保持一串id，但它不会因为这个表更新过就失效，一个collection缓存仅在这个collection里面的元素有增删时才失效。 <br />这样有一个问题，如果你的collection是根据某个字段排序的，当其中一个元素更新了该字段时，导致顺序改变时，collection缓存里面的顺序没有做更新。 <br /><br /><span style="font-weight: bold;">缓存策略</span><br />只读缓存（read-only）：没有什么好说的 <br />读/写缓存（read-write）:程序可能要的更新数据 <br />不严格的读/写缓存（nonstrict-read-write）：需要更新数据，但是两个事务更新同一条记录的可能性很小，性能比读写缓存好 <br />事务缓存（transactional）：缓存支持事务，发生异常的时候，缓存也能够回滚，只支持jta环境，这个我没有怎么研究过 <br /><br />读写缓存和不严格读写缓存在实现上的区别在于，读写缓存更新缓存的时候会把缓存里面的数据换成一个锁，其他事务如果去取相应的缓存数据，发现被锁住了，然后就直接取数据库查询。 <br />在hibernate2.1的ehcache实现中，如果锁住部分缓存的事务发生了异常，那么缓存会一直被锁住，直到60秒后超时。 <br />不严格读写缓存不锁定缓存中的数据。 <br /><br /><br /><span style="font-weight: bold;">使用二级缓存的前置条件</span><br />你的hibernate程序对数据库有独占的写访问权，其他的进程更新了数据库，hibernate是不可能知道的。你操作数据库必需直接通过hibernate，如果你调用存储过程，或者自己使用jdbc更新数据库，hibernate也是不知道的。hibernate3.0的大批量更新和删除是不更新二级缓存的，但是据说3.1已经解决了这个问题。 <br />这个限制相当的棘手，有时候hibernate做批量更新、删除很慢，但是你却不能自己写jdbc来优化，很郁闷吧。 <br />SessionFactory也提供了移除缓存的方法，你一定要自己写一些JDBC的话，可以调用这些方法移除缓存，这些方法是： <br />void evict(Class persistentClass) <br />Evict all entries from the second-level cache. <br />void evict(Class persistentClass, Serializable id) <br />Evict an entry from the second-level cache. <br />void evictCollection(String roleName) <br />Evict all entries from the second-level cache. <br />void evictCollection(String roleName, Serializable id) <br />Evict an entry from the second-level cache. <br />void evictQueries() <br />Evict any query result sets cached in the default query cache region. <br />void evictQueries(String cacheRegion) <br />Evict any query result sets cached in the named query cache region. <br />不过我不建议这样做，因为这样很难维护。比如你现在用JDBC批量更新了某个表，有3个查询缓存会用到这个表，用evictQueries(String cacheRegion)移除了3个查询缓存，然后用evict(Class persistentClass)移除了class缓存，看上去好像完整了。不过哪天你添加了一个相关查询缓存，可能会忘记更新这里的移除代码。如果你的jdbc代码到处都是，在你添加一个查询缓存的时候，还知道其他什么地方也要做相应的改动吗？ <br /><br />---------------------------------------------------- <br /><br /><span style="font-weight: bold;">总结：</span><br />不要想当然的以为缓存一定能提高性能，仅仅在你能够驾驭它并且条件合适的情况下才是这样的。hibernate的二级缓存限制还是比较多的，不方便用jdbc可能会大大的降低更新性能。在不了解原理的情况下乱用，可能会有1+N的问题。不当的使用还可能导致读出脏数据。 <br />如果受不了hibernate的诸多限制，那么还是自己在应用程序的层面上做缓存吧。 <br />在越高的层面上做缓存，效果就会越好。就好像尽管磁盘有缓存，数据库还是要实现自己的缓存，尽管数据库有缓存，咱们的应用程序还是要做缓存。因为底层的缓存它并不知道高层要用这些数据干什么，只能做的比较通用，而高层可以有针对性的实现缓存，所以在更高的级别上做缓存，效果也要好些吧。</span>
<img src ="http://www.blogjava.net/ltc603/aggbug/34548.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ltc603/" target="_blank">阿成</a> 2006-03-09 20:56 <a href="http://www.blogjava.net/ltc603/archive/2006/03/09/34548.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>几个老生常谈的hibernate问题</title><link>http://www.blogjava.net/ltc603/archive/2006/02/15/30754.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Wed, 15 Feb 2006 02:09:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2006/02/15/30754.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/30754.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2006/02/15/30754.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/30754.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/30754.html</trackback:ping><description><![CDATA[
		<p>
				<font style="background-color: rgb(255, 255, 255);" color="#000000">Hibenate作为一种Java对象持久化技术，在很多大型的多层体系构架中得到应用，比如在开发一套电子商务系统可以以J2EE作为体系构架，Structs作为java Web应用框架，以Hibenate实现对象持久化任务，以EJB或者普通的javabean实现业务逻辑，其实现过程的复杂度可想而知，下面收集一些在Hibenate中多对多关系中应用技巧给大家分享</font>
		</p>
		<p>
				<font style="background-color: rgb(255, 255, 255);" color="#000000">1.cascade="..."？</font>
		</p>
		<p>
				<font style="background-color: rgb(255, 255, 255);" color="#000000">cascade属性并不是多对多关系一定要用的，有了它只是让我们在插入或删除对像时更方便一些，只要在cascade的源头上插入或是删除，所有cascade的关系就会被自己动的插入或是删除。便是为了能正确的cascade，unsaved-value是个很重要的属性。</font>
		</p>
		<p>
				<font style="background-color: rgb(255, 255, 255);" color="#000000">Hibernate通过这个属性来判断一个对象应该save还是update，如果这龆韵蟮膇d是unsaved-value的话，那说明这个对象不是persistence object要save（insert)；如果id是非unsaved-value的话，那说明这个对象是persistence object（数据库中已存在），只要update就行了。saveOrUpdate方法用的也是这个机制。</font>
		</p>
		<p>
				<font style="background-color: rgb(255, 255, 255);" color="#000000">2.inverse="ture"?</font>
		</p>
		<p>
				<font style="background-color: rgb(255, 255, 255);" color="#000000">inverse属性默认是false的，就是说关系的两端都来维护关系。这个意思就是说，如有一个Student, Teacher和TeacherStudent表，Student和Teacher是多对多对多关系，这个关系由TeacherStudent这个表来表现。那么什么时候插入或删除TeacherStudent表中的记录来维护关系呢？在用hibernate时，我们不会显示的对TeacherStudent表做操作。</font>
		</p>
		<p>
				<font style="background-color: rgb(255, 255, 255);" color="#000000">对TeacherStudent的操作是hibernate帮我们做的。hibernate就是看hbm文件中指定的是"谁"维护关系，那个在插入或删除"谁"时，就会处发对关系表的操作。前提是"谁"这个对象已经知道这个关系了，就是说关系另一头的对象已经set或是add到"谁"这个对象里来了。前面说过inverse默认是false，就是关系的两端都维护关系，对其中任一个操作都会处发对表系表的操作。当在关系的一头，如Student中的bag或set中用了inverse＝"true"时，那就代表关系是由另一关维护的（Teacher）。就是说当这插入Student时，不会操作TeacherStudent表，即使Student已经知道了关系。只有当Teacher插入或删除时才会处发对关系表的操作。</font>
		</p>
		<p>
				<font style="background-color: rgb(255, 255, 255);" color="#000000">所以，当关系的两头都用inverse="true"是不对的，就会导致任何操作都不处发对关系表的操作。当两端都是inverse="false"或是default值是，在代码对关系显示的维护也是不对的，会导致在关系表中插入两次关系。在一对多关系中inverse就更有意义了。在多对多中，在哪端inverse="true"效果差不多（在效率上）。但是在一对多中，如果要一方维护关系，就会使在插入或是删除"一"方时去update"多"方的每一个与这个"一"的对象有关系的对象。</font>
		</p>
		<p>
				<font style="background-color: rgb(255, 255, 255);" color="#000000">而如果让"多"方面维护关系时就不会有update操作，因为关系就是在多方的对象中的，直指插入或是删除多方对象就行了。当然这时也要遍历"多"方的每一个对象显示的操作修关系的变化体现到DB中。不管怎样说，还是让"多"方维护关系更直观一些。 </font>
		</p>
		<p>
				<font style="background-color: rgb(255, 255, 255);" color="#000000">3.cascade和inverse有什么区别？</font>
		</p>
		<p>
				<font style="background-color: rgb(255, 255, 255);" color="#000000">可以这样理解，cascade定义的是关系两端对象到对象的级联关系；而inverse定义的是关系和对象的级联关系。</font>
		</p>
		<p>
				<font style="background-color: rgb(255, 255, 255);" color="#000000">4.net.sf.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): 2, of class: Xxxxx</font>
		</p>
		<p>
				<font style="background-color: rgb(255, 255, 255);" color="#000000">这个问题出现在要删除关系的一头时。如，要删除一个已经和Student有关系的Teacher。当tx.commit();时才会抛出这个异常。这时一个在关系另一头的Student对象中的Set或是List中把这个Teacher对象显示的remove掉，再session.delete(这个teacher);。这是为了防止在Student端有cascade时把这个Teacher对象再存回DB。</font>
		</p>
		<p>
				<font style="background-color: rgb(255, 255, 255);" color="#000000">所以，这个异常的只有在Student的关系定义中有cascade="..."，而且没有像上面说的显示的解除关系时才会出现。所以防止出现这个异常的方法就是：1，在Student端不用cascade；2，或是用cascade的话，就显示的删除对像中的关系。 3，在Teacher端要用cascade。</font>
		</p>
		<p>
				<font style="background-color: rgb(255, 255, 255);" color="#000000">5.net.sf.hibernate.HibernateException: identifier of an instance of my.MyObject altered from N to N</font>
		</p>
		<p>
				<font style="background-color: rgb(255, 255, 255);" color="#000000">这个异常其实不是多对多中常遇到的，但是这个异常的提示不make sense，所以提一下，是因为id的java对象中的type和hbm文件中定义的不一样，如：java中用long，而hbm中用type="integer"，并且generator用的是identity时就会出现。</font>
		</p>
<img src ="http://www.blogjava.net/ltc603/aggbug/30754.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ltc603/" target="_blank">阿成</a> 2006-02-15 10:09 <a href="http://www.blogjava.net/ltc603/archive/2006/02/15/30754.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>延迟初始化错误（ERROR LazyInitializer）是如何产生的? </title><link>http://www.blogjava.net/ltc603/archive/2006/02/13/30481.html</link><dc:creator>阿成</dc:creator><author>阿成</author><pubDate>Mon, 13 Feb 2006 09:24:00 GMT</pubDate><guid>http://www.blogjava.net/ltc603/archive/2006/02/13/30481.html</guid><wfw:comment>http://www.blogjava.net/ltc603/comments/30481.html</wfw:comment><comments>http://www.blogjava.net/ltc603/archive/2006/02/13/30481.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ltc603/comments/commentRss/30481.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ltc603/services/trackbacks/30481.html</trackback:ping><description><![CDATA[
		<p>延迟初始化错误是运用Hibernate开发项目时最常见的错误。如果对一个类或者集合配置了延迟检索策略，那么必须当代理类实例或代理集合处于持久化状态（即处于Session范围内）时，才能初始化它。如果在游离状态时才初始化它，就会产生延迟初始化错误。</p>
		<p>下面把Customer.hbm.xml文件的&lt;class&gt;元素的lazy属性设为true，表示使用延迟检索策略：</p>
		<p>&lt;class name="mypack.Customer" table="CUSTOMERS" lazy="true"&gt;</p>
		<p>当执行Session的load()方法时，Hibernate不会立即执行查询CUSTOMERS表的select语句，仅仅返回Customer类的代理类的实例，这个代理类具由以下特征：</p>
		<p>（1） 由Hibernate在运行时动态生成，它扩展了Customer类，因此它继承了Customer类的所有属性和方法，但它的实现对于应用程序是透明的。<br />（2） 当Hibernate创建Customer代理类实例时，仅仅初始化了它的OID属性，其他属性都为null，因此这个代理类实例占用的内存很少。<br />（3） 当应用程序第一次访问Customer代理类实例时（例如调用customer.getXXX()或customer.setXXX()方法），Hibernate会初始化代理类实例，在初始化过程中执行select语句，真正从数据库中加载Customer对象的所有数据。但有个例外，那就是当应用程序访问Customer代理类实例的getId()方法时，Hibernate不会初始化代理类实例，因为在创建代理类实例时OID就存在了，不必到数据库中去查询。<br /><br /><i>提示：Hibernate采用CGLIB工具来生成持久化类的代理类。CGLIB是一个功能强大的Java字节码生成工具，它能够在程序运行时动态生成扩展Java类或者实现Java接口的代理类。关于CGLIB的更多知识，请参考：<a href="http://cglib.sourceforge.net/%E3%80%82" target="_blank"><font color="#000000">http://cglib.sourceforge.net/。</font></a></i></p>
		<p>以下代码先通过Session的load()方法加载Customer对象，然后访问它的name属性： </p>
		<p>tx = session.beginTransaction();<br />Customer customer=(Customer)session.load(Customer.class,new Long(1));<br />customer.getName();<br />tx.commit();</p>
		<p>在运行session.load()方法时Hibernate不执行任何select语句，仅仅返回Customer类的代理类的实例，它的OID为1，这是由load()方法的第二个参数指定的。当应用程序调用customer.getName()方法时，Hibernate会初始化Customer代理类实例，从数据库中加载Customer对象的数据，执行以下select语句：</p>
		<p>select * from CUSTOMERS where ID=1;<br />select * from ORDERS where CUSTOMER_ID=1;</p>
		<p>当&lt;class&gt;元素的lazy属性为true，会影响Session的load()方法的各种运行时行为，下面举例说明。</p>
		<p>1．如果加载的Customer对象在数据库中不存在，Session的load()方法不会抛出异常，只有当运行customer.getName()方法时才会抛出以下异常：</p>
		<p>ERROR LazyInitializer:63 - Exception initializing proxy<br />net.sf.hibernate.ObjectNotFoundException: No row with the given identifier exists: 1, of class: <br />mypack.Customer</p>
		<p>2．如果在整个Session范围内，应用程序没有访问过Customer对象，那么Customer代理类的实例一直不会被初始化，Hibernate不会执行任何select语句。以下代码试图在关闭Session后访问Customer游离对象：</p>
		<p>tx = session.beginTransaction();<br />Customer customer=(Customer)session.load(Customer.class,new Long(1));<br />tx.commit();<br />session.close();<br />customer.getName();</p>
		<p>由于引用变量customer引用的Customer代理类的实例在Session范围内始终没有被初始化，因此在执行customer.getName()方法时，Hibernate会抛出以下异常：</p>
		<p>ERROR LazyInitializer:63 - Exception initializing proxy<br />net.sf.hibernate.HibernateException: Could not initialize proxy - the owning Session was closed</p>
		<p>由此可见，Customer代理类的实例只有在当前Session范围内才能被初始化。</p>
		<p>3．net.sf.hibernate.Hibernate类的initialize()静态方法用于在Session范围内显式初始化代理类实例，isInitialized()方法用于判断代理类实例是否已经被初始化。例如：</p>
		<p>tx = session.beginTransaction();<br />Customer customer=(Customer)session.load(Customer.class,new Long(1));<br />if(!Hibernate.isInitialized(customer)) <br />Hibernate.initialize(customer);<br />tx.commit();<br />session.close();<br />customer.getName();</p>
		<p>以上代码在Session范围内通过Hibernate类的initialize()方法显式初始化了Customer代理类实例，因此当Session关闭后，可以正常访问Customer游离对象。</p>
		<p>4．当应用程序访问代理类实例的getId()方法时，不会触发Hibernate初始化代理类实例的行为，例如：</p>
		<p>tx = session.beginTransaction();<br />Customer customer=(Customer)session.load(Customer.class,new Long(1));<br />customer.getId();<br />tx.commit();<br />session.close();<br />customer.getName();</p>
		<p>当应用程序访问customer.getId()方法时，该方法直接返回Customer代理类实例的OID值，无需查询数据库。由于引用变量customer始终引用的是没有被初始化的Customer代理类实例，因此当Session关闭后再执行customer.getName()方法，Hibernate会抛出以下异常：</p>
		<p>ERROR LazyInitializer:63 - Exception initializing proxy<br />net.sf.hibernate.HibernateException: Could not initialize proxy - the owning Session was closed<br /></p>
<img src ="http://www.blogjava.net/ltc603/aggbug/30481.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ltc603/" target="_blank">阿成</a> 2006-02-13 17:24 <a href="http://www.blogjava.net/ltc603/archive/2006/02/13/30481.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>