持久化對象有以下幾種狀態:
臨時對象(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緩存的監視下了