持久化對象有以下幾種狀態:

持久化對象有以下幾種狀態:

臨時對象(Transient): 在使用代理主鍵的情況下, 
             OID 通常爲 null 
            不處於 Session 的緩存中 在數據庫中沒有對應的記錄
持久化對象(也叫”託管”)(Persist): OID 不爲 null 
                    位於 Session 緩存中 
                  若在數據庫中已經有和其對應的記錄, 持久化對象和數據庫中的相關記錄對應 
                   Session 在 flush 緩存時, 會根據持久化對象的屬性變化, 來同步更新數據庫
                   在同一個 Session 實例的緩存中, 數據庫表中的每條記錄只對應唯一的持久化對象

刪除對象(Removed): 在數據庫中沒有和其 OID 對應的記錄 
            不再處於 Session 緩存中 一般情況下, 
             應用程序不該再使用被刪除的對象
遊離對象(也叫”脫管”) (Detached): OID 不爲 null 
                      不再處於 Session 緩存中 一般情況需下, 
                  遊離對象是由持久化對象轉變過來的, 因此在數據庫中可能還存在與它對應的記錄
持久化對象的狀態轉換:

對象狀態轉換的方法解析:

1. save() 方法:可以自動的檢測當前save的對象是否已經在數據庫中存在這個對象(其實檢測時只是在Session緩存中檢測,若Session緩存中沒有這個對象,則會自動的運行insert語句,若Session的緩存中有這個對象則執行update語句:驗證原理:

複製代碼
    @Test
    public void testUnsaveValue(){
        News news=(News)session.get(News.class, 12);
        session.clear();
    
        news.setAuthor("jeremy");

        news.setDate(new Date());
        session.save(news);
                
    }
複製代碼

從數據庫中獲取對象,然後把Session的緩存清理掉,這時對象就變爲遊離對象了,數據庫是有這個對象,但是Session緩存中沒有,所以Session會認爲你這對象是不存在的,所以就執行更新操作

)
  1). 使一個臨時對象變爲持久化對象
  2). 爲對象分配 ID.
  3). 在 flush 緩存時會發送一條 INSERT 語句.
  4). 在 save 方法之前的 id 是無效的
  5). 持久化對象的 ID 是不能被修改的!

複製代碼
@Test
public void testSave(){
News news = new News();
news.setTitle("CC");
news.setAuthor("cc");
news.setDate(new Date());
news.setId(100); 

System.out.println(news);

session.save(news);

System.out.println(news);
//     news.setId(101); 
}
複製代碼

persist(): 也會執行 INSERT 操作
  1). 使一個臨時對象變爲持久化對象
  2). 爲對象分配 ID.
和 save() 的區別 : 
在調用 persist 方法之前, 若對象已經有 id 了, 則不會執行 INSERT, 而拋出異常

複製代碼
@Test
public void testPersist(){
News news = new News();
news.setTitle("EE");
news.setAuthor("ee");
news.setDate(new Date());
news.setId(200); 

session.persist(news); 
}
複製代碼

get():從數據庫加載一個對象(立即加載)
load():從數據加載一個對象(延遲加載)--工作原理:load()會根據ID先生成一對象代理,等需要用到對象時才真正從數據加載對象,


* get VS load:
* 1. 執行 get 方法: 會立即加載對象. 
* 執行 load 方法, 若不使用該對象, 則不會立即執行查詢操作, 而返回一個代理對象

* get 是 立即檢索, load 是延遲檢索. 

* 2. load 方法可能會拋出 LazyInitializationException 異常: 在需要初始化代理對象之前已經關閉了 Session,這個異常就是因爲load的工作原理造成的,因爲的對象沒有被引用,所以load只是生成一個代理,並沒有加載對象,而此時關閉了Session,那代理對象怎麼初始化,所以只能拋出異常

* 3. 若數據表中沒有對應的記錄, Session 也沒有被關閉. 
* get 返回 null(比如你讓我辦事辦不成我就返回null)
* load 若不使用該對象的任何屬性, 沒問題; 若需要初始化了, 拋出異常. (因爲代理對象是生成了,但是不能初始化,那就要拋出一個異常了)(你讓我辦事我先答應了,但是真正辦的時候我才知道辦了不了,所以返回異常)

複製代碼
@Test
    public void testLoad(){
        
        News news = (News) session.load(News.class, 10);
        System.out.println(news.getClass().getName()); 
        
//        session.close();
//        System.out.println(news); 
    }
    
    @Test
    public void testGet(){
        News news = (News) session.get(News.class, 1);
//        session.close();
        System.out.println(news); 
    }
複製代碼

 update:
* 1. 若更新一個持久化對象, 不需要顯示的調用 update 方法. 因爲在調用 Transaction的 commit() 方法時, 會先執行 session 的 flush 方法.匹配到緩存和數據庫不同會  自動發送upata語句,並不用顯式調用
* 2. 更新一個遊離對象, 需要顯式的調用 session 的 update 方法. 可以把一個遊離對象變爲持久化對象
代碼分析:

複製代碼
@Test
    public void testUpdate(){
        News news = (News) session.get(News.class, 1);
        
        transaction.commit();
        session.close();
        
        session = sessionFactory.openSession();
        transaction = session.beginTransaction();
    
        news.setAuthor("SUN"); 

就這個代碼分析:
例如當我獲取了一個對象後,我提交了事務。關閉了Session,此時我再打開Session和開啓事務,然後再操作對象的屬性(news.setAuthor("SUN")),此時還會自動發送update語句嗎??不會,因爲Session已經被關閉了,也就是說前一個Session的緩存被清理了,對象已經不再緩存中了(也就是遊離對象),那你現在改變了對象的屬性,而對象又不在緩存中,那我Session的緩存是感知不到了吧,感知不到那我就不發送update語句,那我就不發送你修改的對象的屬性給數據庫吧,

爲了解決這問題那咋們就要手動的把對象添加到緩存裏去吧,緩存的對象的狀態和數據庫記錄不同時就會自動調用flush()方法吧,會自動發送update語句吧,所以此時數據庫的記錄會改變吧:
代碼:

@Test
    public void testUpdate(){
        News news = (News) session.get(News.class, 1);
        
        transaction.commit();
        session.close();
        
        session = sessionFactory.openSession();
        transaction = session.beginTransaction();
    
        news.setAuthor("SUN"); 

      Session.update();//就是在這裏手動的調用update語句,是遊離對象變爲持久化對象,需要注意的是遊離對象並不存在緩存中的,所以遊離對象的所有操作是不被感知的

 

複製代碼

需要注意的:
* 1. 無論要更新的遊離對象和數據表的記錄是否一致, 都會發送 UPDATE 語句. 
* 如何能讓 updat 方法不再盲目的出發 update 語句呢(因爲有時會盲目的觸發update觸發器,導致會出現很多錯誤) ? 在 .hbm.xml 文件的 class 節點設置
* select-before-update=true (默認爲 false). 但通常不需要設置該屬性. (因爲這樣會導致這個對象在每次更新操作都要先查詢,降低了效率)

* 2. 若數據表中沒有對應的記錄, 但還調用了 update 方法, 會拋出異常(因爲Session的緩存和數據庫的記錄要保持一致)

* 3. 當 update() 方法關聯一個遊離對象時, 
* 如果在 Session 的緩存中已經存在相同 OID 的持久化對象, 會拋出異常. 因爲在 Session 緩存中不能有兩個 OID 相同的對象!

Session 的 saveOrUpdate() 方法同時包含了 save() 與 update() 方法的功能:

判定對象爲臨時對象的標準:
           Java 對象的 OID 爲 null 
           映射文件中爲 <id> 設置了 unsaved-value 屬性, 並且 Java 對象的 OID 取值與這個 unsaved-value 屬性值匹配
注意:

* 1. 若 OID 不爲 null, 但數據表中還沒有和其對應的記錄. 會拋出一個異常. 
* 2. 瞭解: OID 值等於 id 的 unsaved-value 屬性值的對象, 也被認爲是一個遊離對象

Session 的 delete() 方法既可以刪除一個遊離對象, 也可以刪除一個持久化對象 若 OID 在數據表中沒有對應的記錄, 則拋出異常
Session 的 delete() 方法處理過程: 
                 1)先執行一條select語句,把數據表的記錄查詢出來,放在Session緩存中
                 2)計劃執行一條 delete 語句 把對象從 Session 緩存中刪除, 該對象進入刪除狀態. 

Hibernate 的 cfg.xml 配置文件中有一個 hibernate.use_identifier_rollback 屬性, 其默認值爲 false, 若把它設爲 true, 
將改變 delete() 方法的運行行爲: delete() 方法會把持久化對象或遊離對象的 OID 設置爲 null, 使它們變爲臨時對象

 

 

 evict: 從 session 緩存中把指定的持久化對象移除,對象就變爲遊離對象了,不會觸發相應的操作,因爲已經不在Session緩存的監視下了

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