接上篇文章繼續學習Hibernate的Session(http://www.cnblogs.com/dreamfree/p/4111777.html)
持久化對象的狀態;
站在持久化的角度,Hibernate把對象分為4種狀態:持久化狀態、臨時狀態、游離狀態、刪除狀態。
Session的特定方法能使對象從一個狀態轉換到另一個狀態。
臨時對象(Transient):
在使用代理主鍵的情況下,OID通常為null
不處於Session的緩存中
在數據庫中沒有對應的記錄
持久化對象(也叫“托管”)(Persist)
OID為null
位於Session緩存中
若在數據庫中已經有和其對應的記錄,持久化對象和數據庫中的相關記錄對應
Session在flush緩存時,會根據持久化對象的屬性變化,來同步更新數據庫
在同一個Session實例的緩存中,數據庫表中的每條記錄只對應唯一的持久化對象
刪除對象(Removed):
在數據庫中沒有和其OID對應的記錄
不再處於Session緩存中
一般情況下,應用程序不該再使用被刪除的對象
游離對象(也叫"脫管")(Detached):
OID不為null
不再處於Session緩存中
一般情況下,游離對象是由持久化對象轉變過來的,因此在數據庫中可能還存在與它對應的記錄
下面的圖片說明了Hibernate中對象狀態的轉換:
Session的save方法
Session的save()方法使一個臨時對象轉變為持久化對象
Session的save()方法完成以下操作:
把News對象加入到Session緩存中,使它進入持久化狀態
選用映射文件指定的標識符生成器,為持久化對象分配唯一的OID,在使用代理主鍵的情況下,setId()方法為News對象設置OID是無效的
計划執行一條insert語句:在flush緩存的時候
Hibernate通過持久化對象的OID來維持它和數據庫相關記錄的對應關系。當News對象處於持久化狀態時,不允許程序隨意修改它的ID
persist()和save()的區別:
當對一個OID不為null的對象執行save()方法時,會把該對象以一個新的OID保存到數據庫中;但執行persist()方法時會拋出一個異常
Session的get()和load()方法
都可以根據給定的OID從數據庫加載一個持久化對象
區別:
當數據庫中不存在與OID對應的記錄時,load()方法拋出ObjectNotFoundException異常,而get()方法返回null
兩者采用不同的延遲檢索策略:load()方法支持延遲加載策略。而get不支持
Session的update()方法
Session的update()方法使一個游離對象轉變為持久化對象,並且計划執行一條update語句
若希望Session僅當修改了News對象的屬性時,才執行update()語句,可以把映射文件中<class>元素的select-before-update設為true。該屬性的默認值為false
當update()方法關聯一個游離對象時,如果在Session的緩存中已經存在相同的OID的持久化對象,會拋出異常
當update()方法關聯一個游離對象時,如果數據庫中不存在相應的記錄,也會拋出異常
Session的saveOrUpdate()方法
Session的saveOrUpdate()方法同時包含了save()與update()方法的功能
判定對象為臨時對象的標准
Java對象的OID為null
映射文件中的<id>設置了unsaved-value屬性,並且Java對象的OID取值與這個unsaved-value屬性值匹配
Session的merge()方法
Session的delete()方法
Session的delete()方法既可以刪除一個游離對象,也可以刪除一個持久化對象
Session的delete()方法處理過程
計划執行一條delete語句
把對象從Session的緩存中刪除,該對象進入刪除狀態
Hibernate的cfg.xml配置文件中有一個hibernate.use_identifier_rollback屬性,其默認值為false,若把它設為true,將改變delete()方法的運行行為:delete()方法會把持久化對象或游離對象的OID設置為null,使它們變為臨時對象
通過Hibernate調用存儲過程
work接口:直接通過JDBC API來訪問數據庫的操作
Session的doWork(Work)方法用於執行Work對象指定的操作,即調用Work對象的execute()方法。Session會把當前使用的數據庫連接傳遞給execute()方法
Hibernate與觸發器協同工作
Hibernate與數據庫中的觸發器協同工作時,會造成兩類問題
觸發器使Session的緩存中的持久化對象與數據庫中對應的數據不一致;觸發器運行在數據庫中,它執行的操作對Session是透明的
session的update()方法盲目地激發觸發器;無論游離對象的屬性是否發生變化,都會執行update語句,而update語句會激發數據庫中相應的觸發器
解決方案:
在執行完Session的相關操作后,立即調用Session的flush()方法和refresh()方法,迫使Session的緩存與數據庫同步(refresh()方法重新從數據庫加載對象)
在映射文件的<class>元素中設置select-before-update屬性;當Session的update或saveOrUpdate()方法更新一個游離對象時,會先執行select語句,獲得當前游離對象在數據庫中的最新數據。只有在一致的情況下才會執行update語句
測試類:
1 package com.yl.hibernate.entities; 2 3 import static org.junit.Assert.*; 4 5 import java.sql.CallableStatement; 6 import java.sql.Connection; 7 import java.sql.SQLException; 8 import java.util.Date; 9 10 import org.hibernate.Session; 11 import org.hibernate.SessionFactory; 12 import org.hibernate.Transaction; 13 import org.hibernate.cfg.Configuration; 14 import org.hibernate.jdbc.Work; 15 import org.hibernate.service.ServiceRegistry; 16 import org.hibernate.service.ServiceRegistryBuilder; 17 import org.junit.After; 18 import org.junit.Before; 19 import org.junit.Test; 20 21 public class HibernateTest { 22 23 private SessionFactory sessionFactory; 24 private Session session; 25 private Transaction transaction; 26 27 @Before 28 public void init() { 29 Configuration configuration = new Configuration().configure(); 30 ServiceRegistry serviceRegistry = 31 new ServiceRegistryBuilder().applySettings(configuration.getProperties()) 32 .buildServiceRegistry(); 33 34 sessionFactory = configuration.buildSessionFactory(serviceRegistry); 35 36 session = sessionFactory.openSession(); 37 38 transaction = session.beginTransaction(); 39 } 40 @After 41 public void destory() { 42 transaction.commit(); 43 44 session.close(); 45 46 sessionFactory.close(); 47 } 48 49 @Test 50 public void testSessionCache() { 51 News news = (News)session.get(News.class, 21); 52 System.out.println(news); 53 session.flush(); 54 News news2 = (News)session.get(News.class, 21); 55 System.out.println(news2); 56 57 System.out.println(news == news2);//true 58 } 59 60 /** 61 * flush:使數據庫表中的記錄和Session緩存中的對象的狀態保持一致。為了保持一致,則可能會發送對應的SQL語句。 62 * 1.在Transaction的commit()方法中:先調用session的flush方法,再提交事務 63 * 2.flush()方法可能會發送SQL語句,但不會提交事務 64 * 3.注意:在為提交事務或顯示調用session.flush()方法 之前,也有可能會flush()操作。 65 * 1).執行HQL或QBC查詢,會先進行flush操作,以得到數據表的最新紀錄 66 * 2).若記錄的ID是由底層數據庫使用自增的方式生成的,則在調用save方法時,就會立即發送INSERT語句 67 * 因為save方法后,必須保證對象的ID是存在的! 68 */ 69 @Test 70 public void testFlush2() { 71 News news = new News("Hibernate", "oracle", new Date()); 72 session.save(news); 73 } 74 75 @Test 76 public void testFlush() { 77 /*News news = (News)session.get(News.class, 21); 78 news.setAuthor("Bruce Eckel"); 79 80 News news2 = (News)session.createCriteria(News.class).uniqueResult(); 81 System.out.println(news2);*/ 82 News news = (News)session.get(News.class, 22); 83 session.flush(); 84 System.out.println(news); 85 } 86 87 /** 88 * refresh():會強制發送SELECT 語句,以使Session緩存中對象的狀態和數據表中對應的記錄保持一致! 89 */ 90 @Test 91 public void testRefresh() { 92 News news = (News) session.get(News.class, 22); 93 System.out.println(news); 94 session.refresh(news); 95 System.out.println(news); 96 } 97 98 /** 99 * clear():清理緩存 100 */ 101 @Test 102 public void testClear() { 103 News news1 = (News) session.get(News.class, 21); 104 session.clear(); 105 News news2 = (News) session.get(News.class, 21); 106 } 107 108 /** 109 * save()方法: 110 * 1.使一個臨時對象變為持久化對象 111 * 2.為對象分配ID 112 * 3.在flush緩存時會發送一條insert語句 113 * 4.在save方法之前的id是無效的 114 * 5.持久化對象的ID是不能被修改的! 115 */ 116 @Test 117 public void testSave() { 118 News news = new News(); 119 news.setTitle("AA"); 120 news.setAuthor("aa"); 121 news.setNewsDate(new Date()); 122 //news.setId(100);//不起效果 123 System.out.println(news); 124 //news.setId(101);//拋出異常 125 session.save(news); 126 System.out.println(news); 127 } 128 /** 129 * persist():也會執行insert操作 130 * 和save()的區別: 131 * 在調用persist方法之前,若對象已經有ID了,則不會執行insert,而拋出異常 132 */ 133 @Test 134 public void testPsesist() { 135 News news = new News(); 136 news.setTitle("DD"); 137 news.setAuthor("dd"); 138 news.setNewsDate(new Date()); 139 //news.setId(200);//拋出異常 140 System.out.println(news); 141 session.save(news); 142 System.out.println(news); 143 } 144 145 /** 146 * 147 */ 148 @Test 149 public void testGet() { 150 News news = (News) session.get(News.class, 21); 151 System.out.println(news); 152 } 153 154 /** 155 * get 和 load 156 * 157 * 1.執行get方法,會立即加載對象。 158 * 而執行load方法,若不使用該對象,則不會立即執行查詢操作,而返回一個代理對象 159 * 160 * get是立即檢索,load是延遲檢索 161 * 162 * 2.load方法可能會拋出LazyInitializationException(懶加載異常):在需要初始化代理對象之前已經關閉了session 163 * 164 * 3.若數據表中沒有對應的記錄,且session也沒有被關閉。同時需要使用對象時 165 * get 返回null 166 * load 若不使用該對象的任何屬性,則不會出問題;若要初始化了,拋出異常 167 * 168 * 169 */ 170 @Test 171 public void testLoad() { 172 News news = (News) session.load(News.class, 21); 173 System.out.println(news); 174 } 175 176 /** 177 * update: 178 * 1.若更新一個持久化對象,不需要顯式的調用update對象。因為在調用Transaction的commit方法時,會先執行session的flush方法。 179 * 2.更新一個游離對象,需要顯式的調用session的update方法。可以把一個游離對象變為持久化對象 180 * 181 * 需要注意的: 182 * 1.無論要更新的游離對象和數據表中的記錄是否一致,都會發送update語句。 183 * 如何能讓update方法不再盲目的發送update語句,在.hbm.xml文件的class節點設置select-before-update的值為true,默認為flase。但通常不需要設置該屬性 184 * 185 * 2.若數據表中沒有對應的記錄,但還調用了update方法,會拋出異常 186 * 187 * 3.當update()方法關聯一個游離對象時,如果在Session的緩存中已經存在相同OID的持久化對象,會拋出異常。因為在Session緩存中不能有兩個OID相同的對象 188 */ 189 @Test 190 public void testUpdate() { 191 /*News news = (News) session.get(News.class, 22); 192 news.setAuthor("Sun");//此時news的author字段已經更新到數據庫中,因為在執行commit語句時會先執行flush,flush將session緩存中的修改同步到數據庫中。 193 */ 194 195 News news = (News) session.get(News.class, 22); 196 transaction.commit(); 197 session.close(); 198 199 session = sessionFactory.openSession(); 200 transaction = session.beginTransaction(); 201 202 203 //news.setAuthor("Sun"); 204 session.update(news); 205 206 } 207 208 /** 209 * 注意: 210 * 1.若OID不為空,但數據表還沒有和其對應的記錄。會拋出一個異常。 211 * 2.了解:OID值等於id的unsaved-value屬性值的對象,也被認為是一個游離對象 212 */ 213 @Test 214 public void testSaveOrUpdate() { 215 News news = new News("FF", "ff", new Date()); 216 news.setId(41); 217 session.saveOrUpdate(news); 218 219 } 220 221 /** 222 * delete: 執行刪除操作,只要OID和數據表中一條記錄對應,就會准備執行delete操作 223 * 若OID在數據表中沒有對應的記錄,則拋出異常 224 * 225 * 可以通過設置一個Hibernate配置文件hibernate.use_identifier_rollback 為true,使刪除對象后,把其OID置為null 226 */ 227 @Test 228 public void testDelete() { 229 /*//游離對象刪除 230 News news = new News(); 231 news.setId(42); 232 233 session.delete(news);*/ 234 235 //持久化對象刪除 236 News news = (News) session.get(News.class, 41); 237 session.delete(news);//計划刪除,並不立即執行。在commit中的flush時執行delete 238 System.out.println(news);//依然可以打印 239 } 240 241 /** 242 * evict:從session緩存中把指定的持久化對象移除 243 */ 244 @Test 245 public void testEvict() { 246 News news1 = (News) session.get(News.class, 22); 247 News news2 = (News) session.get(News.class, 41); 248 249 news1.setTitle("AA"); 250 news2.setTitle("BB"); 251 252 session.evict(news1); 253 } 254 255 /** 256 * 調用存儲過程 257 */ 258 @Test 259 public void testDoWork() { 260 session.doWork(new Work() { 261 262 @Override 263 public void execute(Connection connection) throws SQLException { 264 System.out.println(connection); 265 //調用存儲過程 266 267 //創建存儲過程的對象 268 CallableStatement c = connection.prepareCall("{call getsum(?,?)}"); 269 270 //給存儲過程的第一個參數設置值 271 c.setInt(1,100); 272 273 //注冊存儲過程的第二個參數 274 c.registerOutParameter(2,java.sql.Types.INTEGER); 275 276 //執行存儲過程 277 c.execute(); 278 279 connection.close(); 280 } 281 }); 282 } 283 }