hibernat學習

http://www.vshj.com/Article/2005/200511/Article_11922.htm(原地址)

Hibernate學習 第四天 基礎配置

http://www.gd-linux.org/bbs/archive/index.php/t-1108.html

Hibernate入門 第五天 包作用詳解

http://www.gd-linux.com/bbs/showthread.php?t=1121

Hibernate入門 第六天 - Transaction 

 http://www.gd-linux.com/bbs/showthread.php?t=1128

Hibernate學習 第七天 update和saveOrUpdate詳解

http://www.gd-linux.org/bbs/archive/index.php/t-1146.html

 

龐勁松
05-02-03, 09:44
Hibernate配置文件可以有兩種格式,一種是 hibernate.properties ,另一種是 hibernate.cfg.xml

後者稍微方便一些,當增加hbm映射文件的時候,可以直接在 hibernate.cfg.xml 裏面增加,不必像 hibernate.properties 必須在初始化代碼中加入。

但不管怎麼說,兩種的配置項都是一樣的,下面詳細介紹:

在Hibernate的src目錄下有一個 hibernate.properties 模板,我們不必自己從頭寫,修改模板就可以了:)


hibernate.query.substitutions true 1, false 0, yes 'Y', no 'N'

這個配置意思是當你在Hibernate裏面輸入true的時候,Hibernate會轉化爲1插入數據庫,當你在Hibernate裏面輸入false的時候,Hibernate會轉化爲0插入數據庫,後面的Y,N同理。

對於某些數據庫,例如Oracle來說,沒有boolean數據類型,就是採用1代表true,0代表false,因此使用這個配置在Hibernate裏面直接用true/false會非常直觀。


hibernate.dialect net.sf.hibernate.dialect.MySQLDialect
hibernate.connection.driver_class com.mysql.jdbc.Driver
hibernate.connection.url jdbc:mysql:///test
hibernate.connection.username root
hibernate.connection.password

這是一個連接MySQL數據庫的例子,很直觀,不必解釋,不同的數據庫的連接參數模板中全部給出了。


hibernate.connection.pool_size 1
hibernate.statement_cache.size 25

這是Hibernate自帶的連接池的配置參數,在默認情況下將採用。意義很直觀,不多解釋。

只是提醒一點,Hibernate這個連接池是非常原始非常簡單的連接池,如果你在項目中用Hibernate的話,建議你首選App Server的連接池,次選Hibernate帶的DBCP連接池。自帶的連接池應該做爲末選。

如果你採用DBCP連接池,除了要配置DBCP連接池以外,還需要取消掉下行的註釋:

hibernate.connection.provider_class net.sf.hibernate.connection.DBCPConnectionProvider

其它的連接池同理。

如果採用App Server的連接池,假設App Server連接池的DataSource的JNDI名稱爲"mypool"的話,配置應該如下:

hibernate.dialect net.sf.hibernate.dialect.MySQLDialect
hibernate.connection.datasource mypool
hibernate.connection.provider_class net.sf.hibernate.connection.DatasourceConnectionProvider

其它參數就不必寫了,因爲已經在App Server配置連接池的時候指定好了。

如果你不是在App Server環境中使用Hibernate,例如遠程客戶端程序,但是你又想用App Server的數據庫連接池,那麼你還需要配置JNDI的參數,例如Hibernate連接遠程Weblogic上的數據庫連接池:

hibernate.dialect net.sf.hibernate.dialect.MySQLDialect
hibernate.connection.datasource mypool
hibernate.connection.provider_class net.sf.hibernate.connection.DatasourceConnectionProvider
hibernate.jndi.class weblogic.jndi.WLInitialContextFactory
hibernate.jndi.url t3://servername:7001/


最後,如果你需要在EJB或者JTA中使用Hibernate,需要取消下行的註釋:

hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory

雜項配置:


hibernate.show_sql false

是否將Hibernate發送給數據庫的sql顯示出來,這是一個非常非常有用處的功能。當你在調試Hibernate的時候,讓Hibernate打印sql語句,可以幫助你迅速解決問題。


#hibernate.connection.isolation 4

指定數據庫的隔離級別,往往不同的數據庫有自己定義的隔離級別,未必是Hibernate的設置所能更改的,所以也不必去管它了。


hibernate.jdbc.fetch_size 50
hibernate.jdbc.batch_size 25

這兩個選項非常非常非常重要!!!將嚴重影響Hibernate的CRUD性能!

C = create, R = read, U = update, D = delete

Fetch Size 是設定JDBC的Statement讀取數據的時候每次從數據庫中取出的記錄條數。

例如一次查詢1萬條記錄,對於Oracle的JDBC驅動來說,是不會1次性把1萬條取出來的,而只會取出Fetch Size條數,當紀錄集遍歷完了這些記錄以後,再去數據庫取Fetch Size條數據。

因此大大節省了無謂的內存消耗。當然Fetch Size設的越大,讀數據庫的次數越少,速度越快;Fetch Size越小,讀數據庫的次數越多,速度越慢。

這有點像平時我們寫程序寫硬盤文件一樣,設立一個Buffer,每次寫入Buffer,等Buffer滿了以後,一次寫入硬盤,道理相同。

Oracle數據庫的JDBC驅動默認的Fetch Size=10,是一個非常保守的設定,根據我的測試,當Fetch Size=50的時候,性能會提升1倍之多,當Fetch Size=100,性能還能繼續提升20%,Fetch Size繼續增大,性能提升的就不顯著了。

因此我建議使用Oracle的一定要將Fetch Size設到50。

不過並不是所有的數據庫都支持Fetch Size特性,例如MySQL就不支持。

MySQL就像我上面說的那種最壞的情況,他總是一下就把1萬條記錄完全取出來,內存消耗會非常非常驚人!這個情況就沒有什麼好辦法了 :(

Batch Size是設定對數據庫進行批量刪除,批量更新和批量插入的時候的批次大小,有點相當於設置Buffer緩衝區大小的意思。

Batch Size越大,批量操作的向數據庫發送sql的次數越少,速度就越快。我做的一個測試結果是當Batch Size=0的時候,使用Hibernate對Oracle數據庫刪除1萬條記錄需要25秒,Batch Size = 50的時候,刪除僅僅需要5秒!!!

可見有多麼大的性能提升!很多人做Hibernate和JDBC的插入性能測試會奇怪的發現Hibernate速度至少是JDBC的兩倍,就是因爲Hibernate使用了Batch Insert,而他們寫的JDBC沒有使用Batch的緣故。

以我的經驗來看,Oracle數據庫 Batch Size = 30 的時候比較合適,50也不錯,性能會繼續提升,50以上,性能提升的非常微弱,反而消耗內存更加多,就沒有必要了。


#hibernate.jdbc.use_scrollable_resultset true

設定是否可以使用JDBC2.0規範的可滾動結果集,這對Hibernate的分頁顯示有一定的作用,默認就好了。


#hibernate.cglib.use_reflection_optimizer false

默認打開,啓用cglib反射優化。cglib是用來在Hibernate中動態生成PO字節碼的,打開優化可以加快字節碼構造的速度。

不過,當你在調試程序過程中,特別是和proxy,lazy loading相關的應用中,代碼出錯,但是出錯提示信息有語焉不詳,那麼你可以把cglib優化關掉,這樣Hibernate會輸出比較詳細的調試信息,幫助你debug。
 Hibernate入門 第五天 包作用詳解
Hibernate一共包括了23個jar包,令人眼花繚亂。本文將詳細講解Hibernate每個jar包的作用,便於你在應用中根據自己的需要進行取捨。

下載Hibernate,例如2.0.3穩定版本,解壓縮,可以看到一個hibernate2.jar和lib目錄下有22個jar包:

hibernate2.jar:
Hibernate的庫,沒有什麼可說的,必須使用的jar包

cglib-asm.jar:
CGLIB庫,Hibernate用它來實現PO字節碼的動態生成,非常核心的庫,必須使用的jar包

dom4j.jar:
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用它來讀寫配置文件。

odmg.jar:
ODMG是一個ORM的規範,Hibernate實現了ODMG規範,這是一個核心的庫,必須使用的jar包。

commons-collections.jar:
Apache Commons包中的一個,包含了一些Apache開發的集合類,功能比java.util.*強大。必須使用的jar包。

commons-beanutils.jar:
Apache Commons包中的一個,包含了一些Bean工具類類。必須使用的jar包。

commons-lang.jar:
Apache Commons包中的一個,包含了一些數據類型工具類,是java.lang.*的擴展。必須使用的jar包。

commons-logging.jar:
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包。

使用Hibernate必須的jar包就是以上的這幾個,剩下的都是可選的。

ant.jar:
Ant編譯工具的jar包,用來編譯Hibernate源代碼的。如果你不準備修改和編譯Hibernate源代碼,那麼就沒有什麼用,可選的jar包

optional.jar:
Ant的一個輔助包。

c3p0.jar:
C3PO是一個數據庫連接池,Hibernate可以配置爲使用C3PO連接池。如果你準備用這個連接池,就需要這個jar包。

proxool.jar:
也是一個連接池,同上。

commons-pool.jar, commons-dbcp.jar:
DBCP數據庫連接池,Apache的Jakarta組織開發的,Tomcat4的連接池也是DBCP。

實際上Hibernate自己也實現了一個非常非常簡單的數據庫連接池,加上上面3個,你實際上可以在Hibernate上選擇4種不同的數據庫連接池,選擇哪一個看個人的偏好,不過DBCP可能更通用一些。另外強調一點,如果在EJB中使用Hibernate,一定要用App Server的連接池,不要用以上4種連接池,否則容器管理事務不起作用。

connector.jar:
JCA 規範,如果你在App Server上把Hibernate配置爲Connector的話,就需要這個jar。不過實際上一般App Server肯定會帶上這個包,所以實際上是多餘的包。

jaas.jar:
JAAS是用來進行權限驗證的,已經包含在JDK1.4裏面了。所以實際上是多餘的包。

jcs.jar:
如果你準備在Hibernate中使用JCS的話,那麼必須包括它,否則就不用。

jdbc2_0-stdext.jar:
JDBC2.0的擴展包,一般來說數據庫連接池會用上它。不過App Server都會帶上,所以也是多餘的。

jta.jar:
JTA規範,當Hibernate使用JTA的時候需要,不過App Server都會帶上,所以也是多餘的。

junit.jar:
Junit包,當你運行Hibernate自帶的測試代碼的時候需要,否則就不用。

xalan.jar, xerces.jar, xml-apis.jar:
Xerces是XML解析器,Xalan是格式化器,xml-apis實際上是JAXP。一般App Server都會帶上,JDK1.4也包含了解析器,不過不是Xerces,是Crimson,效率比較差,不過Hibernate用XML只不過是讀取配置文件,性能沒什麼緊要的,所以也是多餘的。
 Hibernate入門 第六天 - Transaction
Hibernate是對JDBC的輕量級對象封裝,Hibernate本身是不具備Transaction處理功能的,Hibernate的Transaction實際上是底層的JDBC Transaction的封裝,或者是JTA Transaction的封裝,下面我們詳細的分析:

Hibernate可以配置爲JDBCTransaction或者是JTATransaction,這取決於你在hibernate.properties中的配置:

#hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory
#hibernate.transaction.factory_class net.sf.hibernate.transaction.JDBCTransactionFactory

如果你什麼都不配置,默認情況下使用JDBCTransaction,如果你配置爲:

hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory

將使用JTATransaction

不管你準備讓Hibernate使用JDBCTransaction,還是JTATransaction,我的忠告就是什麼都不配,將讓它保持默認狀態,如下:

#hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory
#hibernate.transaction.factory_class net.sf.hibernate.transaction.JDBCTransactionFactory

在下面的分析中我會給出原因。

一、JDBC Transaction

看看使用JDBC Transaction的時候我們的代碼例子:

Session session = sf.openSession();
Transaction tx = session.beginTransactioin();
...
session.flush();
tx.commit();
session.close();

這是默認的情況,當你在代碼中使用Hibernate的Transaction的時候實際上就是JDBCTransaction。那麼JDBCTransaction究竟是什麼東西呢?來看看源代碼就清楚了:

Hibernate2.0.3源代碼中的類

net.sf.hibernate.transaction.JDBCTransaction:

public void begin() throws HibernateException {

...
if (toggleAutoCommit) session.connection().setAutoCommit(false);
...
}

這是啓動Transaction的方法,看到 connection().setAutoCommit(false) 了嗎?是不是很熟悉?

再來看

public void commit() throws HibernateException {
...
try {
if ( session.getFlushMode()!=FlushMode.NEVER ) session.flush();
try {
session.connection().commit();
committed = true;
}
...
toggleAutoCommit();
}


這是提交方法,看到connection().commit() 了嗎?下面就不用我多說了,這個類代碼非常簡單易懂,通過閱讀使我們明白Hibernate的Transaction都在幹了些什麼?我現在把用Hibernate寫的例子翻譯成JDBC,大家就一目瞭然了:

Connection conn = ...; <--- session = sf.openSession();

conn.setAutoCommit(false); <--- tx = session.beginTransactioin();

... <--- ...

conn.commit(); <--- tx.commit(); (對應左邊的兩句)
conn.setAutoCommit(true);

conn.close(); <--- session.close();

看明白了吧,Hibernate的JDBCTransaction根本就是conn.commit而已,根本毫無神祕可言,只不過在Hibernate中,Session打開的時候,就會自動conn.setAutoCommit(false),不像一般的JDBC,默認都是true,所以你最後不寫commit也沒有關係,由於Hibernate已經把AutoCommit給關掉了,所以用Hibernate的時候,你在程序中不寫Transaction的話,數據庫根本就沒有反應。

二、JTATransaction

如果你在EJB中使用Hibernate,或者準備用JTA來管理跨Session的長事務,那麼就需要使用JTATransaction,先看一個例子:

javax.transaction.UserTransaction tx = new InitialContext().lookup("javax.transaction.UserTransaction");

Session s1 = sf.openSession();
...
s1.flush();
s1.close();

...

Session s2 = sf.openSession();
...
s2.flush();
s2.close();

tx.commit();

這是標準的使用JTA的代碼片斷,Transaction是跨Session的,它的生命週期比Session要長。如果你在EJB中使用Hibernate,那麼是最簡單不過的了,你什麼Transaction代碼統統都不要寫了,直接在EJB的部署描述符上配置某某方法是否使用事務就可以了。

現在我們來分析一下JTATransaction的源代碼, net.sf.hibernate.transaction.JTATransaction:

public void begin(InitialContext context, ...
...
ut = (UserTransaction) context.lookup(utName);
...

看清楚了嗎? 和我上面寫的代碼 tx = new (Initial Context)().lookup("javax.transaction.UserTransaction"); 是不是完全一樣?

public void commit() ...
...
if (newTransaction) ut.commit();
...


JTATransaction的控制稍微複雜,不過仍然可以很清楚的看出來Hibernate是如何封裝JTA的Transaction代碼的。

但是你現在是否看到了什麼問題? 仔細想一下,Hibernate Transaction是從Session中獲得的,tx = session.beginTransaction(),最後要先提交tx,然後再session.close,這完全符合JDBC的Transaction的操作順序,但是這個順序是和JTA的Transactioin操作順序徹底矛盾的!!! JTA是先啓動Transaction,然後啓動Session,關閉Session,最後提交Transaction,因此當你使用JTA的Transaction的時候,那麼就千萬不要使用Hibernate的Transaction,而是應該像我上面的JTA的代碼片斷那樣使用纔行。

總結:
1、在JDBC上使用Hibernate

必須寫上Hibernate Transaction代碼,否則數據庫沒有反應。此時Hibernate的Transaction就是Connection.commit而已

2、在JTA上使用Hibernate

寫JTA的Transaction代碼,不要寫Hibernate的Transaction代碼,否則程序會報錯

3、在EJB上使用Hibernate

什麼Transactioin代碼都不要寫,在EJB的部署描述符裏面配置

|---CMT(Container Managed Transaction)
|
|---BMT(Bean Managed Transaction)
|
|----JDBC Transaction
|
|----JTA Transaction


--------------------------------------------------------------------------------

提問:

javax.transaction.UserTransaction tx = new InitialContext().lookup("javax.transaction.UserTransaction");

Session s1 = sf.openSession();
...
s1.flush();
s1.close();

...

Session s2 = sf.openSession();
...
s2.flush();
s2.close();

tx.commit();

s1不關閉,使用s2進行操作的代碼中使用s1可不可以(我覺得這樣更加節約資源,不需要反覆的連接、關閉)

但sf.opengSession()時,並沒有setAutoCommit(false),我想問的是,如果不編寫任何事務代碼,如:
Session s = sf.openSession();
......
s.close();
數據庫會不會有反應(此時應該是默認AutoCommit爲true)。

不會有反應。在sf.openSession() 創建Session實例的時候,就已經調用了conn.setAutoCommit(false)了。

另外,我想問一下:

<code>

1. s.flush()是不是必須的

2. s.close()是不是一定要關閉

</code>


--------------------------------------------------------------------------------

回答:

s.flush不是必須的,s.close()會調用一次s.flush()

s.close()正常情況下應該關閉,除非你是用ThreadLocal管理Session。

s1不關閉,使用s2進行操作的代碼中使用s1可不可以(我覺得這樣更加節約資源,不需要反覆的連接、關閉)

在這個例子中看不出來JTA的作用。

假設

Class A {
find() {
Session s1 = sf.openSession();
...
s1.flush();
s1.close();
}
}

Class B {
find() {
Session s2 = sf.openSession();
...
s2.flush();
s2.close();
}
}

Main {

tx = ...;
A.find();
B.find();
tx.commit();
}

看明白了嗎?JTA的Transaction管理是跨類調用的。
 Hibernate學習 第七天 update和saveOrUpdate詳解
龐勁松
05-02-06, 11:47
在Hibernate中,最核心的概念就是對PO的狀態管理。一個PO有三種狀態:

1、未被持久化的VO

此時就是一個內存對象VO,由JVM管理生命週期

2、已被持久化的PO,並且在Session生命週期內

此時映射數據庫數據,由數據庫管理生命週期

3、曾被持久化過,但現在和Session已經detached了,以VO的身份在運行

這種和Session已經detached的PO還能夠進入另一個Session,繼續進行PO狀態管理,此時它就成爲PO的第二種狀態了。這種PO實際上是跨了Session進行了狀態維護的。

在傳統的JDO1.x中,PO只有前面兩種狀態,一個PO一旦脫離PM,就喪失了狀態了,不再和數據庫數據關聯,成爲一個純粹的內存VO,它即使進入一個新的PM,也不能恢復它的狀態了。

Hibernate強的地方就在於,一個PO脫離Session之後,還能保持狀態,再進入一個新的Session之後,就恢復狀態管理的能力,但此時狀態管理需要使用session.update或者session.saveOrUpdate,這就是Hibernate Reference中提到的“requires a slightly different programming model ”

現在正式進入本話題:

簡單的來說,update和saveOrUpdate是用來對跨Session的PO進行狀態管理的。

假設你的PO不需要跨Session的話,那麼就不需要用到,例如你打開一個Session,對PO進行操作,然後關閉,之後這個PO你也不會再用到了,那麼就不需要用update。

因此,我們來看看:

Foo foo=sess.load(Foo.class,id);
foo.setXXX(xxx);
sess.flush();
sess.commit();

PO對象foo的操作都在一個Session生命週期內完成,因此不需要顯式的進行sess.update(foo)這樣的操作。Hibernate會自動監測到foo對象已經被修改過,因此就向數據庫發送一個update的sql。當然如果你非要加上sess.update(foo)也不會錯,只不過這樣做沒有任何必要。

而跨Session的意思就是說這個PO對象在Session關閉之後,你還把它當做一個VO來用,後來你在Session外面又修改了它的屬性,然後你又想打開一個Session,把VO的屬性修改保存到數據庫裏面,那麼你就需要用update了。

// in the first session
Cat cat = (Cat) firstSession.load(Cat.class, catId);
Cat potentialMate = new Cat();
firstSession.save(potentialMate);

// in a higher tier of the application
cat.setMate(potentialMate);

// later, in a new session
secondSession.update(cat); // update cat
secondSession.update(mate); // update mate

cat和mate對象是在第一個session中取得的,在第一個session關閉之後,他們就成了PO的第三種狀態,和Session已經detached的PO,此時他們的狀態信息仍然被保留下來了。當他們進入第二個session之後,立刻就可以進行狀態的更新。但是由於對cat的修改操作:cat.setMate(potentialMate); 是在Session外面進行的,Hibernate不可能知道cat對象已經被改過了,第二個Session並不知道這種修改,因此一定要顯式的調用secondSession.update(cat); 通知Hibernate,cat對象已經修改了,你必鬚髮送update的sql了。

所以update的作用就在於此,它只會被用於當一個PO對象跨Session進行狀態同步的時候才需要寫。而一個PO對象當它不需要跨Session進行狀態管理的時候,是不需要寫update的。

再談談saveOrUpdate的用場:


saveOrUpdate和update的區別就在於在跨Session的PO狀態管理中,Hibernate對PO採取何種策略。

例如當你寫一個DAOImpl的時候,讓cat對象增加一個mate,如下定義:

public void addMate(Cat cat, Mate mate) {
Session session = ...;
Transacton tx = ...;
session.update(cat);
cat.addMate(mate);
tx.commit();
session.close();
};

顯然你是需要把Hibernate的操作封裝在DAO裏面的,讓業務層的程序員和Web層的程序員不需要了解Hibernate,直接對DAO進行調用。

此時問題就來了:上面的代碼運行正確有一個必要的前提,那就是方法調用參數cat對象必須是一個已經被持久化過的PO,也就是來說,它應該首先從數據庫查詢出來,然後才能這樣用。但是業務層的程序員顯然不知道這種內部的玄妙,如果他的業務是現在增加一個cat,然後再增加它的mate,他顯然會這樣調用,new一個cat對象出來,然後就addMate:

Cat cat = new Cat();
cat.setXXX();
daoimpl.addMate(cat,mate);

但是請注意看,這個cat對象只是一個VO,它沒有被持久化過,它還不是PO,它沒有資格調用addMate方法,因此調用addMate方法不會真正往數據庫裏面發送update的sql,這個cat對象必須先被save到數據庫,在真正成爲一個PO之後,才具備addMate的資格。

你必須這樣來操作:

Cat cat = new Cat();
cat.setXXX();
daoimpl.addCat(cat);
daoimpl.addMate(cat, mate);

先持久化cat,然後才能對cat進行其他的持久化操作。因此要求業務層的程序員必須清楚cat對象處於何種狀態,到底是第一種,還是第三種。如果是第一種,就要先save,再addMate;如果是第三種,就直接addMate。

但是最致命的是,如果整個軟件分層很多,業務層的程序員他拿到這個cat對象也可能是上層Web應用層傳遞過來的cat,他自己也不知道這個cat究竟是VO,沒有被持久化過,還是已經被持久化過,那麼他根本就沒有辦法寫程序了。

所以這樣的DAOImpl顯然是有問題的,它會對業務層的程序員造成很多編程上的陷阱,業務層的程序員必須深刻的瞭解他調用的每個DAO對PO對象進行了何種狀態管理,必須深刻的瞭解他的PO對象在任何時候處於什麼確切的狀態,才能保證編程的正確性,顯然這是做不到的,但是有了saveOrUpdate,這些問題就迎刃而解了。

現在你需要修改addMate方法:

public void addMate(Cat cat, Mate mate) {
Session session = ...;
Transacton tx = ...;
session.saveOrUpdate(cat);
cat.addMate(mate);
tx.commit();
session.close();
};

如上,如果業務層的程序員傳進來的是一個已經持久化過的PO對象,那麼Hibernate會更新cat對象(假設業務層的程序員在Session外面修改過cat的屬性),如果傳進來的是一個新new出來的對象,那麼向數據庫save這個PO對象。

BTW: Hibernate此時究竟採取更新cat對象,還是save cat對象,取決於unsave-value的設定。

這樣,業務層的程序員就不必再操心PO的狀態問題了,對於他們來說,不管cat是new出來的對象,只是一個VO也好;還是從數據庫查詢出來的的PO對象也好,全部都是直接addMate就OK了:

daoimple.addMate(cat, mate);

這便是saveOrUpdate的作用。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章