??xml version="1.0" encoding="utf-8" standalone="yes"?> 2005 q?1 ? 1 介绍 2 读?/span> 3.1.准备 1Q?安装配置好HibernateQ我们后面将%HIBERNATE_HOME%作ؓ对Hibernate安装目录的引用, 2Q?开始创建好自己的第一个例子,例如hibernate手册里面的类CatQ?/p> 3Q?配置好hbm映射文gQ例如Cat.hbm.xmlQ本文不讨论q个文g内配|项的含义)和数据库Q如hsqldbQ, 4Q?在项目的classpath路径下添加一个hibernate.cfg.xml文gQ如?W一ơ用hibernate最常见的配|内?Q?/p> 5Q?然后q需要提供一个类来测试一下创建,更新Q删除和查询CatQ对于熟(zhn)JUnit的开发h员,可以创徏一个单元测试类来进行测试,如下Q?/p> 3.2 new Configuration()都做了什么? Configuration是hibernate的入口,在新Z个Configuration的实例的时候,hibernate会在classpath里面查找hibernate.properties文gQ如果该文g存在Q则该文g的内容加载到一个Properties的实例GLOBAL_PROPERTIES里面Q如果不存在Q将打印信息 然后是将所有系l环境变量(System.getProperties()Q也d到GLOBAL_PROPERTIES里面Q?a xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">?Q。如果hibernate.properties文g存在Q系l还会验证一下这个文仉|的有效性,对于一些已l不支持的配|参敎ͼpȝ打印警告信息?/p> 3.3. configure()在做什么? configure()Ҏ(gu)默认会在classpath下面Lhibernate.cfg.xml文gQ如果没有找到该文gQ系l会打印如下信息q抛出HibernateException异常?/p> 如果扑ֈ该文Ӟconfigure()Ҏ(gu)会首先访?lt; session-factory >Qƈ获取该元素的name属性,如果非空Q将用这个配|的值来覆盖hibernate.properties的hibernate.session_factory_name的配|的|从这里我们可以看出,hibernate.cfg.xml里面的配|信息可以覆盖hibernate.properties的配|信息?/p> 接着configure()Ҏ(gu)讉K<session-factory>的子元素Q首先将使用所有的<property>元素配置的信息(?Q,如前面我们用的配置文g 会覆盖hibernate.properties里面对应的配|,hibernate2.1发布包里面自带的hibernate.properties文gQ位?HIBERNATE_HOME%/etc下面Q里面的|如下Q?/p> 然后configure()会顺序访问以下几个元素的内容 其中<mapping>是必不可的Q必通过配置<mapping>Qconfigure()才能讉K到我们定义的java对象和关pL据库表的映射文gQhbm.xmlQ,例如Q?/p> 通过以上的分析,我们对hibernate配置文ghibernate.properties和hibernate.cfg.xml的默认的加蝲q程比较清楚了?/p> 3.4 Configuration的其他用?/span> 同时Configurationq提供了一pdҎ(gu)用来定制hibernate的加载配|文件的q程Q让你的应用更加灉|Q常用的是以下几U: 通过以上几个Ҏ(gu)Q除了用默认的hibernate.properties文gQ你q可以提供多?properties配置文gQ用Hibernate的时候根据不同的情况使用不同的配|文Ӟ例如Q?/p> 除了指定.properties文g之外Q还可以指定.hbm.xml文gQ下面列出几个常用的Ҏ(gu)Q?/p> 前面我们已经讲了Qconfigure()Ҏ(gu)默认是通过讉Khibernate.cfg.xml?lt;mapping>元素来加载我们提供的.hbm.xml文gQ上面列出的Ҏ(gu)可以直接指定hbm.xml文gQ例如addClass()Ҏ(gu)可以直接通过指定class来加载对应的映射文gQhibernate会将提供的class的全名(包括packageQ自动{化ؓ文g路径Q如net.sf.hibernate.examples.quickstart.Cat.class对应了net/sf/hibernate/examples/quickstart/Cat.hbm.xmlQ还可以用addFileҎ(gu)直接指定映射文g?/p> 3.5 ȝ 1Q?一个应用中往往有很?hbm.xml映射文gQ开发的q程中如果只是ؓ了测试某个或几个Java PO(Persistence Object)Q我们没有必要把所有的.hbm.xml都加载到内存Q这样可以通过addClass或者addFile直接Q显得非常灵zR?/p> 2Q?学习Hibernate的过E中Q往往需要通过l习来体会Hibernate提供的各U特征,而很多特征是需要修攚w|文件的Q如果要观察相同的代码在不同的特征下的表玎ͼ需要手工改配置文gQ这样太ȝ了,而且Ҏ(gu)出错Q我们可以提供多个配|文Ӟ每个配置文g针对需要的特征而配|,q样我们在调用程序的时候,把不同的配置文g作ؓ参数传递进去,而程序代码里面用setProperties和addFile指定传入的配|文件参数就可以了?/p> 3Q?在单元测试中Q特别是在集成测试里面,整个q程是自动化的,我们不能手工q预试q程Q往往需要准备多个配|文仉对不同的试案例Q这个时候setProperties和addFileҎ(gu)显得特别有用了Q在不同的测试案例中用这些方法来指定相应的配|文Ӟq样可以做到自动化试Q保证了持箋性?/p> 3.6 应用举例 例如针对ono-to-many和many-to-one的双向关联的映射关系Q我们想试在one-to-many一方用inverse="false"和inverse="true"的不同效果,假设已经正确的配|好了hibernate.propertiesQ那么还需要提供两个不同的hbm.xml文gQ假讑ֈ别名为bidirect.inverse.false.hbm.xml和bidirect.inverse.true.hbm.xml?/p> 然后需要写两个不同的测试案例,分别针对两个不同的配|文件进行测试就可以了,q样的好处是Q不用针对不同的试案例修改配置文gQ特别是在集成测试的时候,一切都是自动化的,如果每测试一个案例就需要手工去更改配置文gQ这肯定是一个失败的试?/p> TrueInverseTest.java文g l束?/span> 持箋集成中的试的特征是自动化和持箋性,不能手工q预其过E,在用到Hibernate的项目如果要实现持箋集成Q就要ؓ不同的测试案例提供不同的配置文gQ而不是针对不同的试案例q行手工调整Q因此,在用到Hibernate的项目中灉|的运用多配置文gQ可以提高测试的效率Q保证自动化和持l性?/p> ?Q有关的代码请参考Environmentcȝstatic{}?/p> ?Q如果在hibernate.cfg.xml?lt;property/>配置的name没有以hibernate开_那么configure()内部会自动在前面dhibernateQ例如connection.urlQhibernate会自动将其{化ؓhibernate.connection.url?/p>Hibernate 是一个流行的开源对象关pL工P单元试和持l集成的重要性也得到了广泛的推广和认同,在采用了Hibernate的项目中如何保证试的自动化和持l性呢Q本文讨ZHibernate加蝲光|文件hibernate.properties和hibernate.cfg.xml的过E,以及怎么样将hibernate提供的配|文件的讉KҎ(gu)灉|q用到单元测试中?/blockquote>
Hibernate 是一个流行的开源对象关pL工P单元试和持l集成的重要性也得到了广泛的推广和认同,在采用了Hibernate的项目中如何保证试的自动化和持l性呢Q本文讨ZHibernate加蝲光|文件hibernate.properties和hibernate.cfg.xml的过E,以及怎么样将hibernate提供的配|文件的讉KҎ(gu)灉|q用到单元测试中。注意:本文以hibernate2.1作ؓ讨论的基Q不保证本文的观炚w合于其他版本?/p>
Java开发h员,要求熟?zhn)JUnit和掌握Hibernate的基知识
对于hibernate的初学者来_W一ơ用hibernate的经验通常是:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<property name="dialect">net.sf.hibernate.dialect.HSQLDialect</property>
<property name="hibernate.show_sql">false</property>
<mapping resource="Cat.hbm.xml"/>
</session-factory>
</hibernate-configuration>
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();////注意q一行,q是本文重点讨论研究的地斏V?
session = cfg.buildSessionFactory().openSession();
tx = session.beginTransaction();
}
protected void tearDown() throws Exception {
tx.commit();
session.close();
}
public void testCreate() {
//请在此方法内d相关的代码,本文不讨论怎么样用Hibernate API?
}
public void testUpdate() {
//请在此方法内d相关的代码,本文不讨论怎么样用Hibernate API?
}
public void testDelete() {
//请在此方法内d相关的代码,本文不讨论怎么样用Hibernate API?
}
public void testQuery() {
//请在此方法内d相关的代码,本文不讨论怎么样用Hibernate API?
}
}
对于W一ơ用hibernate的新手来_下面的这D代码可以说是最常见的用Configuration方式?/p>
Configuration cfg = new Configuration().configure();
hibernate.properties not found
new Configuration()讨论xQ下面讨论configure()Ҏ(gu)?/p>
hibernate.cfg.xml not found
<property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
<property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
<property name="connection.username">sa</property>
<property name="connection.password"></property>
<property name="dialect">net.sf.hibernate.dialect.HSQLDialect</property>
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
<mapping>
<jcs-class-cache>
<jcs-collection-cache>
<collection-cache>
<mapping resource="Cat.hbm.xml"/>
Configuration的configure ()Ҏ(gu)q支持带参数的访问方式,你可以指定hbm.xml文g的位|,而不是用默认的classpath下面的hibernate.cfg.xmlq种方式Q例如:
Configuration cfg = new Configuration().configure("myexample.xml");
addProperties(Element)
addProperties(Properties)
setProperties(Properties)
setProperty(String, String)
Properties properties = Properties.load("my.properties");
Configuration config = new Configuration().setProperties(properties).configure();
addClass(Class)
addFile(File)
addFile(String)
addURL(URL)
Configuration config = new Configuration().addClass(Cat.class);
Configuration config = new Configuration().addURL(Configuration.class.getResource ("Cat.hbm.xml"));
Configuration config = new Configuration().addFile("Cat.hbm.xml");
Configuration提供的这些方法的好处如下Q?/p>
在刚开始学习hibernate的时候,对于hibernate的hbm映射文g里的各种配置参数没有一个感性的认识Q例如inverse="true",lazy="true"q样的配|参敎ͼ不通过实践是无法体会到其作用的Q传l的Ҏ(gu)是每需要测试一U参数的效果更改相应的配置文gQ然后运行测试来观察l果Q如果能够灵zȝq用Configuration提供的定刉|的Ҏ(gu)Q我们可以提供多个配|文Ӟ每个配置文g里面有不同的配置参数Q配合相应的试案例方便多了?/p>
FalseInverseTest.java文g
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() {
//在此~写试代码
}
}
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() {
//在此~写试代码
}
}
通过对Hibernate默认的配|文件的加蝲序和Hibernate提供的加载配|文件的Ҏ(gu)的讨论,我们对在使用到Hibernate的项目的单元试中用多个Hibernate配置文g有了比较清楚的认识?/p>
]]>
]]>
customer.getOrders().add(order);
order.setCustomer(customer);
//删除
customer.getOrders().remove(order);
order.setCustomer(null);
3、在定义一对多映射中“一”的POJOcLQ注意要private Set orders = new HashSet();//通常把它初始化ؓ集合实现cȝ一个实例,q样避免讉K取gؓnullQ引发NullPointerException异常Q提高健壮性?br />二、Session三种索方法:
1.load():Ҏ(gu)l定OID从数据库中加载一个持久化对象Q如数据库中没有则抛出net.sf.hibernate.ObjectNotFoundException异常?br />2.get():Ҏ(gu)l定OID从数据库中加载一个持久化对象Q如数据库中没有则返回null?br />3.find():按照参数指定的HQL语句加蝲一个或多个持久化对象,实际是HQL索方式的一U简写Ş式?br />三、hql查询Q?br />在数l和Collection中的查询Q?br />String hql = "select u from User u where u in (:users)";
query.setParameterList("users", users);
//括号千万别忘写,否则出现如下错误Q?br />2006-07-07 11:07:35 WARN [org.hibernate.util.JDBCExceptionReporter] - SQL Error: 907, SQLState: 42000
2006-07-07 11:07:35 ERROR [org.hibernate.util.JDBCExceptionReporter] - ORA-00907: ~失x?img src ="http://www.tkk7.com/ltc603/aggbug/63913.html" width = "1" height = "1" />
]]>
下蝲HibernateQ例?.0.3E_版本Q解压羃Q可以看C个hibernate2.jar和lib目录下有22个jar包:
hibernate2.jar:
Hibernate的库Q没有什么可说的Q必M用的jar?
cglib-asm.jar:
CGLIB库,Hibernate用它来实现PO字节码的动态生成,非常核心的库Q必M用的jar?
dom4j.jar:
dom4j是一个Java的XML APIQ类gjdomQ用来读写XML文g的。dom4j是一个非帔R怼U的Java XML APIQ具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的YӞ可以在SourceForge上找到它。在IBM developerWorks上面可以扑ֈ一文章,对主的Java XML APIq行的性能、功能和易用性的评测Qdom4j无论在那个方面都是非常出色的。我早在近两年之前开始用dom4jQ直到现在。如今你可以看到来多的Java软g都在使用dom4j来读写XMLQ特别值得一提的是连Sun的JAXM也在用dom4j。这是必M用的jar包,Hibernate用它来读写配|文件?
odmg.jar:
ODMG是一个ORM的规范,Hibernate实现了ODMG规范Q这是一个核心的库,必须使用的jar包?
commons-collections.jarQ?
Apache Commons包中的一个,包含了一些Apache开发的集合c,功能比java.util.*强大。必M用的jar包?
commons-beanutils.jarQ?
Apache Commons包中的一个,包含了一些Bean工具cȝ。必M用的jar包?
commons-lang.jar:
Apache Commons包中的一个,包含了一些数据类型工LQ是java.lang.*的扩展。必M用的jar包?
commons-logging.jar:
Apache Commons包中的一个,包含了日志功能,必须使用的jar包。这个包本n包含了一个Simple LoggerQ但是功能很弱。在q行的时候它会先在CLASSPATH找log4jQ如果有Q就使用log4jQ如果没有,找JDK1.4带的java.util.loggingQ如果也找不到就用Simple Logger。commons-logging.jar的出现是一个历史的的遗留的遗憾Q当初Apache极力游说Sun把log4j加入JDK1.4Q然而JDK1.4目组已经接近发布JDK1.4产品的时间了Q因此拒l了Apache的要求,使用自己的java.util.loggingQ这个包的功能比log4j差的很远Q性能也一般。后来Apache开发出来了commons-logging.jar用来兼容两个logger。因此用commons-logging.jar写的logE序Q底层的Logger是可以切换的Q你可以选择log4jQjava.util.logging或者它自带的Simple Logger。不q我仍然强烈使用log4jQ因为log4j性能很高Qlog输出信息旉几乎{于System.outQ而处理一条logq_只需?us。你可以在Hibernate的src目录下找到Hibernate已经Z准备好了的log4j的配|文Ӟ你只需要到Apache |站M载log4j可以了。commons-logging.jar也是必须的jar包?
使用Hibernate必须的jar包就是以上的q几个,剩下的都是可选的?
ant.jar:
Ant~译工具的jar包,用来~译Hibernate源代码的。如果你不准备修改和~译Hibernate源代码,那么没有什么用Q可选的jar?
optional.jarQ?
Ant的一个辅助包?
c3p0.jarQ?
C3PO是一个数据库q接池,Hibernate可以配置Z用C3POq接池。如果你准备用这个连接池Q就需要这个jar包?
proxool.jarQ?
也是一个连接池Q同上?
commons-pool.jar, commons-dbcp.jar:
DBCP数据库连接池QApache的Jakartal织开发的QTomcat4的连接池也是DBCP?
实际上Hibernate自己也实C一个非帔R常简单的数据库连接池Q加上上?个,你实际上可以在Hibernate上选择4U不同的数据库连接池Q选择哪一个看个h的偏好,不过DBCP可能更通用一些。另外强调一点,如果在EJB中用HibernateQ一定要用App Server的连接池Q不要用以上4U连接池Q否则容器管理事务不起作用?
connector.jar:
JCA 规范Q如果你在App Server上把Hibernate配置为Connector的话Q就需要这个jar。不q实际上一般App Server肯定会带上这个包Q所以实际上是多余的包?
jaas.jar:
JAAS是用来进行权限验证的Q已l包含在JDK1.4里面了。所以实际上是多余的包?
jcs.jarQ?
如果你准备在Hibernate中用JCS的话Q那么必d括它Q否则就不用?
jdbc2_0-stdext.jar:
JDBC2.0的扩展包Q一般来说数据库q接池会用上它。不qApp Server都会带上Q所以也是多余的?
jta.jarQ?
JTA规范Q当Hibernate使用JTA的时候需要,不过App Server都会带上Q所以也是多余的?
junit.jar:
Junit包,当你q行Hibernate自带的测试代码的时候需要,否则׃用?
xalan.jar, xerces.jar, xml-apis.jar:
Xerces是XML解析器,Xalan是格式化器,xml-apis实际上是JAXP。一般App Server都会带上QJDK1.4也包含了解析器,不过不是XercesQ是CrimsonQ效率比较差Q不qHibernate用XML只不q是d配置文gQ性能没什么紧要的Q所以也是多余的?/span>
]]>
我的l验主要来自hibernate2.1版本Q基本原理和3.0?.1是一LQ请原谅我的固不化?
hibernate的session提供了一U缓存,每个sessionQ对同一个idq行两次loadQ不会发送两条sqll数据库Q但是session关闭的时候,一U缓存就失效了?
二~存是SessionFactoryU别的全局~存Q它底下可以使用不同的缓存类库,比如ehcache、oscache{,需要设|hibernate.cache.provider_classQ我们这里用ehcacheQ在2.1中就?
hibernate.cache.provider_class=net.sf.hibernate.cache.EhCacheProvider
如果使用查询~存Q加?
hibernate.cache.use_query_cache=true
~存可以单的看成一个MapQ通过key在缓存里面找value?
Class的缓?/span>
对于一条记录,也就是一个PO来说Q是Ҏ(gu)ID来找的,~存的key是IDQvalue是POJO。无论listQloadq是iterateQ只要读Z个对象,都会填充~存。但是list不会使用~存Q而iterate会先取数据库select id出来Q然后一个id一个id的loadQ如果在~存里面有,׃~存取,没有的话去数据库load。假设是d~存Q需要设|:
<cache usage="read-write"/>
如果你用的二~存实现是ehcache的话Q需要配|ehcache.xml
<cache name="com.xxx.pojo.Foo" maxElementsInMemory="500" eternal="false" timeToLiveSeconds="7200" timeToIdleSeconds="3600" overflowToDisk="true" />
其中eternal表示~存是不是永q不时QtimeToLiveSeconds是缓存中每个元素Q这里也是一个POJOQ的时旉Q如果eternal="false"Q超q指定的旉Q这个元素就被移C。timeToIdleSeconds是发呆时_是可选的。当往~存里面put的元素超q?00个时Q如果overflowToDisk="true"Q就会把~存中的部分数据保存在硬盘上的时文仉面?
每个需要缓存的class都要q样配置。如果你没有配置Qhibernate会在启动的时候警告你Q然后用defaultCache的配|,q样多个class会共享一个配|?
当某个ID通过hibernate修改Ӟhibernate会知道,于是U除~存?
q样大家可能会想Q同L查询条gQ第一ơ先listQ第二次再iterateQ就可以使用到缓存了。实际上q是很难的,因ؓ你无法判断什么时候是W一ơ,而且每次查询的条仉常是不一LQ假如数据库里面?00条记录,id??00Q第一ơlist的时候出了前50个idQ第二次iterate的时候却查询?0?0号idQ那?0-50是从~存里面取的Q?1?0是从数据库取的,共发?+20条sql。所以我一直认为iterate没有什么用QL会有1+N的问题?
Q题外话Q有说法说大型查询用list会把整个l果集装入内存,很慢Q而iterate只select id比较好,但是大型查询L要分|的,谁也不会真的把整个结果集装进来,假如一?0条的话,iterate共需要执?1条语句,list虽然选择若干字段Q比iterateW一条select id语句慢一些,但只有一条语句,不装入整个结果集hibernateq会Ҏ(gu)数据库方a做优化,比如使用mysql的limitQ整体看来应该还是list快。)
如果惌对list或者iterate查询的结果缓存,p用到查询~存?
查询~存
首先需要配|hibernate.cache.use_query_cache=true
如果用ehcacheQ配|ehcache.xmlQ注意hibernate3.0以后不是net.sf的包名了
<cache name="net.sf.hibernate.cache.StandardQueryCache"
maxElementsInMemory="50" eternal="false" timeToIdleSeconds="3600"
timeToLiveSeconds="7200" overflowToDisk="true"/>
<cache name="net.sf.hibernate.cache.UpdateTimestampsCache"
maxElementsInMemory="5000" eternal="true" overflowToDisk="true"/>
然后
query.setCacheable(true);//ȀzL询缓?
query.setCacheRegion("myCacheRegion");//指定要用的cacheRegionQ可?
W二行指定要使用的cacheRegion是myCacheRegionQ即你可以给每个查询~存做一个单独的配置Q用setCacheRegion来做q个指定Q需要在ehcache.xml里面配置它:
<cache name="myCacheRegion" maxElementsInMemory="10" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200" overflowToDisk="true" />
如果省略W二行,不设|cacheRegion的话Q那么会使用上面提到的标准查询缓存的配置Q也是net.sf.hibernate.cache.StandardQueryCache
对于查询~存来说Q缓存的key是根据hql生成的sqlQ再加上参数Q分늭信息Q可以通过日志输出看到Q不q它的输Z是很可读Q最好改一下它的代码)?
比如hqlQ?
from Cat c where c.name like ?
生成大致如下的sqlQ?
select * from cat c where c.name like ?
参数?tiger%"Q那么查询缓存的key*大约*是这L字符Ԍ我是凭记忆写的,q不_Q不q看了也该明白了Q:
select * from cat c where c.name like ? , parameter:tiger%
q样Q保证了同样的查询、同L参数{条件下h一Lkey?
现在说说~存的valueQ如果是list方式的话Qvalue在这里ƈ不是整个l果集,而是查询出来的这一串ID。也是_不管是listҎ(gu)q是iterateҎ(gu)Q第一ơ查询的时候,它们的查询方式很它们qx的方式是一LQlist执行一条sqlQiterate执行1+N条,多出来的行ؓ是它们填充了~存。但是到同样条gW二ơ查询的时候,都和iterate的行Z样了Q根据缓存的keyȝ存里面查CvalueQvalue是一串idQ然后在到class的缓存里面去一个一个的load出来。这样做是ؓ了节U内存?
可以看出来,查询~存需要打开相关cȝclass~存。list和iterateҎ(gu)W一ơ执行的时候,都是既填充查询缓存又填充class~存的?
q里q有一个很Ҏ(gu)被忽视的重要问题Q即打开查询~存以后Q即使是listҎ(gu)也可能遇?+N的问题!相同条gW一ơlist的时候,因ؓ查询~存中找不到Q不class~存是否存在数据QL发送一条sql语句到数据库获取全部数据Q然后填充查询缓存和class~存。但是第二次执行的时候,问题来了,如果你的class~存的超时时间比较短Q现在class~存都超时了Q但是查询缓存还在,那么listҎ(gu)在获取id串以后,会一个一个去数据库loadQ因此,class~存的超时时间一定不能短于查询缓存设|的时旉Q如果还讄了发呆时间的话,保证class~存的发呆时间也大于查询的缓存的生存旉。这里还有其他情况,比如class~存被程序强制evict了,q种情况p自己注意了?
另外Q如果hql查询包含select字句Q那么查询缓存里面的value是整个l果集了?
当hibernate更新数据库的时候,它怎么知道更新哪些查询~存呢?
hibernate在一个地方维护每个表的最后更新时_其实也就是放在上面net.sf.hibernate.cache.UpdateTimestampsCache所指定的缓存配|里面?
当通过hibernate更新的时候,hibernate会知道这ơ更新媄响了哪些表。然后它更新q些表的最后更新时间。每个缓存都有一个生成时间和q个~存所查询的表Q当hibernate查询一个缓存是否存在的时候,如果~存存在Q它q要取出~存的生成时间和q个~存所查询的表Q然后去查找q些表的最后更新时_如果有一个表在生成时间后更新q了Q那么这个缓存是无效的?
可以看出Q只要更新过一个表Q那么凡是涉及到q个表的查询~存失效了Q因此查询缓存的命中率可能会比较低?
Collection~存
需要在hbm的collection里面讄
<cache usage="read-write"/>
假如class是CatQcollection叫childrenQ那么ehcache里面配置
<cache name="com.xxx.pojo.Cat.children"
maxElementsInMemory="20" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200"
overflowToDisk="true" />
Collection的缓存和前面查询~存的list一P也是只保持一串idQ但它不会因个表更新q就失效Q一个collection~存仅在q个collection里面的元素有增删时才失效?
q样有一个问题,如果你的collection是根据某个字D|序的Q当其中一个元素更C该字D|Q导致顺序改变时Qcollection~存里面的顺序没有做更新?
~存{略
只读~存Qread-onlyQ:没有什么好说的
?写缓存(read-writeQ?E序可能要的更新数据
不严格的?写缓存(nonstrict-read-writeQ:需要更新数据,但是两个事务更新同一条记录的可能性很,性能比读写缓存好
事务~存QtransactionalQ:~存支持事务Q发生异常的时候,~存也能够回滚,只支持jta环境Q这个我没有怎么研究q?
d~存和不严格d~存在实C的区别在于,d~存更新~存的时候会把缓存里面的数据换成一个锁Q其他事务如果去取相应的~存数据Q发现被锁住了,然后q接取数据库查询?
在hibernate2.1的ehcache实现中,如果锁住部分~存的事务发生了异常Q那么缓存会一直被锁住Q直?0U后时?
不严D写缓存不锁定~存中的数据?
使用二~存的前|条?/span>
你的hibernateE序Ҏ(gu)据库有独占的写访问权Q其他的q程更新了数据库Qhibernate是不可能知道的。你操作数据库必需直接通过hibernateQ如果你调用存储q程Q或者自׃用jdbc更新数据库,hibernate也是不知道的。hibernate3.0的大扚w更新和删除是不更CU缓存的Q但是据?.1已经解决了这个问题?
q个限制相当的棘手,有时候hibernate做批量更新、删除很慢,但是你却不能自己写jdbc来优化,很郁闷吧?
SessionFactory也提供了U除~存的方法,你一定要自己写一些JDBC的话Q可以调用这些方法移除缓存,q些Ҏ(gu)是:
void evict(Class persistentClass)
Evict all entries from the second-level cache.
void evict(Class persistentClass, Serializable id)
Evict an entry from the second-level cache.
void evictCollection(String roleName)
Evict all entries from the second-level cache.
void evictCollection(String roleName, Serializable id)
Evict an entry from the second-level cache.
void evictQueries()
Evict any query result sets cached in the default query cache region.
void evictQueries(String cacheRegion)
Evict any query result sets cached in the named query cache region.
不过我不q样做,因ؓq样很难l护。比如你现在用JDBC扚w更新了某个表Q有3个查询缓存会用到q个表,用evictQueries(String cacheRegion)U除?个查询缓存,然后用evict(Class persistentClass)U除了class~存Q看上去好像完整了。不q哪天你d了一个相x询缓存,可能会忘记更新这里的U除代码。如果你的jdbc代码到处都是Q在你添加一个查询缓存的时候,q知道其他什么地方也要做相应的改动吗Q?
----------------------------------------------------
ȝQ?/span>
不要惛_然的以ؓ~存一定能提高性能Q仅仅在你能够驾驭它q且条g合适的情况下才是这L。hibernate的二U缓存限制还是比较多的,不方便用jdbc可能会大大的降低更新性能。在不了解原理的情况下ؕ用,可能会有1+N的问题。不当的使用q可能导致读数据?
如果受不了hibernate的诸多限Ӟ那么q是自己在应用程序的层面上做~存吧?
在越高的层面上做~存Q效果就会越好。就好像管盘有缓存,数据库还是要实现自己的缓存,管数据库有~存Q咱们的应用E序q是要做~存。因为底层的~存它ƈ不知道高层要用这些数据干什么,只能做的比较通用Q而高层可以有针对性的实现~存Q所以在更高的别上做缓存,效果也要好些吧?/span>
]]>
1.cascade="..."Q?/font>
cascade属性ƈ不是多对多关pM定要用的Q有了它只是让我们在插入或删除对像时更方便一些,只要在cascade的源头上插入或是删除Q所有cascade的关pd会被自己动的插入或是删除。便是ؓ了能正确的cascadeQunsaved-value是个很重要的属性?/font>
Hibernate通过q个属性来判断一个对象应该saveq是updateQ如果这龆韵蟮膇d是unsaved-value的话Q那说明q个对象不是persistence object要saveQinsert)Q如果id是非unsaved-value的话Q那说明q个对象是persistence objectQ数据库中已存在Q,只要updatep了。saveOrUpdateҎ(gu)用的也是q个机制?/font>
2.inverse="ture"?
inverse属性默认是false的,是说关pȝ两端都来l护关系。这个意思就是说Q如有一个Student, Teacher和TeacherStudent表,Student和Teacher是多对多对多关系Q这个关pȝTeacherStudentq个表来表现。那么什么时候插入或删除TeacherStudent表中的记录来l护关系呢?在用hibernateӞ我们不会昄的对TeacherStudent表做操作?/font>
对TeacherStudent的操作是hibernate帮我们做的。hibernate是看hbm文g中指定的??l护关系Q那个在插入或删??Ӟ׃处发对关p表的操作。前提是"?q个对象已经知道q个关系了,是说关pd一头的对象已经set或是add??q个对象里来了。前面说qinverse默认是falseQ就是关pȝ两端都维护关p,对其中Q一个操作都会处发对表系表的操作。当在关pȝ一_如Student中的bag或set中用了inverseQ?true"Ӟ那就代表关系是由另一关维护的QTeacherQ。就是说当这插入StudentӞ不会操作TeacherStudent表,即Student已经知道了关pR只有当Teacher插入或删除时才会处发对关p表的操作?/font>
所以,当关pȝ两头都用inverse="true"是不对的Q就会导致Q何操作都不处发对关系表的操作。当两端都是inverse="false"或是default值是Q在代码对关pLC的l护也是不对的,会导致在关系表中插入两次关系。在一对多关系中inverse更有意义了。在多对多中Q在哪端inverse="true"效果差不多(在效率上Q。但是在一对多中,如果要一方维护关p,׃使在插入或是删除"一"Ҏ(gu)去update"?方的每一个与q个"一"的对象有关系的对象?/font>
而如果让"?斚wl护关系时就不会有update操作Q因为关pd是在多方的对象中的,直指插入或是删除多方对象p了。当然这时也要遍??方的每一个对象显C的操作修关pȝ变化体现到DB中。不怎样_q是??方维护关pL直观一些?
3.cascade和inverse有什么区别?
可以q样理解Qcascade定义的是关系两端对象到对象的U联关系Q而inverse定义的是关系和对象的U联关系?/font>
4.net.sf.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): 2, of class: Xxxxx
q个问题出现在要删除关系的一头时。如Q要删除一个已l和Student有关pȝTeacher。当tx.commit();时才会抛个异常。这时一个在关系另一头的Student对象中的Set或是List中把q个Teacher对象昄的remove掉,再session.delete(q个teacher);。这是ؓ了防止在Student端有cascade时把q个Teacher对象再存回DB?/font>
所以,q个异常的只有在Student的关pd义中有cascade="..."Q而且没有像上面说的显C的解除关系时才会出现。所以防止出现这个异常的Ҏ(gu)是Q?Q在Student端不用cascadeQ?Q或是用cascade的话Q就昄的删除对像中的关pR?3Q在Teacher端要用cascade?/font>
5.net.sf.hibernate.HibernateException: identifier of an instance of my.MyObject altered from N to N
q个异常其实不是多对多中帔R到的Q但是这个异常的提示不make senseQ所以提一下,是因为id的java对象中的type和hbm文g中定义的不一P如:java中用longQ而hbm中用type="integer"Qƈ且generator用的是identity时就会出现?/font>
下面把Customer.hbm.xml文g?lt;class>元素的lazy属性设为trueQ表CZ用gq检索策略:
<class name="mypack.Customer" table="CUSTOMERS" lazy="true">
当执行Session的load()Ҏ(gu)ӞHibernate不会立即执行查询CUSTOMERS表的select语句Q仅仅返回Customercȝ代理cȝ实例Q这个代理类L以下特征Q?/p>
Q?Q?由Hibernate在运行时动态生成,它扩展了Customerc,因此它承了Customercȝ所有属性和Ҏ(gu)Q但它的实现对于应用E序是透明的?br />Q?Q?当Hibernate创徏Customer代理cd例时Q仅仅初始化了它的OID属性,其他属性都为nullQ因此这个代理类实例占用的内存很?br />Q?Q?当应用程序第一ơ访问Customer代理cd例时Q例如调用customer.getXXX()或customer.setXXX()Ҏ(gu)Q,Hibernate会初始化代理cd例,在初始化q程中执行select语句Q真正从数据库中加蝲Customer对象的所有数据。但有个例外Q那是当应用程序访问Customer代理cd例的getId()Ҏ(gu)ӞHibernate不会初始化代理类实例Q因为在创徏代理cd例时OID存在了Q不必到数据库中L询?br />
提示QHibernate采用CGLIB工具来生成持久化cȝ代理cRCGLIB是一个功能强大的Java字节码生成工P它能够在E序q行时动态生成扩展JavacL者实现Java接口的代理类。关于CGLIB的更多知识,请参考:http://cglib.sourceforge.net/?/font>
以下代码先通过Session的load()Ҏ(gu)加蝲Customer对象Q然后访问它的name属性:
tx = session.beginTransaction();
Customer customer=(Customer)session.load(Customer.class,new Long(1));
customer.getName();
tx.commit();
在运行session.load()Ҏ(gu)时Hibernate不执行Q何select语句Q仅仅返回Customercȝ代理cȝ实例Q它的OID?Q这是由load()Ҏ(gu)的第二个参数指定的。当应用E序调用customer.getName()Ҏ(gu)ӞHibernate会初始化Customer代理cd例,从数据库中加载Customer对象的数据,执行以下select语句Q?/p>
select * from CUSTOMERS where ID=1;
select * from ORDERS where CUSTOMER_ID=1;
?lt;class>元素的lazy属性ؓtrueQ会影响Session的load()Ҏ(gu)的各U运行时行ؓQ下面D例说明?/p>
1Q如果加载的Customer对象在数据库中不存在QSession的load()Ҏ(gu)不会抛出异常Q只有当q行customer.getName()Ҏ(gu)时才会抛Z下异常:
ERROR LazyInitializer:63 - Exception initializing proxy
net.sf.hibernate.ObjectNotFoundException: No row with the given identifier exists: 1, of class:
mypack.Customer
2Q如果在整个Session范围内,应用E序没有讉KqCustomer对象Q那么Customer代理cȝ实例一直不会被初始化,Hibernate不会执行Mselect语句。以下代码试囑֜关闭Session后访问Customer游离对象Q?/p>
tx = session.beginTransaction();
Customer customer=(Customer)session.load(Customer.class,new Long(1));
tx.commit();
session.close();
customer.getName();
׃引用变量customer引用的Customer代理cȝ实例在Session范围内始l没有被初始化,因此在执行customer.getName()Ҏ(gu)ӞHibernate会抛Z下异常:
ERROR LazyInitializer:63 - Exception initializing proxy
net.sf.hibernate.HibernateException: Could not initialize proxy - the owning Session was closed
由此可见QCustomer代理cȝ实例只有在当前Session范围内才能被初始化?/p>
3Qnet.sf.hibernate.Hibernatecȝinitialize()静态方法用于在Session范围内显式初始化代理cd例,isInitialized()Ҏ(gu)用于判断代理cd例是否已l被初始化。例如:
tx = session.beginTransaction();
Customer customer=(Customer)session.load(Customer.class,new Long(1));
if(!Hibernate.isInitialized(customer))
Hibernate.initialize(customer);
tx.commit();
session.close();
customer.getName();
以上代码在Session范围内通过Hibernatecȝinitialize()Ҏ(gu)昑ּ初始化了Customer代理cd例,因此当Session关闭后,可以正常讉KCustomer游离对象?/p>
4Q当应用E序讉K代理cd例的getId()Ҏ(gu)Ӟ不会触发Hibernate初始化代理类实例的行为,例如Q?/p>
tx = session.beginTransaction();
Customer customer=(Customer)session.load(Customer.class,new Long(1));
customer.getId();
tx.commit();
session.close();
customer.getName();
当应用程序访问customer.getId()Ҏ(gu)Ӟ该方法直接返回Customer代理cd例的OID|无需查询数据库。由于引用变量customer始终引用的是没有被初始化的Customer代理cd例,因此当Session关闭后再执行customer.getName()Ҏ(gu)QHibernate会抛Z下异常:
ERROR LazyInitializer:63 - Exception initializing proxy
net.sf.hibernate.HibernateException: Could not initialize proxy - the owning Session was closed