﻿<?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/yaozhuan/category/9635.html</link><description /><language>zh-cn</language><lastBuildDate>Fri, 02 Mar 2007 07:40:04 GMT</lastBuildDate><pubDate>Fri, 02 Mar 2007 07:40:04 GMT</pubDate><ttl>60</ttl><item><title>JDBC+Hibernate将Blob数据写入Oracle[摘] </title><link>http://www.blogjava.net/yaozhuan/articles/80394.html</link><dc:creator>砖头</dc:creator><author>砖头</author><pubDate>Fri, 10 Nov 2006 06:41:00 GMT</pubDate><guid>http://www.blogjava.net/yaozhuan/articles/80394.html</guid><description><![CDATA[
		<div class="postbody">
				<font size="2">        Oracle的Blob字段比较特殊，他比long字段的性能要好很多，可以用来保存例如图片之类的二进制数据。 <br /><br />　　写入Blob字段和写入其它类型字段的方式非常不同，因为Blob自身有一个cursor，你必须使用cursor对blob进行操作，因而你在写入Blob之前，必须获得cursor才能进行写入，那么如何获得Blob的cursor呢？ <br /><br />　　这需要你先插入一个empty的blob，这将创建一个blob的cursor，然后你再把这个empty的blob的cursor用select查询出来，这样通过两步操作，你就获得了blob的cursor，可以真正的写入blob数据了。 <br /><br />　　看下面的JDBC的demo，把oraclejdbc.jar这个二进制文件写入数据库表javatest的content字段(这是一个blob型字段) <br /><br />　　import java.sql.*; <br />　　import java.io.*; <br />　　import oracle.sql.*; <br />　　public class WriteBlob { <br /><br />　　public static void main(String[] args) { <br /><br />　　try { <br />　　DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver()); <br />　　Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl","fankai","fankai"); <br />　　conn.setAutoCommit(false); <br /><br />　　BLOB blob = null; <br /><br />　　PreparedStatement pstmt = conn.prepareStatement("insert into javatest(name,content) values(?,empty_blob())"); <br />　　pstmt.setString(1,"fankai"); <br />　　pstmt.executeUpdate(); <br />　　pstmt.close(); <br /><br />　　pstmt = conn.prepareStatement("select content from javatest where name= ? for update"); <br />　　pstmt.setString(1,"fankai"); <br />　　ResultSet rset = pstmt.executeQuery(); <br />　　if (rset.next()) blob = (BLOB) rset.getBlob(1); <br /><br />　　String fileName = "oraclejdbc.jar"; <br />　　File f = new File(fileName); <br />　　FileInputStream fin = new FileInputStream(f); <br />　　System.out.println("file size = " + fin.available()); <br /><br />　　pstmt = conn.prepareStatement("update javatest set content=? where name=?"); <br /><br />　　OutputStream out = blob.getBinaryOutputStream(); <br /><br />　　int count = -1, total = 0; <br />　　byte[] data = new byte[(int)fin.available()]; <br />　　fin.read(data); <br />　　out.write(data); <br />　　/* <br />　　byte[] data = new byte[blob.getBufferSize()]; 另一种实现方法,节省内存 <br />　　while ((count = fin.read(data)) != -1) { <br />　　　total += count; <br />　　　out.write(data, 0, count); <br />　　} <br />　　*/ <br /><br />　　fin.close(); <br />　　out.close(); <br /><br />　　pstmt.setBlob(1,blob); <br />　　pstmt.setString(2,"fankai"); <br /><br />　　pstmt.executeUpdate(); <br />　　pstmt.close(); <br /><br />　　conn.commit(); <br />　　conn.close(); <br />　　} catch (SQLException e) { <br />　　 　System.err.println(e.getMessage()); <br />　　e.printStackTrace(); <br />　　} catch (IOException e) { <br />　　System.err.println(e.getMessage()); <br />　　} <br />　　} <br /><br />　　} <br /><br />　　仔细看上例，分三步： <br /><br />　</font>
				<font size="2">
						<strong>　1、插入空blob <br /></strong>
						<br />　　into javatest(name,content) values(?,empty_blob()); <br /><br />　</font>
				<font size="2">
						<strong>　2、获得blob的cursor <br /></strong>
						<br />　　select content from javatest where name= ? for update; <br /><br />　　注意！！！必须加for update，这将锁定该行，直至该行被修改完毕，保证不产生并发冲突。 <br /><br />　　</font>
				<font size="2">
						<strong>3、update javatest set content=? where name= <br /></strong>
						<br />　　用cursor往数据库写数据 <br /><br />　　这里面还有一点要提醒大家： <br /><br />　　JDK1.3带的JDBC2.0规范是不完善的，只有读Blob的接口，而没有写Blob的接口，JDK1.4带的JDBC3.0加入了写Blob的接口。你可以使用JDBC3.0的接口，也可以直接使用Oracle的JDBC的API，我在上例中使用了Oracle的JDBC的API。 <br /><br />　　另外要注意的是： <br /><br />　　java.sql.Blob <br /><br />　　oracle.sql.BLOB <br /><br />　　注意看blob的大小写，是不一样的。写程序的时候不要搞混了。 <br /><br />　　下面看看用Hibernate怎么写，原理是一样的，也要分三步，但是代码简单很多 <br /><br />　　这是Cat对象定义 <br /><br />　　package com.fankai; <br /><br />　　import java.sql.Blob; <br /><br />　　public class Cat { <br />　　private String id; <br />　　private String name; <br />　　private char sex; <br />　　private float weight; <br />　　private Blob image; <br />　　public Cat() { } <br /><br />　　public String getId() { return id; } <br />　　public void setId(String id) { this.id = id; } <br /><br />　　public String getName() { return name; } <br />　　public void setName(String name) { this.name = name; } <br /><br />　　public char getSex() { return sex; } <br />　　public void setSex(char sex) { this.sex = sex; } <br /><br />　　public float getWeight() { return weight; } <br />　　public void setWeight(float weight) { this.weight = weight; } <br /><br />　　public Blob getImage() { return image; } <br />　　public void setImage(Blob image) { this.image = image;} <br />　　} <br /><br /><br />　　这是Cat.hbm.xml <br /><br />　　＜?xml version="1.0"?＞ <br />　　＜!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd"＞ <br /><br />　　＜hibernate-mapping＞ <br />　　＜class name="com.fankai.Cat" table="cat"＞ <br />　　＜!--jcs-cache usage="read-only"/--＞ <br />　　＜id name="id" unsaved-value="null"＞ <br />　　＜generator class="uuid.hex"/＞ <br />　　＜/id＞ <br />　　＜property name="name" length="16" not-null="true"/＞ <br />　　＜property name="sex" length="1" not-null="true"/＞ <br />　　＜property name="weight" /＞ <br />　　＜property name="image" /＞ <br />　　＜/class＞ <br />　　＜/hibernate-mapping＞ <br /><br />　　下面是完整的用Hibernate写入Blob的例子，相比JDBC，已经简单轻松多了，也不用写那些Oracle特殊的sql了： <br /><br />　　package com.fankai; <br /><br />　　import java.sql.Blob; <br />　　import net.sf.hibernate.*; <br />　　import oracle.sql.*; <br />　　import java.io.*; <br /><br />　　public class TestCatHibernate { <br />　　public static void testBlob() { <br />　　Session s = null; <br />　　byte[] buffer = new byte[1]; <br />　　buffer[0] = 1; <br />　　try { <br />　　　SessionFactory sf = HibernateSessionFactory.getSessionFactory(); <br />　　　s = sf.openSession(); <br />　　　Transaction tx = s.beginTransaction(); <br />　　　Cat c = new Cat(); <br />　　　c.setName("Robbin"); <br />　　　c.setImage(Hibernate.createBlob(buffer)); <br />　　　s.save(c); <br />　　　s.flush(); <br />　　　s.refresh(c, LockMode.UPGRADE); <br />　　　BLOB blob = (BLOB) c.getImage(); <br />　　　OutputStream out = blob.getBinaryOutputStream(); <br />　　　String fileName = "oraclejdbc.jar"; <br />　　　File f = new File(fileName); <br />　　　FileInputStream fin = new FileInputStream(f); <br />　　　int count = -1, total = 0; <br />　　　byte[] data = new byte[(int)fin.available()]; <br />　　　fin.read(data); <br />　　　out.write(data); <br />　　　fin.close(); <br />　　　out.close(); <br />　　　s.flush(); <br />　　　tx.commit(); <br /><br />　　} catch (Exception e) { <br />　　　System.out.println(e.getMessage()); <br />　　} finally { <br />　　　if (s != null) <br />　　　try { <br />　　　　s.close(); <br />　　　} catch (Exception e) {} <br />　　} <br />　　} <br />　　}<br clear="all" /></font>
		</div>
<img src ="http://www.blogjava.net/yaozhuan/aggbug/80394.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yaozhuan/" target="_blank">砖头</a> 2006-11-10 14:41 <a href="http://www.blogjava.net/yaozhuan/articles/80394.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>于Hibernate3的escape解决方法 (转)</title><link>http://www.blogjava.net/yaozhuan/articles/75542.html</link><dc:creator>砖头</dc:creator><author>砖头</author><pubDate>Tue, 17 Oct 2006 02:02:00 GMT</pubDate><guid>http://www.blogjava.net/yaozhuan/articles/75542.html</guid><description><![CDATA[不知道大家有没有碰到，还是没有这种需求。就是用like来查询，我们没有用Lucene,Compass这种全文索引的方案,我们只是简单的添加%进行like查询。用户搜索的时候就使用*和?来代表任意和一个。所以要对"%"和"_"进行转义，我们使用的是oracle数据库。sql语句看起来可能是这样的。<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #0000ff">SELECT</span><span style="COLOR: #000000"> </span><span style="COLOR: #808080">*</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">FROM</span><span style="COLOR: #000000"> t_user </span><span style="COLOR: #0000ff">where</span><span style="COLOR: #000000"> nickname </span><span style="COLOR: #808080">like</span><span style="COLOR: #000000"> </span><span style="COLOR: #ff0000">'</span><span style="COLOR: #ff0000">%Goo\_D</span><span style="COLOR: #ff0000">'</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">escape</span><span style="COLOR: #000000"> </span><span style="COLOR: #ff0000">'</span><span style="COLOR: #ff0000">\</span><span style="COLOR: #ff0000">'</span></div>这里对_进行转义了。因为用户昵称包含下划线，如果不进行转义就表示一个任意字符。有时候我们可能还需要对%进行转义。同样的方法在%前加\% 但是比起普通的like语句。多了一个声明转义符的语句。所以我们会想到这样的语句<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #000000">DetachedCriteria criteria </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> DetachedCriteria.forClass(User.</span><span style="COLOR: #0000ff">class</span><span style="COLOR: #000000">);<br />criteria.add(Restrictions.like(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">nickname</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, user.getNickname()</span><span style="COLOR: #000000">+</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">' escape'\</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">));</span></div>但是这样是不管用的。<br />接下来可能会想到使用Hibernate3的原生sql查询，其实我们不需要这样做。我们还是使用Criteria条件查询。<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #000000">criteria.add(Restrictions.sqlRestriction(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">{alias}.nickname like ? escape'/'</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, StringUtil.escapeSQLLike(user.getNickname()), Hibernate.STRING));</span></div>这样Hibernate产生的语句就是我们想要的语句了。<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #000000">    </span><span style="COLOR: #008000">/**</span><span style="COLOR: #008000"><br />     * 转义like语句中的<br />     * &lt;code&gt;'_'&lt;/code&gt;&lt;code&gt;'%'&lt;/code&gt;<br />     * 将&lt;code&gt;'?'&lt;/code&gt;转成sql的&lt;code&gt;'/_'&lt;/code&gt;<br />     * 将&lt;code&gt;'%'&lt;/code&gt;转成sql的&lt;code&gt;'/%'&lt;/code&gt;<br />     * &lt;p&gt;<br />     *   例如搜索&lt;code&gt;?aa*bb?c_d%f&lt;/code&gt;将转化成&lt;br/&gt;<br />     *   &lt;code&gt;_aa%bb_c/_d/%f&lt;/code&gt;<br />     * &lt;/p&gt;<br />     * </span><span style="COLOR: #808080">@param</span><span style="COLOR: #008000"> likeStr<br />     * </span><span style="COLOR: #808080">@return</span><span style="COLOR: #008000"><br />     * </span><span style="COLOR: #808080">@author</span><span style="COLOR: #008000"> &lt;a href="</span><span style="COLOR: #008000; TEXT-DECORATION: underline">http://jdkcn.com</span><span style="COLOR: #008000">"&gt;somebody&lt;/a&gt;<br />     </span><span style="COLOR: #008000">*/</span><span style="COLOR: #000000"><br />    </span><span style="COLOR: #0000ff">public</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">static</span><span style="COLOR: #000000"> String escapeSQLLike(String likeStr) {<br />        String str </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> StringUtils.replace(likeStr, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">_</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">/_</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br />        str </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> StringUtils.replace(str, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">%</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,    </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">/%</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br />        str </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> StringUtils.replace(str, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">?</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">_</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br />        str </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> StringUtils.replace(str, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">%</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br />        </span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> str;<br />    }</span></div><img src ="http://www.blogjava.net/yaozhuan/aggbug/75542.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yaozhuan/" target="_blank">砖头</a> 2006-10-17 10:02 <a href="http://www.blogjava.net/yaozhuan/articles/75542.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>浅谈Hibernate的扩展点</title><link>http://www.blogjava.net/yaozhuan/articles/39742.html</link><dc:creator>砖头</dc:creator><author>砖头</author><pubDate>Fri, 07 Apr 2006 02:18:00 GMT</pubDate><guid>http://www.blogjava.net/yaozhuan/articles/39742.html</guid><wfw:comment>http://www.blogjava.net/yaozhuan/comments/39742.html</wfw:comment><comments>http://www.blogjava.net/yaozhuan/articles/39742.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yaozhuan/comments/commentRss/39742.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yaozhuan/services/trackbacks/39742.html</trackback:ping><description><![CDATA[
		<p class="date">benja | 19 十月, 2005 17:27</p>
		<div>
				<p>
						<font size="2">　　一个好的软件产品应该具有好的扩展性。Hibernate在这方面无疑做得不错，下面我们就看看Hibernate(3.0)的扩展点有哪些：</font>
				</p>
				<ul>
						<li>
								<font size="2">对象标识符生成：org.hibernate.id.IdentifierGenerator接口</font>
						</li>
						<li>
								<font size="2">SQL方言：org.hibernate.dialect.Dialect抽象类</font>
						</li>
						<li>
								<font size="2">缓存机制：org.hibernate.cache.CacheProvider接口和org.hibernate.cache.Cache接口</font>
						</li>
						<li>
								<font size="2">JDBC连接管理：org.hibernate.connection.ConnectionProvider接口</font>
						</li>
						<li>
								<font size="2">事务管理：org.hibernate.transaction.TransactionFactory接口、org.hibernate.Transaction接口、org.hibernate.transaction.TransactionManagerLookup接口</font>
						</li>
						<li>
								<font size="2">代理定制：org.hibernate.proxy.ProxyFactory接口、org.hibernate.proxy.HibernateProxy接口</font>
						</li>
						<li>
								<font size="2">用户自定义映射类型：org.hibernate.usertype.UserType接口、org.hibernate.usertype.CompositeUserType接口、org.hibernate.usertype.ParameterizedType接口等</font>
						</li>
						<li>
								<font size="2">ORM策略：org.hibernate.persister.entity.EntityPersister接口</font>
						</li>
				</ul>
				<p>
						<font size="2">　　以后有空再对每个扩展点做深入说明。你也不妨以此和其他的ORM产品对比一下。</font>
				</p>
		</div>
<img src ="http://www.blogjava.net/yaozhuan/aggbug/39742.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yaozhuan/" target="_blank">砖头</a> 2006-04-07 10:18 <a href="http://www.blogjava.net/yaozhuan/articles/39742.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate的缓存机制介绍</title><link>http://www.blogjava.net/yaozhuan/articles/39726.html</link><dc:creator>砖头</dc:creator><author>砖头</author><pubDate>Fri, 07 Apr 2006 01:49:00 GMT</pubDate><guid>http://www.blogjava.net/yaozhuan/articles/39726.html</guid><wfw:comment>http://www.blogjava.net/yaozhuan/comments/39726.html</wfw:comment><comments>http://www.blogjava.net/yaozhuan/articles/39726.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yaozhuan/comments/commentRss/39726.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yaozhuan/services/trackbacks/39726.html</trackback:ping><description><![CDATA[
		<p class="date">
				<font size="2">benja | 20 二月, 2006 17:43</font>
		</p>
		<div>
				<p>
						<span style="FONT-FAMILY: 宋体">
								<font size="2">　　缓存是介于应用程序和物理数据源之间，其作用是为了降低应用程序对物理数据源访问的频次，从而提高了应用的运行性能。缓存内的数据是对物理数据源中的数据的复制，应用程序在运行时从缓存读写数据，在特定的时刻或事件会同步缓存和物理数据源的数据。</font>
						</span>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>　　</span>
								</span>
								<span style="FONT-FAMILY: 宋体">缓存的介质一般是内存，所以读写速度很快。但如果缓存中存放的数据量非常大时，也会用硬盘作为缓存介质。缓存的实现不仅仅要考虑存储的介质，还要考虑到管理缓存的并发访问和缓存数据的生命周期。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>　　</span>Hibernate</span>
								<span style="FONT-FAMILY: 宋体">的缓存包括</span>
								<span lang="EN-US">Session</span>
								<span style="FONT-FAMILY: 宋体">的缓存和</span>
								<span lang="EN-US">SessionFactory</span>
								<span style="FONT-FAMILY: 宋体">的缓存，其中</span>
								<span lang="EN-US">SessionFactory</span>
								<span style="FONT-FAMILY: 宋体">的缓存又可以分为两类：内置缓存和外置缓存。</span>
								<span lang="EN-US">Session</span>
								<span style="FONT-FAMILY: 宋体">的缓存是内置的，不能被卸载，也被称为</span>
								<span lang="EN-US">Hibernate</span>
								<span style="FONT-FAMILY: 宋体">的第一级缓存。</span>
								<span lang="EN-US">SessionFactory</span>
								<span style="FONT-FAMILY: 宋体">的内置缓存和</span>
								<span lang="EN-US">Session</span>
								<span style="FONT-FAMILY: 宋体">的缓存在实现方式上比较相似，前者是</span>
								<span lang="EN-US">SessionFactory</span>
								<span style="FONT-FAMILY: 宋体">对象的一些集合属性包含的数据，后者是指</span>
								<span lang="EN-US">Session</span>
								<span style="FONT-FAMILY: 宋体">的一些集合属性包含的数据。</span>
								<span lang="EN-US">SessionFactory</span>
								<span style="FONT-FAMILY: 宋体">的内置缓存中存放了映射元数据和预定义</span>
								<span lang="EN-US">SQL</span>
								<span style="FONT-FAMILY: 宋体">语句，映射元数据是映射文件中数据的拷贝，而预定义</span>
								<span lang="EN-US">SQL</span>
								<span style="FONT-FAMILY: 宋体">语句是在</span>
								<span lang="EN-US">Hibernate</span>
								<span style="FONT-FAMILY: 宋体">初始化阶段根据映射元数据推导出来，</span>
								<span lang="EN-US">SessionFactory</span>
								<span style="FONT-FAMILY: 宋体">的内置缓存是只读的，应用程序不能修改缓存中的映射元数据和预定义</span>
								<span lang="EN-US">SQL</span>
								<span style="FONT-FAMILY: 宋体">语句，因此</span>
								<span lang="EN-US">SessionFactory</span>
								<span style="FONT-FAMILY: 宋体">不需要进行内置缓存与映射文件的同步。</span>
								<span lang="EN-US">SessionFactory</span>
								<span style="FONT-FAMILY: 宋体">的外置缓存是一个可配置的插件。在默认情况下，</span>
								<span lang="EN-US">SessionFactory</span>
								<span style="FONT-FAMILY: 宋体">不会启用这个插件。外置缓存的数据是数据库数据的拷贝，外置缓存的介质可以是内存或者硬盘。</span>
								<span lang="EN-US">SessionFactory</span>
								<span style="FONT-FAMILY: 宋体">的外置缓存也被称为</span>
								<span lang="EN-US">Hibernate</span>
								<span style="FONT-FAMILY: 宋体">的第二级缓存。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>　　</span>Hibernate</span>
								<span style="FONT-FAMILY: 宋体">的这两级缓存都位于持久化层，存放的都是数据库数据的拷贝，那么它们之间的区别是什么呢？为了理解二者的区别，需要深入理解持久化层的缓存的两个特性：缓存的范围和缓存的并发访问策略。</span>
						</font>
				</p>
				<p>
						<b>
								<span style="FONT-FAMILY: 宋体">
										<font size="2">持久化层的缓存的范围</font>
								</span>
						</b>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>　　</span>
								</span>
								<span style="FONT-FAMILY: 宋体">缓存的范围决定了缓存的生命周期以及可以被谁访问。缓存的范围分为三类。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>　　</span>1 </span>
								<span style="FONT-FAMILY: 宋体">事务范围：缓存只能被当前事务访问。缓存的生命周期依赖于事务的生命周期，当事务结束时，缓存也就结束生命周期。在此范围下，缓存的介质是内存。事务可以是数据库事务或者应用事务，每个事务都有独自的缓存，缓存内的数据通常采用相互关联的的对象形式。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>　　</span>2 </span>
								<span style="FONT-FAMILY: 宋体">进程范围：缓存被进程内的所有事务共享。这些事务有可能是并发访问缓存，因此必须对缓存采取必要的事务隔离机制。缓存的生命周期依赖于进程的生命周期，进程结束时，缓存也就结束了生命周期。进程范围的缓存可能会存放大量的数据，所以存放的介质可以是内存或硬盘。缓存内的数据既可以是相互关联的对象形式也可以是对象的松散数据形式。松散的对象数据形式有点类似于对象的序列化数据，但是对象分解为松散的算法比对象序列化的算法要求更快。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>　　</span>3 </span>
								<span style="FONT-FAMILY: 宋体">集群范围：在集群环境中，缓存被一个机器或者多个机器的进程共享。缓存中的数据被复制到集群环境中的每个进程节点，进程间通过远程通信来保证缓存中的数据的一致性，缓存中的数据通常采用对象的松散数据形式。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>　　</span>
								</span>
								<span style="FONT-FAMILY: 宋体">对大多数应用来说，应该慎重地考虑是否需要使用集群范围的缓存，因为访问的速度不一定会比直接访问数据库数据的速度快多少。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>　　</span>
								</span>
								<span style="FONT-FAMILY: 宋体">持久化层可以提供多种范围的缓存。如果在事务范围的缓存中没有查到相应的数据，还可以到进程范围或集群范围的缓存内查询，如果还是没有查到，那么只有到数据库中查询。事务范围的缓存是持久化层的第一级缓存，通常它是必需的；进程范围或集群范围的缓存是持久化层的第二级缓存，通常是可选的。</span>
						</font>
				</p>
				<p>
						<b>
								<span style="FONT-FAMILY: 宋体">
										<font size="2">持久化层的缓存的并发访问策略</font>
								</span>
						</b>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>　　</span>
								</span>
								<span style="FONT-FAMILY: 宋体">当多个并发的事务同时访问持久化层的缓存的相同数据时，会引起并发问题，必须采用必要的事务隔离措施。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>　　</span>
								</span>
								<span style="FONT-FAMILY: 宋体">在进程范围或集群范围的缓存，即第二级缓存，会出现并发问题。因此可以设定以下四种类型的并发访问策略，每一种策略对应一种事务隔离级别。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>　　</span>
								</span>
								<span style="FONT-FAMILY: 宋体">事务型：仅仅在受管理环境中适用。它提供了</span>
								<span lang="EN-US">Repeatable Read</span>
								<span style="FONT-FAMILY: 宋体">事务隔离级别。对于经常被读但很少修改的数据，可以采用这种隔离类型，因为它可以防止脏读和不可重复读这类的并发问题。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>　　</span>
								</span>
								<span style="FONT-FAMILY: 宋体">读写型：提供了</span>
								<span lang="EN-US">Read Committed</span>
								<span style="FONT-FAMILY: 宋体">事务隔离级别。仅仅在非集群的环境中适用。对于经常被读但很少修改的数据，可以采用这种隔离类型，因为它可以防止脏读这类的并发问题。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>　　</span>
								</span>
								<span style="FONT-FAMILY: 宋体">非严格读写型：不保证缓存与数据库中数据的一致性。如果存在两个事务同时访问缓存中相同数据的可能，必须为该数据配置一个很短的数据过期时间，从而尽量避免脏读。对于极少被修改，并且允许偶尔脏读的数据，可以采用这种并发访问策略。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>　　</span>
								</span>
								<span style="FONT-FAMILY: 宋体">只读型：对于从来不会修改的数据，如参考数据，可以使用这种并发访问策略。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>　　</span>
								</span>
								<span style="FONT-FAMILY: 宋体">事务型并发访问策略是事务隔离级别最高，只读型的隔离级别最低。事务隔离级别越高，并发性能就越低。</span>
						</font>
				</p>
				<p>
						<b>
								<span style="FONT-FAMILY: 宋体">
										<font size="2">什么样的数据适合存放到第二级缓存中？</font>
								</span>
						</b>
				</p>
				<p>
						<font size="2">
								<b>
										<span style="FONT-FAMILY: 宋体">1 </span>
								</b>
								<span style="FONT-FAMILY: 宋体">很少被修改的数据</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span style="FONT-FAMILY: 宋体">
										<strong>2 </strong>
								</span>
								<span style="FONT-FAMILY: 宋体">不是很重要的数据，允许出现偶尔并发的数据</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span style="FONT-FAMILY: 宋体">
										<strong>3</strong>
								</span>
								<span style="FONT-FAMILY: 宋体">不会被并发访问的数据</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span style="FONT-FAMILY: 宋体">
										<strong>4</strong>
								</span>
								<span lang="EN-US" style="FONT-FAMILY: Wingdings">
										<span>
												<span>
												</span>
										</span>
								</span>
								<span style="FONT-FAMILY: 宋体">参考数据</span>
						</font>
				</p>
				<p>
						<b>
								<span style="FONT-FAMILY: 宋体">
										<font size="2">不适合存放到第二级缓存的数据？</font>
								</span>
						</b>
				</p>
				<p>
						<font size="2">
								<b>
										<span style="FONT-FAMILY: 宋体">1 </span>
								</b>
								<span style="FONT-FAMILY: 宋体">经常被修改的数据</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span style="FONT-FAMILY: 宋体">
										<strong>2 </strong>
								</span>
								<span style="FONT-FAMILY: 宋体">财务数据，绝对不允许出现并发</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span style="FONT-FAMILY: 宋体">
										<strong>3</strong>
								</span>
								<span style="FONT-FAMILY: 宋体">与其他应用共享的数据。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<b>
										<span lang="EN-US">Hibernate</span>
								</b>
								<b>
										<span style="FONT-FAMILY: 宋体">的二级缓存</span>
								</b>
						</font>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>　　</span>
								</span>
								<span style="FONT-FAMILY: 宋体">如前所述，</span>
								<span lang="EN-US">Hibernate</span>
								<span style="FONT-FAMILY: 宋体">提供了两级缓存，第一级是</span>
								<span lang="EN-US">Session</span>
								<span style="FONT-FAMILY: 宋体">的缓存。由于</span>
								<span lang="EN-US">Session</span>
								<span style="FONT-FAMILY: 宋体">对象的生命周期通常对应一个数据库事务或者一个应用事务，因此它的缓存是事务范围的缓存。第一级缓存是必需的，不允许而且事实上也无法比卸除。在第一级缓存中，持久化类的每个实例都具有唯一的</span>
								<span lang="EN-US">OID</span>
								<span style="FONT-FAMILY: 宋体">。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>　　</span>
								</span>
								<span style="FONT-FAMILY: 宋体">第二级缓存是一个可插拔的的缓存插件，它是由</span>
								<span lang="EN-US">SessionFactory</span>
								<span style="FONT-FAMILY: 宋体">负责管理。由于</span>
								<span lang="EN-US">SessionFactory</span>
								<span style="FONT-FAMILY: 宋体">对象的生命周期和应用程序的整个过程对应，因此第二级缓存是进程范围或者集群范围的缓存。这个缓存中存放的对象的松散数据。第二级对象有可能出现并发问题，因此需要采用适当的并发访问策略，该策略为被缓存的数据提供了事务隔离级别。缓存适配器用于把具体的缓存实现软件与</span>
								<span lang="EN-US">Hibernate</span>
								<span style="FONT-FAMILY: 宋体">集成。第二级缓存是可选的，可以在每个类或每个集合的粒度上配置第二级缓存。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<b>
										<span lang="EN-US">Hibernate</span>
								</b>
								<b>
										<span style="FONT-FAMILY: 宋体">的二级缓存策略的一般过程如下：</span>
								</b>
						</font>
				</p>
				<p>
						<b>
								<span style="FONT-FAMILY: 宋体">
								</span>
						</b>
						<font size="2">
								<span lang="EN-US">
										<span>1)<span></span></span>
								</span>
								<span style="FONT-FAMILY: 宋体">条件查询的时候，总是发出一条</span>
								<span lang="EN-US">select * from table_name where …. </span>
								<span style="FONT-FAMILY: 宋体">（选择所有字段）这样的</span>
								<span lang="EN-US">SQL</span>
								<span style="FONT-FAMILY: 宋体">语句查询数据库，一次获得所有的数据对象。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>2)<span></span></span>
								</span>
								<span style="FONT-FAMILY: 宋体">把获得的所有数据对象根据</span>
								<span lang="EN-US">ID</span>
								<span style="FONT-FAMILY: 宋体">放入到第二级缓存中。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>3)<span></span></span>
								</span>
								<span style="FONT-FAMILY: 宋体">当</span>
								<span lang="EN-US">Hibernate</span>
								<span style="FONT-FAMILY: 宋体">根据</span>
								<span lang="EN-US">ID</span>
								<span style="FONT-FAMILY: 宋体">访问数据对象的时候，首先从</span>
								<span lang="EN-US">Session</span>
								<span style="FONT-FAMILY: 宋体">一级缓存中查；查不到，如果配置了二级缓存，那么从二级缓存中查；查不到，再查询数据库，把结果按照</span>
								<span lang="EN-US">ID</span>
								<span style="FONT-FAMILY: 宋体">放入到缓存。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>4)<span></span></span>
								</span>
								<span style="FONT-FAMILY: 宋体">删除、更新、增加数据的时候，同时更新缓存。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US">
										<span>　　</span>Hibernate</span>
								<span style="FONT-FAMILY: 宋体">的二级缓存策略，是针对于</span>
								<span lang="EN-US">ID</span>
								<span style="FONT-FAMILY: 宋体">查询的缓存策略，对于条件查询则毫无作用。为此，</span>
								<span lang="EN-US">Hibernate</span>
								<span style="FONT-FAMILY: 宋体">提供了针对条件查询的</span>
								<span lang="EN-US">Query</span>
								<span style="FONT-FAMILY: 宋体">缓存。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<strong>
										<span lang="EN-US">Hibernate</span>
								</strong>
								<strong>
										<span style="FONT-FAMILY: 宋体">的</span>
										<span lang="EN-US">Query</span>
								</strong>
								<strong>
										<span style="FONT-FAMILY: 宋体">缓存策略的过程如下：</span>
								</strong>
						</font>
				</p>
				<p>
						<strong>
								<span style="FONT-FAMILY: 宋体">
								</span>
						</strong>
						<font size="2">
								<span lang="EN-US">
										<span>1)<span></span></span>
								</span>
								<span lang="EN-US">Hibernate</span>
								<span style="FONT-FAMILY: 宋体">首先根据这些信息组成一个</span>
								<span lang="EN-US">Query Key</span>
								<span style="FONT-FAMILY: 宋体">，</span>
								<span lang="EN-US">Query Key</span>
								<span style="FONT-FAMILY: 宋体">包括条件查询的请求一般信息：</span>
								<span lang="EN-US">SQL, SQL</span>
								<span style="FONT-FAMILY: 宋体">需要的参数，记录范围（起始位置</span>
								<span lang="EN-US">rowStart</span>
								<span style="FONT-FAMILY: 宋体">，最大记录个数</span>
								<span lang="EN-US">maxRows)</span>
								<span style="FONT-FAMILY: 宋体">，等。</span>
								<span lang="EN-US">
								</span>
						</font>
				</p>
				<p>
						<span lang="EN-US">
						</span>
						<font size="2">
								<span lang="EN-US">
										<span>2)<span></span></span>
								</span>
								<span lang="EN-US">Hibernate</span>
								<span style="FONT-FAMILY: 宋体">根据这个</span>
								<span lang="EN-US">Query Key</span>
								<span style="FONT-FAMILY: 宋体">到</span>
								<span lang="EN-US">Query</span>
								<span style="FONT-FAMILY: 宋体">缓存中查找对应的结果列表。如果存在，那么返回这个结果列表；如果不存在，查询数据库，获取结果列表，把整个结果列表根据</span>
								<span lang="EN-US">Query Key</span>
								<span style="FONT-FAMILY: 宋体">放入到</span>
								<span lang="EN-US">Query</span>
								<span style="FONT-FAMILY: 宋体">缓存中。</span>
						</font>
				</p>
				<p>
						<font size="2">
								<span lang="EN-US" style="FONT-SIZE: 10.5pt; FONT-FAMILY: 'Times New Roman'">
										<strong>3) </strong>Query Key</span>
								<span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">中的</span>
								<span lang="EN-US" style="FONT-SIZE: 10.5pt; FONT-FAMILY: 'Times New Roman'">SQL</span>
								<span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">涉及到一些表名，如果这些表的任何数据发生修改、删除、增加等操作，这些相关的</span>
								<span lang="EN-US" style="FONT-SIZE: 10.5pt; FONT-FAMILY: 'Times New Roman'">Query Key</span>
								<span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体">都要从缓存中清空。</span>
						</font>
				</p>
		</div>
<img src ="http://www.blogjava.net/yaozhuan/aggbug/39726.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yaozhuan/" target="_blank">砖头</a> 2006-04-07 09:49 <a href="http://www.blogjava.net/yaozhuan/articles/39726.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> [转载]hibernate二级缓存攻略 </title><link>http://www.blogjava.net/yaozhuan/articles/39724.html</link><dc:creator>砖头</dc:creator><author>砖头</author><pubDate>Fri, 07 Apr 2006 01:47:00 GMT</pubDate><guid>http://www.blogjava.net/yaozhuan/articles/39724.html</guid><wfw:comment>http://www.blogjava.net/yaozhuan/comments/39724.html</wfw:comment><comments>http://www.blogjava.net/yaozhuan/articles/39724.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yaozhuan/comments/commentRss/39724.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yaozhuan/services/trackbacks/39724.html</trackback:ping><description><![CDATA[
		<div class="content">
				<font size="2">很多人对二级缓存都不太了解，或者是有错误的认识，我一直想写一篇文章介绍一下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 />&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 />查询缓存  <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 />这里还有一个很容易被忽视的重要问题，即打开查询缓存以后，即使是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 />&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 />缓存策略  <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 />在越高的层面上做缓存，效果就会越好。就好像尽管磁盘有缓存，数据库还是要实现自己的缓存，尽管数据库有缓存，咱们的应用程序还是要做缓存。因为底层的缓存它并不知道高层要用这些数据干什么，只能做的比较通用，而高层可以有针对性的实现缓存，所以在更高的级别上做缓存，效果也要好些吧。</font>
		</div>
<img src ="http://www.blogjava.net/yaozhuan/aggbug/39724.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yaozhuan/" target="_blank">砖头</a> 2006-04-07 09:47 <a href="http://www.blogjava.net/yaozhuan/articles/39724.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate获取数据方式与缓存使用 </title><link>http://www.blogjava.net/yaozhuan/articles/39721.html</link><dc:creator>砖头</dc:creator><author>砖头</author><pubDate>Fri, 07 Apr 2006 01:36:00 GMT</pubDate><guid>http://www.blogjava.net/yaozhuan/articles/39721.html</guid><wfw:comment>http://www.blogjava.net/yaozhuan/comments/39721.html</wfw:comment><comments>http://www.blogjava.net/yaozhuan/articles/39721.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/yaozhuan/comments/commentRss/39721.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/yaozhuan/services/trackbacks/39721.html</trackback:ping><description><![CDATA[
		<p>
				<font size="2">Hibernate获取数据的方式有不同的几种，其与缓存结合使用的效果也不尽相同，而Hibernate中具体怎么使用缓存其实是我们很关心的一个问题，直接涉及到性能方面。<br />缓存在Hibernate中主要有三个方面：一级缓存、二级缓存和查询缓存；一级缓存在Hibernate中对应的即为session范围的缓存，也就是当session关闭时缓存即被清除，一级缓存在Hibernate中是不可配置的部分；二级缓存在Hibernate中对应的即为SessionFactory范围的缓存，通常来讲SessionFactory的生命周期和应用的生命周期相同，所以可以看成是进程缓存或集群缓存，二级缓存在Hibernate中是可以配置的，可以通过class-cache配置类粒度级别的缓存(class-cache在class中数据发生任何变化的情况下自动更新)，同时也可通过collection-cache配置集合粒度级别的缓存(collection-cache仅在collection中增加了元素或者删除了元素的情况下才自动更新，也就是当collection中元素发生值的变化的情况下它是不会自动更新的)，缓存自然会带来并发的访问问题，这个时候相应的就要根据应用来设置缓存所采用的事务隔离级别，和数据库的事务隔离级别概念基本一样，没什么多介绍的，^_^；查询缓存在Hibernate同样是可配置的，默认是关闭的，可以通过设置cache.use_ query_cache为true来打开查询缓存。根据缓存的通常实现策略，我们可以来理解Hibernate的这三种缓存，缓存的实现通过是通过key/value的Map方式来实现，在Hibernate的一级、二级和查询缓存也同样如此，一级、二级缓存使用的key均为po的主键ID，value即为po实例对象，查询缓存使用的则为查询的条件、查询的参数、查询的页数，value有两种情况，如果采用的是select po.property这样的方式那么value为整个结果集，如采用的是from这样的方式那么value为获取的结果集中各po对象的主键ID，这样的作用很明显，节省内存，^_^<br />简单介绍完Hibernate的缓存后，再结合Hibernate的获取数据方式来说明缓存的具体使用方式，在Hibernate中获取数据常用的方式主要有四种：Session.load、Session.get、Query.list、Query.iterator。<br />1、Session.load<br />      在执行session.load时，Hibernate首先从当前session的一级缓存中获取id对应的值，在获取不到的情况下，将根据该对象是否配置了二级缓存来做相应的处理，如配置了二级缓存，则从二级缓存中获取id对应的值，如仍然获取不到则还需要根据是否配置了延迟加载来决定如何执行，如未配置延迟加载则从数据库中直接获取，在从数据库获取到数据的情况下，Hibernate会相应的填充一级缓存和二级缓存，如配置了延迟加载则直接返回一个代理类，只有在触发代理类的调用时才进行数据库查询的操作。<br />      在这样的情况下我们就可以看到，在session一直打开的情况下，要注意在适当的时候对一级缓存进行刷新操作，通常是在该对象具有单向关联维护的时候，在Hibernate中可以使用象session.clear、session.evict的方式来强制刷新一级缓存。<br />      二级缓存则在数据发生任何变化(新增、更新、删除)的情况下都会自动的被更新。<br />2、Session.get<br />      在执行Session.get时，和Session.load不同的就是在当从缓存中获取不到时，直接从数据库中获取id对应的值。<br />3、Query.list<br />      在执行Query.list时，Hibernate的做法是首先检查是否配置了查询缓存，如配置了则从查询缓存中查找key为查询语句+查询参数+分页条件的值，如获取不到则从数据库中进行获取，从数据库获取到后Hibernate将会相应的填充一级、二级和查询缓存，如获取到的为直接的结果集，则直接返回，如获取到的为一堆id的值，则再根据id获取相应的值(Session.load)，最后形成结果集返回，可以看到，在这样的情况下，list也是有可能造成N次的查询的。<br />      查询缓存在数据发生任何变化的情况下都会被自动的清空。<br />4、Query.iterator<br />      在执行Query.iterator时，和Query.list的不同的在于从数据库获取的处理上，Query.iterator向数据库发起的是select id from这样的语句，也就是它是先获取符合查询条件的id，之后在进行iterator.next调用时才再次发起session.load的调用获取实际的数据。<br />      可见，在拥有二级缓存并且查询参数多变的情况下，Query.iterator会比Query.list更为高效。<br /><br />这四种获取数据的方式都各有适用的场合，要根据实际情况做相应的决定，^_^，最好的方式无疑就是打开show_sql选项看看执行的情况来做分析，系统结构上只用保证这种调整是容易实现的就好了，在cache这个方面的调整自然是非常的容易，只需要调整配置文件里的设置，而查询的方式则可对外部进行屏蔽，这样要根据实际情况调整也非常容易。<br /><br />推荐三篇关于Hibernate缓存机制介绍的文章：<br /></font>
				<a href="http://gocom.primeton.com/blog/index.php?op=ViewArticle&amp;articleId=467&amp;blogId=37&amp;src=jdon&amp;srcforum=62">
						<font size="2">http://gocom.primeton.com/blog/index.php?op=ViewArticle&amp;articleId=467&amp;blogId=37&amp;src=jdon&amp;srcforum=62</font>
				</a>
				<br />
				<a href="http://club.gamvan.com/club/clubPage.jsp?ccStyle=0&amp;tID=10456&amp;ccID=37">
						<font size="2">http://club.gamvan.com/club/clubPage.jsp?ccStyle=0&amp;tID=10456&amp;ccID=37</font>
				</a>
				<br />
				<a href="http://www.devx.com/dbzone/Article/29685/1954?pf=true">
						<font size="2">http://www.devx.com/dbzone/Article/29685/1954?pf=true</font>
				</a>
		</p>
<img src ="http://www.blogjava.net/yaozhuan/aggbug/39721.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/yaozhuan/" target="_blank">砖头</a> 2006-04-07 09:36 <a href="http://www.blogjava.net/yaozhuan/articles/39721.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>