原創地址:http://www.cnblogs.com/binyulan/p/5628579.html
Session是java應用程序和hibernate框架之間的一個主要接口。它是從持久化服務中剝離出來的一個非常重要的API接口。
Session的主要功能是為映射的實體類的實例提供增刪改查操作(User.class 為被映射的實體類,new User()即為實例)。這些實例可能是以下三種狀態之一:
1) transient: 從沒有被持久化,不在Session緩存中
2) persistent: 在Session的緩存中。
3) detached: 曾經是persistent狀態,現在不在Session緩存中。
transient 實例通過調用save(), persist() or saveOrUpdate()可以變成persistent實例。persistent實例通過調用delete可以變成transient實例。任何通過get()或者load()方法獲取的實例都是persistent實例。detached實例通過調用update(), saveOrUpdate(), lock() or replicate()可以變成persistent實例。transient或者detached實例通過調用merge()方法可以生成一個新的persistent實例,但是並不是他們自己,這個persistent實例是merge方法自己創建的。
save() 和 persist() 生成SQL insert。delete() 生成SQL delete。update() 和 merge()生成SQL update。對persistent實例的修改在flush時會生成SQL update。saveOrUpdate() 和 replicate()生成insert或者update。
Session接口的實現類並沒有專門設計為線程安全的。相反,每個線程或者事務都應該通過SessionFactory獲取自己的實例。
一個典型的事務應該使用如下的形式:
Session sess = factory.openSession(); Transaction tx; try { tx = sess.beginTransaction(); //do some work ... tx.commit(); } catch (Exception e) { if (tx!=null) tx.rollback(); throw e; } finally { sess.close(); }
如果Session拋出異常,事務必須回滾。同時Session被棄用,因為Session內部狀態和數據庫可能會不一致。
1 保存對象到數據庫 session.save(Object user); //參數user為transient實例,返回值為新生成的主鍵id
持久化transient實例。返回值為生成的主鍵id。
按照第一節的配置,主鍵的生成策略為native(數據庫自動生成主鍵),由於數據庫使用的是mysql 5,所以是自增的主鍵生成方式。保存對象時並不需要設置id屬性。
@Test public void testSave() { try { User user = new User(); user.setBirthday(new java.sql.Date(0)); user.setName("binyulan"); session.save(user); } catch (HibernateException e) { if (transaction != null) { transaction.rollback(); } throw e; } }
2 保存對象到數據庫 session.persist(Object user); //參數user為transient實例
持久化transient實例。這個方法和session.save()方法一樣,都是保存對象到數據庫。但是不能設置id屬性,否則拋出異常。
@Test public void testPersist() { try { User user = new User(); user.setBirthday(new java.sql.Date(0)); user.setName("binyulan"); user.setId(13); session.persist(user); } catch (HibernateException e) { e.printStackTrace(); if (transaction != null) { transaction.rollback(); } throw e; } }
執行testPersist后拋出異常,如下:
org.hibernate.PersistentObjectException: detached entity passed to persist: com.binyulan.domain.User
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:139)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:75)
user創建出來為transient實例,但是異常顯示是detached實例。所以我認為只要給對象設置了id屬性,即使沒有持久化過,也可以看做detached實例。
persist()方法的參數為transient實例,即不帶主鍵id的實例。save()方法參數也為transient實例,但是也可以給此實例設置id屬性,即detached實例,
只不過此id屬性不起作用(native生成方式下)。結論如下:
save()和persist()方法參數雖然都說明為transient實例,但是也可以傳detached實例,也可以傳persistent實例。但是沒有理由傳非transient實例,因為這些方法都是設計為傳transient實例的,雖然傳其他實例也可以,但是不推薦,誰知道100年后hibernate90.10.9會不會改變save()或persist()方法傳入非transient實例的行為呢。
3 合並對象到數據庫 session.merge(Object user); //參數user為detached實例,其屬性將被copy到persistent實例。並且返回此persistent實例
拷貝user到持久化對象。如果沒有persistent對象在session緩存中,則加載數據到persistent對象並且返回此persistent對象。
如果user在數據庫中沒有對應記錄,則拷貝user到一個副本作為persistent對象並且返回這個persistent對象。user不會被保存到session緩存中
1) 保存未設置id屬性的User對象,執行insert把User對象保存到數據庫中,同時返回保存后的User對象。
@Test public void testMerge() { try { User user = new User(); user.setBirthday(new java.sql.Date(0)); user.setName("binyulan2"); User user1 = (User) session.merge(user); //把user屬性拷貝到新建的user1對象,執行insert插入數據庫,並且返回新建的user1對象,user1包含id,在session緩存中 System.out.println(user1 == user); //返回false。user為新建的對象,並沒有放入session緩存。user1為新建的持久化對象,並且在session的緩存中。 } catch (HibernateException e) { e.printStackTrace(); if (transaction != null) { transaction.rollback(); } throw e; } }
2) 保存設置id屬性的User對象,首先執行select查詢數據庫。若數據庫中存在此id的記錄,並且與數據庫中記錄不一致,則執行update操作,
若不存在此記錄,否則執行insert操作。
@Test public void testMerge1() { try { User user = new User(); user.setBirthday(new java.sql.Date(0)); user.setName("binyulan123"); user.setId(20); /** * 把user屬性拷貝到新建的User對象,並且執行select查詢數據庫。 * 假設數據庫中存在id為20的記錄,且user對象與數據庫中記錄不一致,執行update */ User user1 = (User) session.merge(user); System.out.println(user1 == user); //返回false。user為參數,不在session緩存中,user1為新建的持久化對象,並且在session的緩存中。 /** * 把user屬性拷貝到新建的User對象,並且執行select查詢數據庫。 * 假設數據庫中不存在id為123456789的記錄,執行insert */ user.setId(123456789L); User user2 = (User) session.merge(user); System.out.println(user2 == user); //返回false。user為參數,不再session緩存中,user2為新建的持久化對象,並且在session的緩存中。 } catch (HibernateException e) { e.printStackTrace(); if (transaction != null) { transaction.rollback(); } throw e; } }
4 數據更新,session.update(Object user);// 參數為一個detached實例。
使用user更新數據庫中記錄,並且把user對象變為persistent實例。
@Test public void testUpdate() { User user = new User(); user.setName(null); user.setBirthday(null); user.setId(20); session.update(user); }
5 更新或者保存,session.saveOrUpdate(Object user);
@Test public void testSaveOrUpdate() { User user = new User(); user.setName(null); user.setBirthday(null); session.saveOrUpdate(user); //insert操作 User user1 = new User(); user1.setName(null); user1.setBirthday(null); user1.setId(9); session.saveOrUpdate(user1);//update操作 }
6 刪除操作,session.delete(Object user);
從session緩存中刪除一個persistent實例。參數為一個Session緩存中的實例或者一個帶id的transient實例
@Test public void testDelete() { User user = new User(); user.setId(9); session.delete(user); }
7 查詢操作,session.get(Class clazz, Serializable id);
根據給定的Class對象和主鍵id返回persistent實例,如果數據庫中沒有此記錄,則返回null。
@Test public void testGet() { User user = (User) session.get(User.class, 1L); System.out.println(user); }
8 查詢操作,session.load(Class theClass, Serializable id);
返回一個代理對象,並在訪問非主鍵id屬性時會初始化這個代理對象。不能使用這個方法判斷是否一個實例存在。即使數據庫中沒有相應記錄,也會返回代理對象,但是在使用此代理對象是就會報錯。
@Test public void testLoad() { User user = (User) session.load(User.class, 1L); //返回的user為代理對象 user.getName(); //訪問name屬性時向數據庫發送SQL select System.out.println(user.getClass()); //代理對象類 }
9 復制操作,replicate(Object object, ReplicationMode replicationMode);
@Test public void testReplicate() { User user = new User(); user.setId(48);// 假定數據庫中存在id為48的記錄 user.setBirthday(new java.sql.Date(0)); user.setName("binyulan1"); /** * 可以選擇復制的模式,OVERWRITE模式為如果存在此記錄,則覆蓋 * 執行SQL select */ session.replicate(user, ReplicationMode.OVERWRITE); session.flush();// 執行SQL update User user1 = new User(); user1.setId(48); user1.setBirthday(new java.sql.Date(0)); user1.setName("binyulan2"); /** * 可以選擇復制的模式,IGNORE模式為如果存在此記錄,則不進行更新 * 執行SQL select */ session.replicate(user1, ReplicationMode.IGNORE); session.flush();// 記錄存在,所以忽略掉,不會執行SQL update User user3 = new User(); user3.setId(48); user3.setBirthday(new java.sql.Date(0)); user3.setName("binyulan3"); /** * 可以選擇復制的模式,EXCEPTION模式為如果存在此記錄,則拋異常 * 此語句執行SQL insert,ReplicationMode.EXCEPTION 始終都會執行SQL insert。 * 使用數據庫自身約束拋出異常,因為使用的是native主鍵生成方式,所以此處不拋異常,會插入成功 */ session.replicate(user3, ReplicationMode.EXCEPTION); }
10 強制同步數據到數據庫,session.flush();
可以設置FlushMode, 如果設置為NERVER,且不手動執行session.flush(), 提交事務也不會執行SQL update。
System.out.println(session.getFlushMode()); //默認模式為AUTO
@Test public void testFlush() { User user = new User(); user.setId(1); //假設id為1的記錄存在 user.setBirthday(new java.sql.Date(0)); user.setName("binyulan3"); session.update(user); session.flush(); //強制執行SQL update }
11 同步數據庫中數據到java對象,session.refresh(Object user); //參數為persistent實例或者detached實例
重新讀取數據庫記錄到user,不建議在跨多個業務長時間運行的session中使用。在以下特定場景中使用
1) 觸發器出發了一個insert或者update
2) 執行一大堆sql后
3) 插入一個Blob或Clob后
@Test public void testRefresh() { User user = (User) session.get(User.class, 1L); user.setName("original"); session.refresh(user); //重新同步數據庫中記錄到user,此時user的name不是original System.out.println(user.getName()); // 輸出binyulan }
請勿轉載,謝謝合作
