﻿<?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/renyangok/category/17019.html</link><description /><language>zh-cn</language><lastBuildDate>Thu, 08 Mar 2007 02:30:17 GMT</lastBuildDate><pubDate>Thu, 08 Mar 2007 02:30:17 GMT</pubDate><ttl>60</ttl><item><title>session.flush()</title><link>http://www.blogjava.net/renyangok/archive/2007/02/09/99003.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Fri, 09 Feb 2007 06:09:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/02/09/99003.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/99003.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/02/09/99003.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/99003.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/99003.html</trackback:ping><description><![CDATA[
		<p>hibernate一个Session有一个Cache，cache屏蔽了应用程序直接去访问数据库，并且按某种算法存储了优先级高的数据，避免每次访问数据库影响速度。cache里边的数据如果含有脏数据，需要跟数据库同步（也就是需要hibernate来发送update, delete, insert），调用session.flush()达到此目的。一般情况下，hibernate在一个tx.commit()的时候会自动调用session.flush();也可以应用程序手动调用session.flush()来立刻将session的脏数据提交。<br /><br />        Session session = HibernateSessionFactory.currentSession();<br />        Transaction tx = session.beginTransaction();<br />        try{<br />         Employee employee1 = (Employee)session.load(Employee.class, new Integer(3));<br />         System.out.println(employee1);<br />         employee1.setAge((byte)98);<br />         session.flush();//发送employee1的update给数据库<br />//         employee1.setAge((byte)43);<br />         tx.commit();//employee1已经被flush过，不会再被flush。如果把上面以行代码注释取消，还会再发一条update语句。<br />        }<br />        catch(HibernateException e){<br />            e.printStackTrace();<br />            tx.rollback();<br />        }<strong><br />tx.commit()动作实际包含了session.flush()动作，所以不需要手动调用session.flush();</strong></p>
<img src ="http://www.blogjava.net/renyangok/aggbug/99003.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-02-09 14:09 <a href="http://www.blogjava.net/renyangok/archive/2007/02/09/99003.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate学习笔记（不断更新）</title><link>http://www.blogjava.net/renyangok/archive/2007/01/24/95749.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Wed, 24 Jan 2007 07:50:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/01/24/95749.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/95749.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/01/24/95749.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/95749.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/95749.html</trackback:ping><description><![CDATA[一、映射<br /><p>1.cascade="..."？</p><p>cascade属性并不是多对多关系一定要用的，有了它只是让我们在插入或删除对像时更方便一些，只要在cascade的源头上插入或是删除，所有cascade的关系就会被自己动的插入或是删除。便是为了能正确的cascade，unsaved- value是个很重要的属性。</p><p>Hibernate通过这个属性来判断一个对象应该save还是update，如果这个对象的id是 unsaved-value的话，那说明这个对象不是persistence object要save（insert)；如果id是非unsaved-value的话，那说明这个对象是persistence object（数据库中已存在），只要update就行了。saveOrUpdate方法用的也是这个机制。</p><p>2.inverse="ture"?</p><p>inverse 属性默认是false的，就是说关系的两端都来维护关系。这个意思就是说，如有一个Student, Teacher和TeacherStudent表，Student和Teacher是多对多对多关系，这个关系由TeacherStudent这个表来表现。那么什么时候插入或删除TeacherStudent表中的记录来维护关系呢？在用hibernate时，我们不会显示的对 TeacherStudent表做操作。</p><p>对TeacherStudent的操作是hibernate帮我们做的。hibernate就是看 hbm文件中指定的是"谁"维护关系，那个在插入或删除"谁"时，就会处发对关系表的操作。前提是"谁"这个对象已经知道这个关系了，就是说关系另一头的对象已经set或是add到"谁"这个对象里来了。前面说过inverse默认是false，就是关系的两端都维护关系，对其中任一个操作都会处发对表系表的操作。当在关系的一头，如Student中的bag或set中用了inverse＝"true"时，那就代表关系是由另一关维护的（Teacher）。就是说当这插入Student时，不会操作TeacherStudent表，即使Student已经知道了关系。只有当 Teacher插入或删除时才会处发对关系表的操作。</p><p>所以，当关系的两头都用inverse="true"是不对的，就会导致任何操作都不处发对关系表的操作。当两端都是inverse="false"或是default值是，在代码对关系显示的维护也是不对的，会导致在关系表中插入两次关系。在一对多关系中inverse就更有意义了。在多对多中，在哪端inverse="true"效果差不多（在效率上）。但是在一对多中，如果要一方维护关系，就会使在插入或是删除"一"方时去update"多"方的每一个与这个"一"的对象有关系的对象。</p><p>而如果让"多"方面维护关系时就不会有update操作，因为关系就是在多方的对象中的，直指插入或是删除多方对象就行了。当然这时也要遍历"多"方的每一个对象显示的操作修关系的变化体现到DB中。不管怎样说，还是让"多"方维护关系更直观一些。 </p><p>3.cascade和inverse有什么区别？</p><p>可以这样理解，cascade定义的是关系两端对象到对象的级联关系；而inverse定义的是关系和对象的级联关系。</p><p>4.net.sf.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): 2, of class: Xxxxx</p><p>这个问题出现在要删除关系的一头时。如，要删除一个已经和 Student有关系的Teacher。当tx.commit();时才会抛出这个异常。这时一个在关系另一头的Student对象中的Set或是 List中把这个Teacher对象显示的remove掉，再session.delete(这个teacher);。这是为了防止在Student端有 cascade时把这个Teacher对象再存回DB。</p><p>所以，这个异常的只有在Student的关系定义中有cascade="..."，而且没有像上面说的显示的解除关系时才会出现。所以防止出现这个异常的方法就是：1，在Student端不用cascade；2，或是用cascade的话，就显示的删除对像中的关系。 3，在Teacher端要用cascade。</p><p>5.net.sf.hibernate.HibernateException: identifier of an instance of my.MyObject altered from N to N</p><p>这个异常其实不是多对多中常遇到的，但是这个异常的提示不make sense，所以提一下，是因为id的java对象中的type和hbm文件中定义的不一样，如：java中用long，而hbm中用type= "integer"，并且generator用的是identity时就会出现。</p><p><br />6、在定义一对多映射中“一”的POJO类时，注意要private Set orders = new HashSet();//通常把它初始化为集合实现类的一个实例，这样避免访问取值为null，引发NullPointerException异常，提高健壮性。<br /><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: 缺失右括号<br /><br />三、<br />下面代码如不注释可以插入数据库，但注释了Transaction部分就插入不了<br />       public  static  void  main(String[]  args){  <br />             Session  session  =  HibernateSessionFactory.getSession();  <br />             //Transaction  tx  =  null;  <br />             try  {  <br />             //    tx  =  session.beginTransaction();  <br />                 account  a  =  new  account("admin","123");  <br />                 session.save(a);  <br />             //    tx.commit();  <br />             }catch  (Exception  ex)  {  <br />             //              tx.rollback();  <br />                         ex.printStackTrace();  <br />             }  finally  {  <br />                         session.close();  <br />             }              <br />       }  <br />原因：其实在调用hibernate的时候会自动把commit设置成非自动的。  <br />所以在更改数据的时候不会有任何反应。可以该配置文件为自动提交，但一般不这样。  <br /><br /></p><img src ="http://www.blogjava.net/renyangok/aggbug/95749.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-01-24 15:50 <a href="http://www.blogjava.net/renyangok/archive/2007/01/24/95749.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>(hibernate)inverse,cascade详细分析（举例说明）</title><link>http://www.blogjava.net/renyangok/archive/2006/12/04/85420.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Mon, 04 Dec 2006 09:27:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/04/85420.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/85420.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/04/85420.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/85420.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/85420.html</trackback:ping><description><![CDATA[首先引用另一个blog的说法：<br /><p>1.cascade="..."？</p><p>cascade属性并不是多对多关系一定要用的，有了它只是让我们在插入或删除对像时更方便一些，只要在cascade的源头上插入或是删除，所有cascade的关系就会被自己动的插入或是删除。便是为了能正确的cascade，unsaved- value是个很重要的属性。</p><p>Hibernate通过这个属性来判断一个对象应该save还是update，如果这个对象的id是 unsaved-value的话，那说明这个对象不是persistence object要save（insert)；如果id是非unsaved-value的话，那说明这个对象是persistence object（数据库中已存在），只要update就行了。saveOrUpdate方法用的也是这个机制。</p><p>2.inverse="ture"?</p><p>inverse 属性默认是false的，就是说关系的两端都来维护关系。这个意思就是说，如有一个Student, Teacher和TeacherStudent表，Student和Teacher是多对多对多关系，这个关系由TeacherStudent这个表来表现。那么什么时候插入或删除TeacherStudent表中的记录来维护关系呢？在用hibernate时，我们不会显示的对 TeacherStudent表做操作。</p><p>对TeacherStudent的操作是hibernate帮我们做的。hibernate就是看 hbm文件中指定的是"谁"维护关系，那个在插入或删除"谁"时，就会处发对关系表的操作。前提是"谁"这个对象已经知道这个关系了，就是说关系另一头的对象已经set或是add到"谁"这个对象里来了。前面说过inverse默认是false，就是关系的两端都维护关系，对其中任一个操作都会处发对表系表的操作。当在关系的一头，如Student中的bag或set中用了inverse＝"true"时，那就代表关系是由另一关维护的（Teacher）。就是说当这插入Student时，不会操作TeacherStudent表，即使Student已经知道了关系。只有当 Teacher插入或删除时才会处发对关系表的操作。</p><p>所以，当关系的两头都用inverse="true"是不对的，就会导致任何操作都不处发对关系表的操作。当两端都是inverse="false"或是default值是，在代码对关系显示的维护也是不对的，会导致在关系表中插入两次关系。在一对多关系中inverse就更有意义了。在多对多中，在哪端inverse="true"效果差不多（在效率上）。但是在一对多中，如果要一方维护关系，就会使在插入或是删除"一"方时去update"多"方的每一个与这个"一"的对象有关系的对象。</p><p>而如果让"多"方面维护关系时就不会有update操作，因为关系就是在多方的对象中的，直指插入或是删除多方对象就行了。当然这时也要遍历"多"方的每一个对象显示的操作修关系的变化体现到DB中。不管怎样说，还是让"多"方维护关系更直观一些。 </p><p>3.cascade和inverse有什么区别？</p><p><font color="#ff0000">可以这样理解，cascade定义的是关系两端对象到对象的级联关系；而inverse定义的是关系和对象的级联关系。在一对多映射中，一的一方要设&lt;set (inverse="false") casecade="save-update" /&gt;，多的一方要设&lt;many-to-one inverse=true (casecade="none") /&gt;<br /></font>Wenic的blog:Wenic的blog:http://www.matrix.org.cn/blog/wenic705<br />－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br /><br />这个是我做的用例。测试cascade和inverse.分析结果以及具体hibernate sql都在，比较容易看明白。<br />希望不是很明白的看看，加深印象。<br />小弟学识浅薄，如果有不对的地方，请指出来，谢谢！~<br />测试环境：Eclispe，用断点测试。<br />          数据库是SQLServer2000.<br /><br />Area.hbm.xml<br /></p><pre class="overflow">&lt;?xml version="1.0" encoding="utf-8"?&gt;<br />&lt;!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"; &gt;<br />&lt;hibernate-mapping&gt;<br />&lt;class name="net.villion.model.test.Area" table="r_area"&gt;<br />&lt;id column="ID" type="long" name = "id" length="10" unsaved-value="null"&gt;<br /> &lt;generator class = "native"/&gt;<br />&lt;/id&gt;<br />&lt;property column="name" name="name" type="string"/&gt;<br />&lt;many-to-one name="state" class="net.villion.model.test.State" cascade="all"&gt;<br /> &lt;column name="stateid" index="index_area_state"/&gt;<br />&lt;/many-to-one&gt;<br />&lt;/class&gt;<br />&lt;/hibernate-mapping&gt;<br /><br /><br /><br /><br /><br /><br />State.hbm.xml<br />&lt;?xml version="1.0" encoding="utf-8"?&gt;<br />&lt;!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 2.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"; &gt;<br />&lt;hibernate-mapping&gt;<br />&lt;class name="net.villion.model.test.State" table="r_state"&gt;<br />&lt;id column="ID" type="long" name = "id" length="10" unsaved-value="null"&gt;<br /> &lt;generator class = "native"/&gt;<br />&lt;/id&gt;<br />&lt;property column="name" name="name" type="string"/&gt;<br />&lt;set name="areas" lazy="true" cascade="all" inverse="false"&gt;<br /> &lt;key column="stateid"/&gt;<br /> &lt;one-to-many class="net.villion.model.test.Area"/&gt;<br />&lt;/set&gt;<br /><br />&lt;/class&gt;<br />&lt;/hibernate-mapping&gt;</pre><br /><br /><br />我们主要看inverse,cascade级联的设置。<br />当State.hbm.xml inverse = "false" cascade = "all"<br /><pre class="overflow">//添加<br />//code   <br />StateDAO sdao =(StateDAO) ctx.getBean("stateDAO");<br />AreaDAO adao = (AreaDAO)ctx.getBean("areaDAO");             <br /> Area a3 = new Area();<br />a3.setName("NewYork");<br />Area a4 = new Area();<br />a4.setName("Frolida");<br />State s2 = new State();<br />s2.setName("America");  <br />Set set = new HashSet();<br />set.add(a3);<br />set.add(a4);<br />s2.setAreas(set);<br />sdao.saveObject(s2);<br />      <br />//sql:<br />Hibernate: insert into r_state (name) values (?)<br />Hibernate: insert into r_area (name, stateid) values (?, ?)<br />Hibernate: insert into r_area (name, stateid) values (?, ?)<br />Hibernate: update r_area set stateid=? where ID=?<br />Hibernate: update r_area set stateid=? where ID=?</pre><br /><br />//当State.hbm.xml inverse = "false" cascade = "none"(注意我们操作的是State)<br /><br />//sql: insert into r_state (name) values (?)<br /><br />Hibernate: update r_area set stateid=? where ID=?<br />org.springframework.dao.InvalidDataAccessApiUsageException: object references an unsaved transient instance<br /><br />这时候更新area的时候由于area,并未级联插入所以找不到报错。所以这样设置还是会更新Area的stateid<br />接着上面的说，我们要看当设置改变时的sql变化。<br />上面就说明了cascade="all" 时候能级联插入，而none的时候只是维持两者之间的关系（inverse="false"）。<br />上面两种情况的删除又不一样了，但是一个道理<br />cascade="all"时，两个表数据都级联删除了。<br /><br />而cascade="none"时<br />Hibernate: update r_area set stateid=null where stateid=?<br />Hibernate: delete from r_state where ID=?说明它只是维持关系。<br /><br />而当上面的inverse ="true"时 cascade="none"时<br />sql: insert into r_state (name) values (?)<br />只插入一条State,不会插入Area表了。<br />删除时<br />Hibernate: delete from r_state where ID=?<br />org.springframework.dao.DataIntegrityViolationException: (Hibernate operation): data integrity violated by SQL ''; nested exception is com.jnetdirect.jsql.u: DELETE 语句与 COLUMN REFERENCE 约束 'FKC7F1003A8EEBF16C' 冲突。该冲突发生于数据库 'hd'，表 'r_area', column 'stateid'。<br />com.jnetdirect.jsql.u: DELETE 语句与 COLUMN REFERENCE 约束 'FKC7F1003A8EEBF16C' 冲突<br />由于没有维护数据库关系，引起外键冲突报错。<br /><br /><br />inverse ="true"时 cascade="all"时<br />sql:<br />Hibernate: insert into r_state (name) values (?)<br />Hibernate: insert into r_area (name, stateid) values (?, ?)<br />Hibernate: insert into r_area (name, stateid) values (?, ?)<br />全部级联插入,但r_area表中stateid是null。<br />删除时。<br />Hibernate: delete from r_area where ID=?<br />Hibernate: delete from r_area where ID=?<br />Hibernate: delete from r_state where ID=?<br />级联删除。<br /><br /><br />所以总结一下 ，cascade 是级联的程度，而inverse="false|ture" 是指是否维持两个实体的关系。（两个表的外键）。<br /><br />水平有限，请各位看客口下留情。<br />如果耽误了你的宝贵，我在这里道歉了。 ：（ <img src ="http://www.blogjava.net/renyangok/aggbug/85420.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-04 17:27 <a href="http://www.blogjava.net/renyangok/archive/2006/12/04/85420.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate（2.0.3稳定版）包作用祥解</title><link>http://www.blogjava.net/renyangok/archive/2006/12/04/85421.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Mon, 04 Dec 2006 09:27:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/04/85421.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/85421.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/04/85421.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/85421.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/85421.html</trackback:ping><description><![CDATA[
		<p>Hibernate一共包括了23个jar包，令人眼花缭乱。本文将详细讲解Hibernate每个jar包的作用，便于你在应用中根据自己的需要进行取舍。 </p>
		<p>下载Hibernate，例如2.0.3稳定版本，解压缩，可以看到一个hibernate2.jar和lib目录下有22个jar包： </p>
		<p>hibernate2.jar: </p>
		<p>Hibernate的库，没有什么可说的，必须使用的jar包 </p>
		<p>cglib-asm.jar: </p>
		<p>CGLIB库，Hibernate用它来实现PO字节码的动态生成，非常核心的库，必须使用的jar包 </p>
		<p>dom4j.jar: </p>
		<p>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用它来读写配置文件。 </p>
		<p>odmg.jar: </p>
		<p>ODMG是一个ORM的规范，Hibernate实现了ODMG规范，这是一个核心的库，必须使用的jar包。 </p>
		<p>commons-collections.jar： </p>
		<p>Apache Commons包中的一个，包含了一些Apache开发的集合类，功能比java.util.*强大。必须使用的jar包。 </p>
		<p>commons-beanutils.jar： </p>
		<p>Apache Commons包中的一个，包含了一些Bean工具类类。必须使用的jar包。 </p>
		<p>commons-lang.jar: </p>
		<p>Apache Commons包中的一个，包含了一些数据类型工具类，是java.lang.*的扩展。必须使用的jar包。 </p>
		<p>commons-logging.jar: </p>
		<p>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包。 </p>
		<p>使用Hibernate必须的jar包就是以上的这几个，剩下的都是可选的。 </p>
		<p> </p>
		<p>ant.jar: </p>
		<p>Ant编译工具的jar包，用来编译Hibernate源代码的。如果你不准备修改和编译Hibernate源代码，那么就没有什么用，可选的jar包 </p>
		<p>optional.jar： </p>
		<p>Ant的一个辅助包。 </p>
		<p> </p>
		<p>c3p0.jar： </p>
		<p>C3PO是一个数据库连接池，Hibernate可以配置为使用C3PO连接池。如果你准备用这个连接池，就需要这个jar包。 </p>
		<p>proxool.jar： </p>
		<p>也是一个连接池，同上。 </p>
		<p>commons-pool.jar, commons-dbcp.jar: </p>
		<p>DBCP数据库连接池，Apache的Jakarta组织开发的，Tomcat4的连接池也是DBCP。 </p>
		<p>实际上Hibernate自己也实现了一个非常非常简单的数据库连接池，加上上面3个，你实际上可以在Hibernate上选择4种不同的数据库连接池，选择哪一个看个人的偏好，不过DBCP可能更通用一些。另外强调一点，如果在EJB中使用Hibernate，一定要用App Server的连接池，不要用以上4种连接池，否则容器管理事务不起作用。 </p>
		<p>
				<br />connector.jar: </p>
		<p>JCA 规范，如果你在App Server上把Hibernate配置为Connector的话，就需要这个jar。不过实际上一般App Server肯定会带上这个包，所以实际上是多余的包。 </p>
		<p>jaas.jar: </p>
		<p>JAAS是用来进行权限验证的，已经包含在JDK1.4里面了。所以实际上是多余的包。 </p>
		<p>jcs.jar： </p>
		<p>如果你准备在Hibernate中使用JCS的话，那么必须包括它，否则就不用。 </p>
		<p>jdbc2_0-stdext.jar: </p>
		<p>JDBC2.0的扩展包，一般来说数据库连接池会用上它。不过App Server都会带上，所以也是多余的。 </p>
		<p>jta.jar： </p>
		<p>JTA规范，当Hibernate使用JTA的时候需要，不过App Server都会带上，所以也是多余的。 </p>
		<p>junit.jar: </p>
		<p>Junit包，当你运行Hibernate自带的测试代码的时候需要，否则就不用。 </p>
		<p>xalan.jar, xerces.jar, xml-apis.jar: </p>
		<p>Xerces 是XML解析器，Xalan是格式化器，xml-apis实际上是JAXP。一般App Server都会带上，JDK1.4也包含了解析器，不过不是Xerces，是Crimson，效率比较差，不过Hibernate用XML只不过是读取配置文件，性能没什么紧要的，所以也是多余的。</p>
<img src ="http://www.blogjava.net/renyangok/aggbug/85421.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-04 17:27 <a href="http://www.blogjava.net/renyangok/archive/2006/12/04/85421.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>延迟初始化错误（ERROR LazyInitializer）是如何产生的?</title><link>http://www.blogjava.net/renyangok/archive/2006/12/04/85418.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Mon, 04 Dec 2006 09:26:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/04/85418.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/85418.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/04/85418.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/85418.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/85418.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>ERROR LazyInitializer:63 - Exception initializing proxy<br />net.sf.hibernate.HibernateException: Could not initialize proxy - the owning Session was closed<img src ="http://www.blogjava.net/renyangok/aggbug/85418.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-04 17:26 <a href="http://www.blogjava.net/renyangok/archive/2006/12/04/85418.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【未读】hibernate二级缓存攻略 -------转载java视线上Areyouok?的文章</title><link>http://www.blogjava.net/renyangok/archive/2006/12/04/85417.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Mon, 04 Dec 2006 09:25:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/04/85417.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/85417.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/04/85417.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/85417.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/85417.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/renyangok/aggbug/85417.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-04 17:25 <a href="http://www.blogjava.net/renyangok/archive/2006/12/04/85417.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/renyangok/archive/2006/12/04/85416.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Mon, 04 Dec 2006 09:24:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/04/85416.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/85416.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/04/85416.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/85416.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/85416.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 1.														     																																																												关于																						hibernate																																														缓存的问题：...&nbsp;&nbsp;<a href='http://www.blogjava.net/renyangok/archive/2006/12/04/85416.html'>阅读全文</a><img src ="http://www.blogjava.net/renyangok/aggbug/85416.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-04 17:24 <a href="http://www.blogjava.net/renyangok/archive/2006/12/04/85416.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate实用工具</title><link>http://www.blogjava.net/renyangok/archive/2006/12/04/85412.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Mon, 04 Dec 2006 09:23:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/04/85412.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/85412.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/04/85412.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/85412.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/85412.html</trackback:ping><description><![CDATA[为了简化开发，Hibernate提供了一些实用的工具，用于在映射文件、java源文件、数据库Schema只见自动转化。<br />java源文件－－－》映射文件（XDoclet）<br />映射文件－－－》java源文件（hbm2java）<br />数据库Schema－－－》映射文件（Middlegen）<br />映射文件－－－》数据库Schema（hbm2ddl）<img src ="http://www.blogjava.net/renyangok/aggbug/85412.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-04 17:23 <a href="http://www.blogjava.net/renyangok/archive/2006/12/04/85412.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>hibernate二级缓存攻略</title><link>http://www.blogjava.net/renyangok/archive/2006/12/04/85411.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Mon, 04 Dec 2006 09:22:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/04/85411.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/85411.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/04/85411.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/85411.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/85411.html</trackback:ping><description><![CDATA[
		<!--StartFragment --> 很多人对二级缓存都不太了解，或者是有错误的认识，我一直想写一篇文章介绍一下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 />Class的缓存 <br />对于一条记录，也就是一个PO来说，是根据ID来找的，缓存的key就是ID，value是POJO。无论list，load还是iterate，只要读出一个对象，都会填充缓存。但是list不会使用缓存，而iterate会先取数据库select id出来，然后一个id一个id的load，如果在缓存里面有，就从缓存取，没有的话就去数据库load。假设是读写缓存，需要设置： <br /><cache &nbsp;usage="read-write"> <br />如果你使用的二级缓存实现是ehcache的话，需要配置ehcache.xml <br /><cache &nbsp="" &nbsp;overflowtodisk="true" &nbsp;timetoidleseconds="3600" &nbsp;timetoliveseconds="7200" &nbsp;eternal="false" &nbsp;maxelementsinmemory="500" &nbsp;name="com.xxx.pojo.Foo"> <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 />查询缓存 <br />首先需要配置hibernate.cache.use_query_cache=true <br />如果用ehcache，配置ehcache.xml，注意hibernate3.0以后不是net.sf的包名了 <br /><cache &nbsp="" &nbsp;name="net.sf.hibernate.cache.StandardQueryCache"><br />maxElementsInMemory="50" eternal="false" timeToIdleSeconds="3600" <br />timeToLiveSeconds="7200" overflowToDisk="true"/&gt; <br /><cache &nbsp="" &nbsp;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 /><cache &nbsp="" &nbsp;overflowtodisk="true" &nbsp;timetoidleseconds="3600" &nbsp;timetoliveseconds="7200" &nbsp;eternal="false" &nbsp;maxelementsinmemory="10" &nbsp;name="myCacheRegion"> <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 />这里还有一个很容易被忽视的重要问题，即打开查询缓存以后，即使是list方法也可能遇到1+N的问题！相同条件第一次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 />Collection缓存 <br />需要在hbm的collection里面设置 <br /><cache &nbsp;usage="read-write"> <br />假如class是Cat，collection叫children，那么ehcache里面配置 <br /><cache &nbsp="" &nbsp;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 />缓存策略 <br />只读缓存（read-only）：没有什么好说的 <br />读/写缓存（read-write）:程序可能要的更新数据 <br />不严格的读/写缓存（nonstrict-read-write）：需要更新数据，但是两个事务更新同一条记录的可能性很小，性能比读写缓存好 <br />事务缓存（transactional）：缓存支持事务，发生异常的时候，缓存也能够回滚，只支持jta环境，这个我没有怎么研究过 <br /><br />读写缓存和不严格读写缓存在实现上的区别在于，读写缓存更新缓存的时候会把缓存里面的数据换成一个锁，其他事务如果去取相应的缓存数据，发现被锁住了，然后就直接取数据库查询。 <br />在hibernate2.1的ehcache实现中，如果锁住部分缓存的事务发生了异常，那么缓存会一直被锁住，直到60秒后超时。 <br />不严格读写缓存不锁定缓存中的数据。 <br /><br /><br />使用二级缓存的前置条件 <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 />总结： <br />不要想当然的以为缓存一定能提高性能，仅仅在你能够驾驭它并且条件合适的情况下才是这样的。hibernate的二级缓存限制还是比较多的，不方便用jdbc可能会大大的降低更新性能。在不了解原理的情况下乱用，可能会有1+N的问题。不当的使用还可能导致读出脏数据。 <br />如果受不了hibernate的诸多限制，那么还是自己在应用程序的层面上做缓存吧。 <br />在越高的层面上做缓存，效果就会越好。就好像尽管磁盘有缓存，数据库还是要实现自己的缓存，尽管数据库有缓存，咱们的应用程序还是要做缓存。因为底层的缓存它并不知道高层要用这些数据干什么，只能做的比较通用，而高层可以有针对性的实现缓存，所以在更高的级别上做缓存，效果也要好些吧。 </cache></cache></cache></cache></cache></cache></cache><img src ="http://www.blogjava.net/renyangok/aggbug/85411.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-04 17:22 <a href="http://www.blogjava.net/renyangok/archive/2006/12/04/85411.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>