1. 程式人生 > >Hibernate學習5—Hibernate操作對象2

Hibernate學習5—Hibernate操作對象2

之前 on() nbsp select語句 代理 teacher 內存 不存在 png

第二節:Session 常用方法講解                                         技術分享 2)load和get()方法: 數據庫中不存在與OID 對應的記錄,Load()方法會拋出異常: load方法默認采用延遲加載,load出來的對象是一個代理類。開始並沒有值,只有用到它的屬性等的時候,才會去發出sql語句。 而get方法一開始就發出sql語句。 如果說獲取一個對象是為了刪除它,可以用load,因為只要獲取個引用就行了。 如果說獲取一個對象是為了訪問它的屬性,建議用get; 技術分享
@Test
    public void testLoadClass() {
        Class c 
= (Class) session.load(Class.class, Long.valueOf(2)); //class id為2的不存在,拋出異常 System.out.println(c.getStudents()); } @Test public void testGetClass() { Class c = (Class) session.get(Class.class, Long.valueOf(2)); //class id為2的不存在,打印null System.out.println(c); }
View Code

2)update:

技術分享
@Test
    public void testUpdateClass(){
        Session session1=sessionFactory.openSession();
        session1.beginTransaction();
        Class c=(Class)session1.get(Class.class, Long.valueOf(1));
        session1.getTransaction().commit(); 
        session1.close();
        
        Session session2
=sessionFactory.openSession(); session2.beginTransaction(); c.setName("08計算機本科2"); session2.update(c); session2.getTransaction().commit(); session2.close(); }
View Code

補充:

update方法:
1.更新一個detached的對象;
2.更新一個transient的會報錯;但是更新自己設定id的transient對象可以(數據庫有對應記錄);
3.上面的,比如更新teacher,我們只是想更新name,但是它會把所有的屬性都更新一遍;這樣會造成效率低,比如有個字段特別長...
4.持久化的對象,只要改變了它的內容,session在提交或者關閉的時候,會檢查緩存中的和數據庫中的是否一致,如果不一致,自動的發update語句;
但是也和上面一樣,雖然只改了一個字段,也會更新所有的字段;
5.能不能做到:哪個字段改了才更新,哪個字段沒改,哪個字段就不更新?怎麽做:
a.xml配置:
<class name="com.cy.Teacher" dynamic-update="true">.....</class>
b.跨session更新的問題:
@Test
    public void testUpdate5() {
        
        
        Session session = sessionFactory.getCurrentSession();
        session.beginTransaction();
        Student s = (Student)session.get(Student.class, 1);
        s.setName("zhangsan5");
        session.getTransaction().commit();
        
        s.setName("z4");
        
        Session session2 = sessionFactory.getCurrentSession();
        session2.beginTransaction();
        session2.update(s);
        session2.getTransaction().commit();
    }
首先student對象被我們放到了緩存裏,s.setName("zhangsan3"),Hibernate會檢查哪些屬性改過了,這時候生成sql語句,由於使用了dynamic-update,它就只更新name這個字段了;
session提交之後,關閉了。緩存中的這個對象沒了。但是內存中Student s這個對象還在,是detached狀態的。
這個對象又setName("z4"),第二個session2來了,這個session2裏面沒有s這個對象,然後update(s),它有沒有地方來比較哪個字段改過了?
沒有,它沒法將內存中的s和session2緩存中的s來比較,所以update(s),發出的sql會更新全部的字段;

c.根據上面,如果想跨session,只更新改過的字段,怎麽做:
將上面的update改為merge:
@Test
    public void testUpdate6() {
        
        
        Session session = sessionFactory.getCurrentSession();
        session.beginTransaction();
        Student s = (Student)session.get(Student.class, 1);
        s.setName("zhangsan6");
        session.getTransaction().commit();
        
        s.setName("z4");
        
        Session session2 = sessionFactory.getCurrentSession();
        session2.beginTransaction();
        session2.merge(s);
        session2.getTransaction().commit();
    }
merge:把這個對象給我合並到數據庫;原來沒改的內容還需要合並嗎?不需要。
merge的時候,它怎麽檢查哪些字段改過哪些字段沒改過呢?緩存中又沒有,只能從數據庫中load一次,所以它在update之前先發出了一條select語句,然後再比較你給我的對象和我load的對象在什麽地方不同,再重新發update語句。

d:dynamic-update這種xml配置,對應的JPA Annotation沒有對應的屬性;
在真正開發中建議使用HQL:
session.createQuery("update Student s set s.name=‘z5‘ where s.id = 1");
@Test
    public void testUpdate7() {
        Session session = sessionFactory.getCurrentSession();
        session.beginTransaction();
        Query q = session.createQuery("update Student s set s.name=‘z5‘ where s.id = 1");
        q.executeUpdate();
        session.getTransaction().commit();
        
    }

3)saveOrUpdate:

saveOrUpdate(): 如果傳的是一個臨時對象,則執行save方法;如果傳的是遊離對象,就調用update方法; 技術分享
@Test
    public void testSaveOrUpdateClass(){
        Session session1=sessionFactory.openSession();
        session1.beginTransaction();
        Class c=(Class)session1.get(Class.class, Long.valueOf(1));
        session1.getTransaction().commit();
        session1.close();
        
        Session session2=sessionFactory.openSession();
        session2.beginTransaction();
        c.setName("08計算機本科3");
        
        Class c2=new Class();
        c2.setName("09計算機本科3");
        session2.saveOrUpdate(c);        //c是遊離狀態,執行update
        session2.saveOrUpdate(c2);        //c2臨時狀態,執行save
        session2.getTransaction().commit();
        session2.close();
        
        /**
         * 發出的sql:
         *     Hibernate: select class0_.classId as classId1_0_0_, class0_.className as classNam2_0_0_ from t_class class0_ where class0_.classId=?
            Hibernate: insert into t_class (className) values (?)
            Hibernate: update t_class set className=? where classId=?
         */
    }
View Code

4)merge:

有的時候update會報錯:session中有兩個對象,擁有相同的OID(比如OID為1),更新的時候,session發現緩存中你已經有一個OID為1的對象了,所以更新的時候就報錯了; 比如: 技術分享
@Test
    public void testUpdateClass2(){
        Session session1=sessionFactory.openSession();
        session1.beginTransaction();
        Class c=(Class)session1.get(Class.class, Long.valueOf(1));
        session1.getTransaction().commit();
        session1.close();
        
        Session session2=sessionFactory.openSession();
        session2.beginTransaction();
        Class c2=(Class)session2.get(Class.class, Long.valueOf(1));
        c.setName("08計算機本科3");
        
        session2.update(c);
        session2.getTransaction().commit();
        session2.close();
    }     
View Code

執行報錯:

技術分享

為了解決這個問題,多了個merge方法,合並對象: 更新的時候如果發現這個對象OID和session緩存中另一個對象OID重合了,調用merge方法就會合並,把這兩個對象的屬性合並,然後更新; 技術分享
@Test
    public void testMergeClass(){
        Session session1=sessionFactory.openSession();
        session1.beginTransaction();
        Class c=(Class)session1.get(Class.class, Long.valueOf(1));
        session1.getTransaction().commit(); 
        session1.close();
        
        Session session2=sessionFactory.openSession();
        session2.beginTransaction();
        
        Class c2=(Class)session2.get(Class.class, Long.valueOf(1));
        c.setName("08計算機本科4");
    
        session2.merge(c);

        session2.getTransaction().commit(); 
        session2.close();
    }
View Code

5)delete:

技術分享
@Test
    public void testDeleteStudent(){
        Student student=(Student)session.load(Student.class, Long.valueOf(1));
        session.delete(student);
        session.getTransaction().commit();    
        session.close();
    }
View Code

因為刪除的時候,只需要獲得它的引用,這裏使用了load延遲加載就行了。不需要使用get了,因為不需要獲取它裏面的屬性。

session.delete的時候還沒有真正刪除,提交事務的時候,才同步數據庫,真的刪了。

Hibernate學習5—Hibernate操作對象2