hibernate將orm對象的種類分爲四種,分別是:java
臨時狀態sql
臨時狀態通常來講就是咱們使用new關鍵字建立的對象,尚未和session關聯上,而且對象的id爲null。調用session的save方法、saveorupdate方法、persist方法、merge方法、get方法和load方法均可以使臨時狀態對象變爲持久化狀態對象。
數據庫
持久化狀態編程
持久化狀態也被稱爲託管狀態,又session緩存進行託管,持久化對象的id不爲空,而且與數據庫中的記錄相互對應。調用session的evict方法、clear方法和close方法可使持久化狀態對象變爲遊離狀態對象。
緩存
遊離狀態session
遊離狀態能夠理解爲對象從session緩存中取出後,session緩存被清空了,或者session被關閉了,又或者調用了evict方法將該對象手動從session緩存中刪除了,這時session再也不對該對象進行託管。遊離狀態與臨時狀態的一個區別就是,遊離狀態的對象的id不爲空,而且數據庫中有可能還保存着這個對象的信息。app
調用session的update方法、saveorupdate方法和merge方法可使遊離狀態對象變爲持久化狀態對象。
ide
刪除狀態測試
調用了session的delete方法後,被delete的對象的狀態將變爲刪除狀態。通常來講刪除狀態的對象將不會在使用了,當事務commit的時候,數據庫中也會將該對象刪除。.net
以上就是orm對象在session中的四種狀態,接下來測試一下session的經常使用方法。首先是save方法:
@Test public void testSave() { // 建立一個對象,該對象如今爲臨時狀態。 User user = new User(); // 此處設置id的值不會生效 // save方法將調用sql本地生成方式重新爲對象id賦值。 user.setId(100); user.setName("Kobe"); user.setBirthday(new Date()); // 調用save方法將對象狀態改變爲持久化狀態,由session緩存託管。 session.save(user); // 持久化後的對象id不容許修改,不然將出現異常。 // org.hibernate.HibernateException: // identifier of an instance of cn.net.bysoft.model.User was altered // from 1 to 13 // user.setId(13); }
上面是save方法的測試,須要注意的就是在save以前,設置對象的id無效,save方法將調用id生成方式生成一個id複製給對象。在save後,不可修改id,不然將會拋出異常。接下來了persist方法:
@Test public void testPersist() { User user = new User(); // 使用persist保存對象,若是設置了id值將會拋出異常。 // user.setId(111); user.setName("Jordan"); user.setBirthday(new Date()); session.persist(user); }
使用persist方法也能夠將對象從臨時狀態改變爲持久化狀態,而且保存到數據庫中。與save方法的區別在於若是,調用save方法前設置的id無效,但不會發生異常,而使用persist方法以前若是設置了id,將會拋出異常。
在看看get方法:
@Test public void testGet() { // 從數據庫中得到id=3的記錄,加載到User對象中。 // User對象從臨時狀態改變到持久化狀態。 User user = (User) session.get(User.class, 3); System.out.println(user); /** * output: User [id=3, name=Jordan, birthday=2016-03-25 19:56:14.0] * */ // 若是傳入的id在數據庫中找不到記錄,則返回null。 User user2 = (User) session.get(User.class, 44); System.out.println(user2); /** * output: null * */ }
調用get方法後,會發送一條select語句到數據庫,查找id對應的記錄,若是查找到,則將記錄加載到對象中,並把對象的狀態改變爲持久化狀態,若是沒用經過id得到記錄,則返回null。
get方法以後,看看load方法:
@Test public void testLoad() { // load方法建立了一個user的代理對象,執行load後並不會發送select語句。 User user = (User) session.load(User.class, 3); // 在使用user對象的屬性時,才發送select語句查詢數據,這種機制叫作延遲加載。 System.out.println(user); /** * output: User [id=3, name=Jordan, birthday=2016-03-25 19:56:14.0] * */ // 若是傳入的id在數據庫中找不到記錄。 User user2 = (User) session.load(User.class, 44); // 在使用該對象時會拋出異常。 System.out.println(user2); }
在執行load方法後,hibernate不會當即去發送select語句查詢數據庫,而是建立一個代理,等到調用對象的屬性時,由代理去發送select語句查詢數據。
若load對象使用的id在數據庫中不存在,則會拋出異常。
雖然都是從數據庫查詢數據加載到對象,但兩個方法略有不一樣,對比一下get方法與load方法:
get方法 |
load方法 |
|
什麼時候執行select語句 |
調用get方法後當即執行select語句。 |
調用load方法後不執行select語句,而是建立一個代理。 等到使用對象的屬性時再執行select語句。 |
若是id在數據庫中不存在 |
調用get方法返回null |
調用load方法後,在使用該對象時會拋出異常。 |
在使用對象前調用session.close |
不會拋出異常 |
會拋出異常 |
以上是get方法與load方法,接下來測試一下update方法:
@Test public void testUpdate(){ // update方法能夠將遊離對象變爲持久化對象。 User user = new User(); user.setId(2); user.setName("Jack"); user.setBirthday(new Date()); // 調用update將上面的遊離狀態對象變爲持久化狀態對象。 // 而且更新數據庫中的記錄。 session.update(user); }
建立一個User對象,這裏由於給user設置了id,id不爲空的話就不是臨時對象,而是遊離對象。經過update方法把遊離對象的狀態改爲持久化對象,而且向數據庫發送了update語句。
在調用flush的時候,若有必要,hibernate也會發送update語句,這裏,數據庫中如有觸發器,則會頻繁出發。這些出發中可能有沒必要要的update操做。hibernate提供了select-before-update屬性來解決這個問題,將該屬性設置成true,在update以前會查詢一次數據庫,若是對象沒有變化則不執行update。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <!-- update以前先進行select,對象與數據庫中的記錄一致,則不執行update --> <class name="cn.net.bysoft.model.User" table="S_USER" select-before-update="true"> <id name="id" type="integer" column="ID"> <!-- 指定主鍵的生成方式,native是使用數據庫本地的方式 --> <generator class="native"></generator> </id> <property name="name" type="string" column="NAME"></property> <property name="birthday" type="timestamp" column="BIRTHDAY"></property> </class> </hibernate-mapping>
update時,傳入的id若是數據庫中不存在,則拋出異常。
saveorupdate方法顧名思義是傳入一個對象,由hibernate分析是save仍是update。規則是,若是對象id是null就save,若是id不是null就update。
@Test public void testSaveOrUpdate() { User user = new User(); user.setName("Mary"); user.setBirthday(new Date()); // user的id爲null,執行insert操做。 session.saveOrUpdate(user); User user2 = new User(); user2.setId(2); user2.setName("Kobe"); user2.setBirthday(new Date()); // user的id爲2,數據庫中有id等於2的記錄,將執行update操做。 session.saveOrUpdate(user2); }
第一個saveorupdate將輸出insert語句,由於id is null。第二個將輸出update語句,由於id=2,在數據庫中存在這條記錄。
若在*.hbm.xml的id屬性上設置了unsaved-value的值,在作saveorupdate時,id不爲null,可是值等於unsaved-value設置的值,也會進行save。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <!-- update以前先進行select,對象與數據庫中的記錄一致,則不執行update --> <class name="cn.net.bysoft.model.User" table="S_USER" select-before-update="true"> <id name="id" type="integer" column="ID" unsaved-value="11"> <!-- 指定主鍵的生成方式,native是使用數據庫本地的方式 --> <generator class="native"></generator> </id> <property name="name" type="string" column="NAME"></property> <property name="birthday" type="timestamp" column="BIRTHDAY"></property> </class> </hibernate-mapping>
將id節點的設置了一個unsaved-value屬性,值是11。
@Test public void testSaveOrUpdate() { User user = new User(); user.setId(11); user.setName("Mark"); user.setBirthday(new Date()); // user的id爲null,執行insert操做。 session.saveOrUpdate(user); }
這裏設置了user的id等於11,與unsaved-value中的值同樣。執行saveorupdate會發送一條insert語句。
可是數據庫中的id值不是11,而是自增的id數值。
以上就是saveorupdate的基本應用。還剩下兩個方法,一個是delete,一個是evict。先測試一下delete方法:
@Test public void testSaveOrUpdate() { User user = new User(); user.setId(6); session.delete(user); // 調用delete後,對象的狀態變爲刪除狀態。 // 在事務commit以前,打印這個對象看看。 System.out.println(user); /** * output: User [id=6, name=null, birthday=null] * */ // 能夠看到在commit以前,user對象還有id,那麼在commit以前調用save或者update就會出現問題。 // 建議不要刪除狀態的對象。 }
調用delete方法會把傳入方法中的對象的狀態改變成刪除狀態,可是在commit以前,這個對象還能夠進行操做。建議不要使用刪除狀態的對象。
hibernate提供了hibernate.use_identifier_rollback屬性,在hibernate.cfg.xml中設置,將屬性的值等於true。在調用delete方法時,hibernate會自動將刪除的對象的id置空。
<!-- 刪除對象時,將對象的id設置成null --> <property name="hibernate.use_identifier_rollback">true</property>
@Test public void testSaveOrUpdate() { User user = new User(); user.setId(5); session.delete(user); System.out.println(user); /** * output:User [id=0, name=null, birthday=null] * */ }
設置了hibernate.use_identifier_rollback=true後,在刪除對象能夠看到輸出的結果,對象的id被設置成了0。
還有就是,若是刪除的對象id在數據庫中不存在,將會拋出異常。
最後一個方法是evict,這個方法用來送session緩存中移除一個託管對象。也就是說將對象從持久化狀態編程遊離狀態。
@Test public void testSaveOrUpdate() { // 持久化對象。 User user = (User) session.get(User.class, 3); System.out.println(user); // 調用evict方法變成遊離對象。 session.evict(user); }
還有一點須要注意的,同一個id的對象在session的緩存中只能有一個。
@Test public void testSaveOrUpdate() { // 持久化對象。 User user = (User) session.get(User.class, 3); System.out.println(user); // 調用evict方法把id爲3的user變成遊離對象。 // 這是session緩存中已經沒有對象了。 session.evict(user); // 把一個新的id爲3的user對象放到緩存中。 User user1 = (User) session.get(User.class, 3); // 在嘗試把舊的id爲3的user對象放到緩存中,會拋出異常。 // 此時session的緩存中已經有一個id爲3的user對象了 // org.hibernate.NonUniqueObjectException: // A different object with the same identifier value was already associated with the session : // [cn.net.bysoft.model.User#3] session.update(user); }
以上就是session的經常使用方法。