1. 程式人生 > >Hibernate(八)級聯儲存或更新(含問題在末尾,求大佬指點..)

Hibernate(八)級聯儲存或更新(含問題在末尾,求大佬指點..)

級聯儲存或更新CASCADE

級聯儲存或更新:

  作用就是:儲存一方的資料的時候,會把關聯的物件也同時儲存。

級聯儲存或更新的配置:

    屬性名:cascade

        屬性值:

            1.none:所有情況下均不進行關聯操作。(預設值)

            2.save-update:在執行save/update/saveOrUpdate時進行關聯操作

            3.delete:在執行delete時進行關聯操作

            4.all-delete-orphan:當一個節點在物件圖中成為孤兒節點時,刪除該節點

            5.all:所有情況下均進行關聯操作,即save-update和delete(但是不推薦,避免刪除後級聯刪除最後導致莫名其妙少了很多資料)

先貼上一段程式碼:

    這段程式碼是上一章的程式碼,作用是建立新使用者並且建立兩個訂單新增到該客戶中。

    新建立的物件都是瞬時狀態,我們儲存到資料庫需要將物件進行持久化操作。

    這裡發現,最後持久化物件用了三行程式碼。

     session.save(cst); session.save(od); session.save(od1);

    又儲存客戶,又儲存兩個訂單,感覺程式碼有點多。但是不執行後兩個行程式碼又不行,瞬時狀態的怎麼可以!

    所以我們就得設定級聯操作了,這樣就可以儲存某一方的同時,自動儲存或更新級聯的物件。

/**
     * 建立新使用者並且建立兩個訂單新增到該客戶中
     
*/ @Test public void fun1(){ Session session = HibernateUtils.getSession(); session.getTransaction().begin(); try { //新建使用者 瞬時狀態 Customer cst = new Customer(); cst.setCust_id(1); cst.setCust_name("臺用勝"); cst.setCust_gender(
"男"); cst.setCust_age(24); cst.setCust_phone("18736549531"); //新建訂單 瞬時狀態 Order od = new Order(); od.setOrder_id(UUID.randomUUID().toString()); od.setDetail_id(UUID.randomUUID().toString()); Order od1 = new Order(); od1.setOrder_id(UUID.randomUUID().toString()); od1.setDetail_id(UUID.randomUUID().toString()); //表達客戶和訂單的關係 cst.getOds().add(od); cst.getOds().add(od1); //表達訂單和客戶的關係 od.setCst(cst); od1.setCst(cst); //持久化使用者和訂單 session.save(cst); session.save(od); session.save(od1); } catch (Exception e) { session.getTransaction().rollback(); e.printStackTrace(); } session.getTransaction().commit(); }

級聯儲存或更新的操作:

  這裡有客戶對訂單的關係,上述中,給客戶新增訂單需要三行程式碼。

  這三行程式碼中, 如果我們認為客戶方是“主控方”那麼後面的兩行程式碼我們就可以省略,如果我們認為訂單方是“主控方”那麼第一行的程式碼就可以省略。具體看程式碼

  那我就舉一個例子,選擇客戶方是“主控方”,

  1.配置Customer.hbm.xml(在配置的時候新增cascade="save-update")

<?xml version = "1.0" encoding = "UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="deep.domain">
    <class name="Customer" table="customera">
        <id name="cust_id" column = "cust_id">
            <generator class="native"></generator>
        </id>
        
        <property name="cust_name" column = "cust_name"></property>
        <property name="cust_gender" column = "cust_gender"></property>
        <property name="cust_age" column = "cust_age"></property>
        <property name="cust_phone" column = "cust_phone"></property>
        
        <!-- 配置一對多屬性 -->
        <set name="ods" cascade="save-update">
            <key column="cust_order_id" ></key>
            <one-to-many class="Order"/>
        </set>
        
    </class>
    
</hibernate-mapping>

2.隨後將上面貼上的java程式碼進行修改(我們會發現,少了session.save(od)和session.save(od1)),但是效果是一樣的

/**
     * 級聯儲存或更新
     * 建立新使用者並且建立兩個訂單新增到該客戶中
     */
    @Test
    public void fun2(){
        Session session = HibernateUtils.getSession();
        session.getTransaction().begin();
    
        try {
            //新建使用者  瞬時狀態
            Customer cst = new Customer();
            cst.setCust_id(1);
            cst.setCust_name("臺用勝");
            cst.setCust_gender("男");
            cst.setCust_age(24);
            cst.setCust_phone("18736549531");
            
            //新建訂單 瞬時狀態
            Order od = new Order();
            od.setDetail_id(UUID.randomUUID().toString());
            
            Order od1 = new Order();
            od1.setDetail_id(UUID.randomUUID().toString());
            
            //表達客戶和訂單的關係
            cst.getOds().add(od);
            cst.getOds().add(od1);
            //表達訂單和客戶的關係
            od.setCst(cst);
            od1.setCst(cst);
            
            //持久化使用者和訂單
            session.save(cst);
        } catch (Exception e) {
            session.getTransaction().rollback();
            e.printStackTrace();
        }
        
        session.getTransaction().commit();
    }

同樣,如果我們認為訂單方是“主控方”,那麼就只要這樣配置

<!-- 配置多對一屬性 -->
        <many-to-one name="cst" class="Customer" column="cust_order_id" cascade="save-update" />

隨後的java程式碼是:(獲取主鍵值為 6的客戶,並新建兩個訂單新增到該客戶中)

/**
     * 級聯儲存或更新
     * 獲取使用者並且建立兩個訂單新增到該客戶中
     */
    @Test
    public void fun3(){
        Session session = HibernateUtils.getSession();
        session.getTransaction().begin();
    
        try {
            //獲取主鍵值為6的客戶  瞬時狀態
            Customer cst = session.get(Customer.class, 6);
            
            //新建訂單 瞬時狀態
            Order od = new Order();
            od.setDetail_id(UUID.randomUUID().toString());
            
            Order od1 = new Order();
            od1.setDetail_id(UUID.randomUUID().toString());
            
            //表達客戶和訂單的關係
            cst.getOds().add(od);
            cst.getOds().add(od1);
            //表達訂單和客戶的關係
            od.setCst(cst);
            od1.setCst(cst);
            
            //持久化使用者和訂單
            session.save(od);
            session.save(od1);
        } catch (Exception e) {
            session.getTransaction().rollback();
            e.printStackTrace();
        }
        
        session.getTransaction().commit();
    }

 

剛剛上面的是兩種單一方向,也可以認為訂單方和客戶方都是“主控方”,方法自然很簡單,就是在兩邊都加上cascade="save-update"啦~

到這裡cascade概念基本可以了,我也算是有點理解了吧,這樣確實方便很多,少了程式碼量,也比較人性化。

 

問題:我的訂單表中的主鍵這是的是uuid,在配置檔案中設定了主鍵自增策略是uuid,在建立新訂單的時候,我有一個訂單.setId的動作,並且手動生成了uuid。 在我沒有級聯操作的時候,能夠正常新增訂單,但是級聯操作後就會報錯,java程式碼和報錯程式碼如下(我認為客戶方是“主控方”)

錯誤明細:

 十一月 18, 2018 4:31:07 下午 org.hibernate.internal.SessionImpl$5 mapManagedFlushFailure
ERROR: HHH000346: Error during managed flush [Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1]

百度查了也沒搞懂是怎麼回事,想了有一會兒,然後報錯,看到錯誤來源sql語句update的第一個引數,我就看到了我java中,然後猜測是主鍵策略方面的問題,我就嘗試把那一行程式碼刪除,發現可以執行,但是不知道為什麼。如果有大佬知道希望能指點一二。。謝謝啦~