學習hibernate(三) -- session經常使用方法

    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的經常使用方法。

相關文章
相關標籤/搜索