save肯定是添加的時候用,update修改時候用,saveorupdate是添加或修改,如果真是這樣save和update的存在就沒意義了,我們直接saveorupdate就行了。
save在添加用的時候 不會出現索引機制(即遍歷目錄 效率最高)
update在修改時候要遍歷 不存在則會異常
saveorupdate是優先遍歷 如果不存在則創建(效率最低)
merge和saveOrUpdate方法區別在於:merge方法是把我們提供的對象轉變為托管狀態的對象;而saveOrUpdate則是把我們提供的對象變成一個持久化對象;
說的通俗一點就是:saveOrUpdate后的對象會納入session的管理,對象的狀態會跟數據庫同步,再次查詢該對象會直接從session中取,merge后的對
象不會納入session的管理,再次查詢該對象還是會從數據庫中取。
Session的save()方法使一個臨時對象轉變為持久化對象。例如以下代碼保存一個Customer對象:
Customer customer = new Customer();
customer.setId(new Long(9)); // 為Customer臨時對象設置OID是無效的
customer.setName("Tom");
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
session.save(customer);
session.close();
Session的save()方法完成以下的操作:
(1)把Customer對象加入到緩存中,使它變為持久化對象。
(2)選用映射文件指定的標識符生成器為持久化對象分配惟一的OID。Customer.hbm.xml文件中<id>元素的<generator>子元素指定標識符生成器:
<id name="id" column="ID">
<generator class="increment"/>
</id>
以上程序試圖通過setId()方法為Customer臨時對象設置OID是無效的。假如起初CUSTOMERS表中沒有記錄,那么執行完save()方法后,Customer對象的ID為1。如果希望由程序來為Customer對象指定OID,可以調用save()的另一個重載方法:
save(customer, new Long(9));
以上save()方法的第二參數顯示指定Customer對象的OID。這種形式的save()方法不推薦使用,尤其在使用代理主鍵的場合,不應該由程序為持久化對象指定OID。
(3)計划執行一個insert語句,把Customer對象當前的屬性值組裝到insert語句中:
insert into CUSTOMERS(ID, NAME, ......) values(1, 'Tom', ......);
值得注意的是,save()方法並不立即執行SQL insert語句。只有當Session清理緩存時,才會執行SQL insert語句。如果在save()方法之后,又修改了持久化對象的屬性,這會使得Session在清理緩存時,額外執行SQL update語句。以下兩段代碼盡管都能完成相同的功能,但是左邊代碼僅執行一條SQL insert語句,而右邊代碼執行一條SQL insert和一條SQL update語句。左邊代碼減少了操作數據庫的次數,具有更好的運行性能。
Customer customer = new Customer(); Customer customer = new Customer();
// 先設置Customer對象的屬性,再保存它 session.save(customer);
customer.setName("Tom"); // 先保存Customer對象,再修改它的屬性
session.save(customer); customer.setName("Tom");
transaction.commit(); transaction.commit();
Hibernate通過持久化對象的OID來維持它和數據庫相關記錄的對應關系。當Customer對象處於持久化狀態時,不允許程序隨意修改它的OID,例如:
Customer customer = new Customer();
session.save(customer);
customer.setId(new Long(100)); // 拋出HibernateException
transaction.commit();
以上代碼會導致Session在清理緩存時拋出異常。
無論Java對象處於臨時狀態、持久化狀態還是游離狀態,應用程序都不應該修改它的OID。因此,比較安全的做法是,在定義持久化類時,把它的setId()方法高為private類型,禁止外部程序訪問該方法。
Session的save()方法是用來持久化一個臨時對象的。在應用程序中不應該把持久化對象或游離對象傳給save()方法。例如以下代碼兩次調用了Session的save()方法,第二次傳給save()方法的Customer對角處於持久化狀態,這步操作其它是多余的:
Customer customer = new Customer();
session.save(customer);
customer.setName("Tom");
session.save(customer); // 這步操作是多余的
transaction.commit();
在例如以下代碼把Customer游離對象傳給session2的save()方法,session2會把它當做臨時對象處理,再次向數據庫中插入一條Customer記錄:
Customer customer = new Customer();
customer.setName("Tom");
Session session1 = sessionFactory.openSession();
Transaction tx1 = session1.beginTransaction();
session1.save(customer); // 此時Customer對象的ID變為1
tx1.commit();
session1.close(); // 此時Customer對象變為游離對象
Session session2 = sessionFactory.openSession();
Transaction tx2 = session2.beginTransaction();
session2.save(cutomer); // 此時Customer對象的ID變為2
tx2.commit();
session2.close();
盡管以上程序代碼能正常運行,但是會導致CUSTOMERS表中有兩條代表相同業務實體的記錄,因此不符合業務邏輯。
hibernate提供了saveOrUpdate的方法來進行數據庫的操作。hibernate會根據對象的狀態決定是insert還是update,
其根本是通過xml文件中unsaved-value來確定的。如果設置null,系統會根據傳入的對象的id的值判斷,如果是null,
則表示對象不存在,那么insert;如果不是Null,則表示已經存在,那么update.如果設置為none,那么表示對象不存在,
會始終調用insert;如果設置為any,那么表示對象始終存在,會始終調用update
數據庫表主鍵的知識點:
Generator 為每個 POJO 的實例提供唯一標識。一般情況,我們使用“native”。
class 表示采用由生成器接口net.sf.hibernate.id.IdentifierGenerator 實現的某個實例,其中包括:
“assigned” 主鍵由外部程序負責生成,在 save() 之前指定一個。
“hilo” 通過hi/lo 算法實現的主鍵生成機制,需要額外的數據庫表或字段提供高位值來源。
“seqhilo”與hilo 類似,通過hi/lo 算法實現的主鍵生成機制,需要數據庫中的 Sequence,
適用於支持 Sequence 的數據庫,如Oracle。
“increment”主鍵按數值順序遞增。此方式的實現機制為在當前應用實例中維持一個變量,以保存着當前的最大值,
之后每次需要生成主鍵的時候將此值加1作為主鍵。這種方式可能產生的問題是:不能在集群下使用。
“identity”采用數據庫提供的主鍵生成機制。如DB2、SQL Server、MySQL 中的主鍵生成機制。
“sequence”采用數據庫提供的 sequence 機制生成主鍵。如 Oralce 中的Sequence。
“native”由 Hibernate 根據使用的數據庫自行判斷采用 identity、hilo、sequence 其中一種作為主鍵生成方式。
“uuid.hex”由 Hibernate 基於128 位 UUID 算法 生成16 進制數值(編碼后以長度32 的字符串表示)作為主鍵。
“uuid.string”與uuid.hex 類似,只是生成的主鍵未進行編碼(長度16),不能應用在 PostgreSQL 數據庫中。
“foreign”使用另外一個相關聯的對象的標識符作為主鍵。
以下舉例:
1、指定參數的情況:
<id name="id" unsaved-value="0">
<generator class="sequence">
<param name="sequence">SEQ_CHILD</param>
</generator>
</id>
使用的是sequence,適合oracle數據庫;
2、對於sql server2000中的數據庫子增字段,在配置文件使用下列方法實現:
<id name="id" type="long" unsaved-value="0">
<column name="id" sql-type="numeric" not-null="true" />
<generator class="identity" />
</id>
這里主要是:identity:代表由sql server2000數據庫自己提供子增字段.如果要hibernate自己提供,則用increment關鍵字來實現
3、如果表中的主鍵用字符串類型:可以用hibernate自己提供的方法實現主鍵唯一:
<id name="id" type="string" unsaved-value="null">
<column name="cid" sql-type="char(32)" not-null="true" />
<generator class="uuid.hex" />
</id>
使用的是uuid.hex: 采用128位的算法來生成一個32位字符串。最通用的一種方式。適用於所有數據庫。
重要的知識點:
1. 如果有部門表,有員工表,員工表中有dep_id,則表部門類和員工類是one-to-many的關系:
可以使用: ( 在部門類department中使用下列)
Department類:
/** 部門的所有員工 */
private Set staffs = new TreeSet();
xml的文件:
<set name="staffs" >
<key column="dep_id"/>
<one-to-many class="hbp.sys.data.Staff"/>
</set>
如果是list,需要用索引<index> </index>,具體其中的含義,不是很明白.待以后研究
2. 如果部門要有一個負責人,即部門表(tb_department)中有一個字段:staff_id.
那么表示部門和負責人之間的關系是many-to-one的關系
Department類:
/** 部門負責人id */
private Staff staff;
xml 文件
<many-to-one name="staff" class="hbp.sys.data.Staff" column="staff_id"/>
3. 多對多關系,一般我們是做一個中間關聯表.我用角色和權限做了個例子,
Right(id,name) Role(id,name) 中間表:tb_role_right(role_id,right_id)
Right類中有一個Role的集合:private Set roles=new TreeSet();
Role類中也有一個Right的集合:private Set rights=new TreeSet();
則兩者是明顯的多對多關系.使用many-to-many來實現;
xml文件中
right.hbm.xml:如下:
<set name="roles" table="tb_role_right" cascade="all">
<key column="right_id"/>
<many-to-many column="role_id" class="hbp.sys.data.Role"/>
</set>
role.hbm.xml文件中類似:
<set name="rights" table="tb_role_right" cascade="all">
<key column="role_id"/>
<many-to-many column="right_id" class="hbp.sys.data.Right"/>
</set>
4. 幾個值得注意的問題:
a)在xml?映射文件中,寫類的名字時一定用類的全名:即:包+類名如:(hbp.sys.data.Staff),這個錯誤使我費了半天勁.:(
b)我在寫實現基本DAO操作時,寫了
session.delete("from Right as right where right.id="+id);
程序死活報錯,我折騰了半天,跟蹤到底,才恍然大悟,hibernate在解析sql語句的時候把
其中的right,當成了數據庫中的右連接("保留字"),唉,這種關鍵字,不能隨便用啊,:)
5. hibernate中HQL語言的查詢根據你的sql的不同而返回不同的對象類型.
如果你使用session.find(String hql)
一般會返回一個List,如:from Staff staff;返回的是包含所有的員工對象的集合
如你的hql為:select count(*) from Staff staff;則返回的是一個Integer對象
如果你使用的hql為:select count(distinct staff.name),count(*) from Staff staff;則返回的是一個Object
即Object[],需要先把他轉換成Object[],然后在取[0],[1].
這種設計我不知道hibernate是如何處理的,感覺既好也不好.好的是可以使用一個find獲得任意查詢
不好在於根據hql來處理返回結果,容易出錯.