﻿<?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-wanghao-文章分类-Hibernate</title><link>http://www.blogjava.net/wanghao/category/34267.html</link><description>向前进</description><language>zh-cn</language><lastBuildDate>Mon, 01 Sep 2008 19:06:28 GMT</lastBuildDate><pubDate>Mon, 01 Sep 2008 19:06:28 GMT</pubDate><ttl>60</ttl><item><title>hibernate入门 - Transaction </title><link>http://www.blogjava.net/wanghao/articles/226176.html</link><dc:creator>中间开花</dc:creator><author>中间开花</author><pubDate>Mon, 01 Sep 2008 12:04:00 GMT</pubDate><guid>http://www.blogjava.net/wanghao/articles/226176.html</guid><wfw:comment>http://www.blogjava.net/wanghao/comments/226176.html</wfw:comment><comments>http://www.blogjava.net/wanghao/articles/226176.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wanghao/comments/commentRss/226176.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wanghao/services/trackbacks/226176.html</trackback:ping><description><![CDATA[Hibernate是对JDBC的轻量级对象封装，Hibernate本身是不具备Transaction处理功能的，Hibernate的
Transaction实际上是底层的JDBC Transaction的封装，或者是JTA Transaction的封装，下面我们详细的分析：
<br />
<br />
Hibernate可以配置为JDBCTransaction或者是JTATransaction，这取决于你在hibernate.properties中的配置:
<br />
<br />
<div>引用</div>
<div>#hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory
<br />
#hibernate.transaction.factory_class net.sf.hibernate.transaction.JDBCTransactionFactory</div>
<br />
<br />
如果你什么都不配置，默认情况下使用JDBCTransaction，如果你配置为：
<br />
<br />
<div>引用</div>
<div>hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory</div>
<br />
<br />
将使用JTATransaction
<br />
<br />
不管你准备让Hibernate使用JDBCTransaction，还是JTATransaction，我的忠告就是什么都不配，将让它保持默认状态，如下：
<br />
<br />
<div>引用</div>
<div>#hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory
<br />
#hibernate.transaction.factory_class net.sf.hibernate.transaction.JDBCTransactionFactory</div>
<br />
<br />
在下面的分析中我会给出原因。
<br />
<br />
一、JDBC Transaction
<br />
<br />
看看使用JDBC Transaction的时候我们的代码例子：
<br />
<br />
[code:1]Session session = sf.openSession();
<br />
Transaction tx = session.beginTransactioin();
<br />
...
<br />
session.flush();
<br />
tx.commit();
<br />
session.close();[/code:1]
<br />
<br />
这是默认的情况，当你在代码中使用Hibernate的Transaction的时候实际上就是JDBCTransaction。那么JDBCTransaction究竟是什么东西呢？来看看源代码就清楚了：
<br />
<br />
Hibernate2.0.3源代码中的类[code:1]net.sf.hibernate.transaction.JDBCTransaction:
<br />
<br />
public void begin() throws HibernateException {
<br />
<br />
log.debug("begin");
<br />
try {
<br />
toggleAutoCommit = session.connection().getAutoCommit();
<br />
if (toggleAutoCommit) session.connection().setAutoCommit(false);
<br />
}
<br />
catch (SQLException e) {
<br />
log.error("Begin failed", e);
<br />
throw new TransactionException("Begin failed with SQL exception: ", e);
<br />
}
<br />
begun = true;
<br />
}[/code:1]
<br />
这是启动Transaction的方法，看到	connection().setAutoCommit(false) 了吗？是不是很熟悉？
<br />
<br />
再来看
<br />
<br />
[code:1]public void commit() throws HibernateException {
<br />
if (!begun) throw new TransactionException("Transaction not successfully started");
<br />
log.debug("commit");
<br />
try {
<br />
if ( session.getFlushMode()!=FlushMode.NEVER ) session.flush();
<br />
try {
<br />
session.connection().commit();
<br />
committed = true;
<br />
}
<br />
catch (SQLException e) {
<br />
log.error("Commit failed", e);
<br />
throw new TransactionException("Commit failed with SQL exception: ", e);
<br />
}
<br />
}
<br />
finally {
<br />
session.afterTransactionCompletion();
<br />
}
<br />
toggleAutoCommit();
<br />
}[/code:1]
<br />
这是提交方法，看到connection().commit()
了吗？下面就不用我多说了，这个类代码非常简单易懂，通过阅读使我们明白Hibernate的Transaction都在干了些什么？我现在把用
Hibernate写的例子翻译成JDBC，大家就一目了然了：
<br />
<br />
[code:1]Connection conn = ...;         &lt;---   session = sf.openSession();
<br />
<br />
conn.setAutoCommit(false);     &lt;---   tx = session.beginTransactioin();
<br />
<br />
...                            &lt;---   ...
<br />
<br />
conn.commit();                 &lt;---   tx.commit(); (对应左边的两句)
<br />
conn.setAutoCommit(true);
<br />
<br />
conn.close();                  &lt;---   session.close();[/code:1]
<br />
看明白了吧，Hibernate的JDBCTransaction根本就是conn.commit而已，根本毫无神秘可言，只不过在
Hibernate中，Session打开的时候，就会自动conn.setAutoCommit(false)，不像一般的JDBC，默认都是
true，所以你最后不写commit也没有关系，由于Hibernate已经把AutoCommit给关掉了，所以用Hibernate的时候，你在程
序中不写Transaction的话，数据库根本就没有反应。
<br />
<br />
<br />
二、JTATransaction
<br />
<br />
如果你在EJB中使用Hibernate，或者准备用JTA来管理跨Session的长事务，那么就需要使用JTATransaction，先看一个例子：
<br />
<br />
[code:1]javax.transaction.UserTransaction tx = new InitialContext().lookup("javax.transaction.UserTransaction");
<br />
<br />
Session s1 = sf.openSession();
<br />
...
<br />
s1.flush();
<br />
s1.close();
<br />
<br />
...
<br />
<br />
Session s2 = sf.openSession();
<br />
...
<br />
s2.flush();
<br />
s2.close();
<br />
<br />
tx.commit();[/code:1]
<br />
这是标准的使用JTA的代码片断，Transaction是跨Session的，它的生命周期比Session要长。如果你在EJB中使用
Hibernate，那么是最简单不过的了，你什么Transaction代码统统都不要写了，直接在EJB的部署描述符上配置某某方法是否使用事务就可
以了。
<br />
<br />
现在我们来分析一下JTATransaction的源代码， net.sf.hibernate.transaction.JTATransaction:
<br />
<br />
[code:1]public void begin(InitialContext context, ...
<br />
...
<br />
ut = (UserTransaction) context.lookup(utName);
<br />
...[/code:1]
<br />
看清楚了吗？ 和我上面写的代码 tx = new InitialContext().lookup("javax.transaction.UserTransaction"); 是不是完全一样？
<br />
<br />
[code:1]public void commit() ...
<br />
...
<br />
if (newTransaction) ut.commit();
<br />
...[/code:1]
<br />
JTATransaction的控制稍微复杂，不过仍然可以很清楚的看出来Hibernate是如何封装JTA的Transaction代码的。
<br />
<br />
但是你现在是否看到了什么问题？ 仔细想一下，Hibernate Transaction是从Session中获得的，tx =
session.beginTransaction()，最后要先提交tx，然后再session.close，这完全符合JDBC的
Transaction的操作顺序，但是这个顺序是和JTA的Transactioin操作顺序彻底矛盾的！！！
JTA是先启动Transaction，然后启动Session，关闭Session，最后提交Transaction，因此当你使用JTA的
Transaction的时候，那么就千万不要使用Hibernate的Transaction，而是应该像我上面的JTA的代码片断那样使用才行。
<br />
<br />
总结：
<br />
<br />
1、在JDBC上使用Hibernate
<br />
<br />
必须写上Hibernate Transaction代码，否则数据库没有反应。此时Hibernate的Transaction就是Connection.commit而已
<br />
<br />
2、在JTA上使用Hibernate
<br />
<br />
写JTA的Transaction代码，不要写Hibernate的Transaction代码，否则程序会报错
<br />
<br />
3、在EJB上使用Hibernate
<br />
<br />
什么Transactioin代码都不要写，在EJB的部署描述符里面配置
<br />
<br />
<br />
[code:1]
<br />
|---CMT(Container Managed Transaction)
<br />
|
<br />
|---BMT(Bean Managed Transaction)
<br />
|
<br />
|----JDBC Transaction
<br />
|
<br />
|----JTA Transaction
<br />
[/code:1]
<br />
<br />
<br />
robbin:
<br />
你说&#8220;Hibernate的JDBCTransaction根本就是conn.commit而已，根本毫无神秘可言，只不过在Hibernate
中，Session打开的时候，就会自动conn.setAutoCommit(false)，不像一般的JDBC，默认都是true，所以你最后不写
commit也没有关系，由于Hibernate已经把AutoCommit给关掉了，所以用Hibernate的时候，你在程序中不写
Transaction的话，数据库根本就没有反应&#8221;
<br />
但sf.opengSession()时，并没有setAutoCommit(false)，我想问的是，如果不编写任何事务代码，如：
<br />
[code:1]Session s = sf.openSession();
<br />
......
<br />
s.close();[/code:1]数据库会不会有反应（此时应该是默认AutoCommit为true）。
<br />
<br />
另外，我想问一下：
<br />
1. s.flush()是不是必须的
<br />
2. s.close()是不是一定要关闭
<br />
比如你上面提到的：
<br />
[code:1]javax.transaction.UserTransaction tx = new InitialContext().lookup("javax.transaction.UserTransaction");
<br />
<br />
Session s1 = sf.openSession();
<br />
...
<br />
s1.flush();
<br />
s1.close();
<br />
<br />
...
<br />
<br />
Session s2 = sf.openSession();
<br />
...
<br />
s2.flush();
<br />
s2.close();
<br />
<br />
tx.commit();[/code:1]
<br />
<br />
s1不关闭，使用s2进行操作的代码中使用s1可不可以（我觉得这样更加节约资源，不需要反复的连接、关闭）
<br />
<br />
<br />
<br />
<div>引用</div>
<div>但sf.opengSession()时，并没有setAutoCommit(false)，我想问的是，如果不编写任何事务代码，如：
<br />
Session s = sf.openSession();
<br />
......
<br />
s.close();
<br />
数据库会不会有反应（此时应该是默认AutoCommit为true）。</div>
<br />
<br />
不会有反应。在sf.openSession() 创建Session实例的时候，就已经调用了conn.setAutoCommit(false)了。
<br />
<br />
<br />
<div>引用</div>
<div>另外，我想问一下：
<br />
1. s.flush()是不是必须的
<br />
2. s.close()是不是一定要关闭 </div>
<br />
<br />
s.flush不是必须的，s.close()会调用一次s.flush()
<br />
<br />
s.close()正常情况下应该关闭，除非你是用ThreadLocal管理Session。
<br />
<br />
<br />
<div>引用</div>
<div>s1不关闭，使用s2进行操作的代码中使用s1可不可以（我觉得这样更加节约资源，不需要反复的连接、关闭）</div>
<br />
<br />
在这个例子中看不出来JTA的作用。
<br />
假设
<br />
[code:1]Class A  {
<br />
find() {
<br />
Session s1 = sf.openSession();
<br />
...
<br />
s1.flush();
<br />
s1.close();
<br />
}
<br />
}[/code:1]
<br />
<br />
[code:1]Class B  {
<br />
find() {
<br />
Session s2 = sf.openSession();
<br />
...
<br />
s2.flush();
<br />
s2.close();
<br />
}
<br />
}[/code:1]
<br />
<br />
[code:1]Main {
<br />
<br />
tx = ...;
<br />
A.find();
<br />
B.find();
<br />
tx.commit();
<br />
}[/code:1]
<br />
<br />
看明白了吗？JTA的Transaction管理是跨类调用的。
<img src ="http://www.blogjava.net/wanghao/aggbug/226176.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wanghao/" target="_blank">中间开花</a> 2008-09-01 20:04 <a href="http://www.blogjava.net/wanghao/articles/226176.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>update和saveOrUpdate详解</title><link>http://www.blogjava.net/wanghao/articles/226167.html</link><dc:creator>中间开花</dc:creator><author>中间开花</author><pubDate>Mon, 01 Sep 2008 11:31:00 GMT</pubDate><guid>http://www.blogjava.net/wanghao/articles/226167.html</guid><wfw:comment>http://www.blogjava.net/wanghao/comments/226167.html</wfw:comment><comments>http://www.blogjava.net/wanghao/articles/226167.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wanghao/comments/commentRss/226167.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wanghao/services/trackbacks/226167.html</trackback:ping><description><![CDATA[先来点概念：
<br />
<br />
在Hibernate中，最核心的概念就是对PO的状态管理。一个PO有三种状态：
<br />
<br />
1、未被持久化的VO
<br />
此时就是一个内存对象VO，由JVM管理生命周期
<br />
<br />
2、已被持久化的PO，并且在Session生命周期内
<br />
此时映射数据库数据，由数据库管理生命周期
<br />
<br />
3、曾被持久化过，但现在和Session已经detached了，以VO的身份在运行
<br />
这种和Session已经detached的PO还能够进入另一个Session，继续进行PO状态管理，此时它就成为PO的第二种状态了。<span style="color: red;">这种PO实际上是跨了Session进行了状态维护的。</span>
<br />
<br />
在传统的JDO1.x中，PO只有前面两种状态，一个PO一旦脱离PM，就丧失了状态了，不再和数据库数据关联，成为一个纯粹的内存VO，它即使进入一个新的PM，也不能恢复它的状态了。
<br />
<br />
Hibernate强的地方就在于，一个PO脱离Session之后，还能保持状态，再进入一个新的Session之后，就恢复状态管理的能力，
但此时状态管理需要使用session.update或者session.saveOrUpdate，这就是Hibernate
Reference中提到的&#8220;requires a slightly different programming model &#8221;
<br />
<br />
现在正式进入本话题：
<br />
<br />
<span style="color: red;">简单的来说，update和saveOrUpdate是用来对跨Session的PO进行状态管理的。</span>
<br />
<br />
假设你的PO不需要跨Session的话，那么就不需要用到，例如你打开一个Session，对PO进行操作，然后关闭，之后这个PO你也不会再用到了，那么就不需要用update。
<br />
<br />
因此，我们来看看：
<br />
[code:1]Foo foo=sess.load(Foo.class,id);
<br />
foo.setXXX(xxx);
<br />
sess.flush();
<br />
sess.commit(); [/code:1]
<br />
<br />
PO对象foo的操作都在一个Session生命周期内完成，因此不需要显式的进行sess.update(foo)这样的操作。
Hibernate会自动监测到foo对象已经被修改过，因此就向数据库发送一个update的sql。当然如果你非要加上
sess.update(foo)也不会错，只不过这样做没有任何必要。
<br />
<br />
而跨Session的意思就是说这个PO对象在Session关闭之后，你还把它当做一个VO来用，后来你在Session外面又修改了它的属性，然后你又想打开一个Session，把VO的属性修改保存到数据库里面，那么你就需要用update了。
<br />
<br />
[code:1]// in the first session
<br />
Cat cat = (Cat) firstSession.load(Cat.class, catId);
<br />
Cat potentialMate = new Cat();
<br />
firstSession.save(potentialMate);
<br />
<br />
// in a higher tier of the application
<br />
cat.setMate(potentialMate);
<br />
<br />
// later, in a new session
<br />
secondSession.update(cat);  // update cat
<br />
secondSession.update(mate); // update mate[/code:1]
<br />
<br />
cat和mate对象是在第一个session中取得的，在第一个session关闭之后，他们就成了PO的第三种状态，和Session已经
detached的PO，此时他们的状态信息仍然被保留下来了。当他们进入第二个session之后，立刻就可以进行状态的更新。但是由于对cat的修改
操作：cat.setMate(potentialMate);
是在Session外面进行的，Hibernate不可能知道cat对象已经被改过了，第二个Session并不知道这种修改，因此一定要显式的调用
secondSession.update(cat); 通知Hibernate，cat对象已经修改了，你必须发送update的sql了。
<br />
<br />
所以update的作用就在于此，它只会被用于当一个PO对象跨Session进行状态同步的时候才需要写。而一个PO对象当它不需要跨Session进行状态管理的时候，是不需要写update的。
<br />
<br />
再谈谈saveOrUpdate的用场：
<br />
<br />
saveOrUpdate和update的区别就在于在跨Session的PO状态管理中，Hibernate对PO采取何种策略。
<br />
<br />
例如当你写一个DAOImpl的时候，让cat对象增加一个mate，如下定义：
<br />
[code:1]public void addMate(Cat cat, Mate mate) {
<br />
Session session = ...;
<br />
Transacton tx = ...;
<br />
session.update(cat);
<br />
cat.addMate(mate);
<br />
tx.commit();
<br />
session.close();
<br />
};[/code:1]
<br />
<br />
显然你是需要把Hibernate的操作封装在DAO里面的，让业务层的程序员和Web层的程序员不需要了解Hibernate，直接对DAO进行调用。
<br />
<br />
此时问题就来了：上面的代码运行正确有一个必要的前提，那就是方法调用参数cat对象必须是一个已经被持久化过的PO，也就是来说，它应该首先从
数据库查询出来，然后才能这样用。但是业务层的程序员显然不知道这种内部的玄妙，如果他的业务是现在增加一个cat，然后再增加它的mate，他显然会这
样调用，new一个cat对象出来，然后就addMate：
<br />
<br />
[code:1]Cat cat = new Cat();
<br />
cat.setXXX();
<br />
daoimpl.addMate(cat,mate);[/code:1]
<br />
<br />
但是请注意看，这个cat对象只是一个VO，它没有被持久化过，它还不是PO，它没有资格调用addMate方法，因此调用addMate方法不
会真正往数据库里面发送update的sql，这个cat对象必须先被save到数据库，在真正成为一个PO之后，才具备addMate的资格。
<br />
<br />
你必须这样来操作：
<br />
<br />
[code:1]Cat cat = new Cat();
<br />
cat.setXXX();
<br />
daoimpl.addCat(cat);
<br />
daoimpl.addMate(cat, mate);[/code:1]
<br />
<br />
先持久化cat，然后才能对cat进行其他的持久化操作。因此要求业务层的程序员必须清楚cat对象处于何种状态，到底是第一种，还是第三种。如果是第一种，就要先save，再addMate；如果是第三种，就直接addMate。
<br />
<br />
但是最致命的是，如果整个软件分层很多，业务层的程序员他拿到这个cat对象也可能是上层Web应用层传递过来的cat，他自己也不知道这个cat究竟是VO，没有被持久化过，还是已经被持久化过，那么他根本就没有办法写程序了。
<br />
<br />
所以这样的DAOImpl显然是有问题的，它会对业务层的程序员造成很多编程上的陷阱，业务层的程序员必须深刻的了解他调用的每个DAO对PO对
象进行了何种状态管理，必须深刻的了解他的PO对象在任何时候处于什么确切的状态，才能保证编程的正确性，显然这是做不到的，但是有了
saveOrUpdate，这些问题就迎刃而解了。
<br />
<br />
现在你需要修改addMate方法：
<br />
<br />
[code:1]public void addMate(Cat cat, Mate mate) {
<br />
Session session = ...;
<br />
Transacton tx = ...;
<br />
session.saveOrUpdate(cat);
<br />
cat.addMate(mate);
<br />
tx.commit();
<br />
session.close();
<br />
};[/code:1]
<br />
<br />
如上，如果业务层的程序员传进来的是一个已经持久化过的PO对象，那么Hibernate会更新cat对象(假设业务层的程序员在Session外面修改过cat的属性)，如果传进来的是一个新new出来的对象，那么向数据库save这个PO对象。
<br />
<br />
BTW: Hibernate此时究竟采取更新cat对象，还是save cat对象，取决于unsave-value的设定。
<br />
<br />
这样，业务层的程序员就不必再操心PO的状态问题了，对于他们来说，不管cat是new出来的对象，只是一个VO也好；还是从数据库查询出来的的PO对象也好，全部都是直接addMate就OK了：
<br />
<br />
[code:1]daoimple.addMate(cat, mate);[/code:1]
<br />
<br />
这便是saveOrUpdate的作用。
<img src ="http://www.blogjava.net/wanghao/aggbug/226167.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wanghao/" target="_blank">中间开花</a> 2008-09-01 19:31 <a href="http://www.blogjava.net/wanghao/articles/226167.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>