2011-10-01 93 views
0

我正在研究Hibernate的父/子關係。休眠父/子關係。爲什麼對象保存兩次?

我有3個實體EmployeeCustomersOrders

它們的關係是
- >ñOrders
- >ñOrders

我希望能夠保存/更新/刪除Customer對象和EmployeesOrders但我想使用一些接口,以便調用代碼不處理任何Hibernate或JPA的東西。

E.g.我試着像下面這樣:

class Utils{ 
    public static void saveObject(Object o){  
     logger.debug(o.toString()); 
     Session session = getSessionFactory().openSession();  
     Transaction tx = session.beginTransaction(); 
     session.save(o); 
     tx.commit(); 
     session.close(); 
    } 
} 

,並在調用代碼我做的:

Employee employee = new Employee(); 
//set data on employee 
Customer customer = new Customer(); 
    //set data on customer 
Order order = new Order(); 
//set data on order 
employee.addOrder(order);//this adds order to its list, order gets a reference of employee as parent 
customer.addOrder(order);//this adds order to its list, order gets a reference of customer as parent 
    Utils.saveObject(customer); 
    Utils.saveObject(employee); 

現在我發現這個代碼,2條記錄員工創建的1

代替

如果我只做:

Utils.saveObject(customer);

只有1個(正確)記錄被創建。

爲什麼會發生這種情況?
發生這種情況是因爲CustomerEmployee都保存了相同的Order對象嗎?而cascade="all"會造成這種副作用?

現在,如果我不使用DBUtils方法,直接做:

Session session = DBUtil.getSessionFactory().openSession();   
Transaction tx = session.beginTransaction(); 
session.save(employee); 
session.save(customer); 
tx.commit(); 
session.close(); 

再次,它工作正常。即僅創建1個員工記錄。

我在這裏做錯了什麼?


UPDATE:

休眠映射:

<hibernate-mapping> 
    <class name="database.entities.Associate" table="EMPLOYEE"> 
     <id name="assosiateId" type="java.lang.Long"> 
      <column name="EMPLOYEEID" /> 
      <generator class="identity" /> 
     </id> 
     <property name="firstName" type="java.lang.String" not-null="true"> 
      <column name="FIRSTNAME" /> 
     </property> 
     <property name="lastName" type="java.lang.String" not-null="true"> 
      <column name="LASTNAME" /> 
     </property> 
     <property name="userName" type="java.lang.String" not-null="true"> 
      <column name="USERNAME" /> 
     </property> 
     <property name="password" type="java.lang.String" not-null="true"> 
      <column name="PASSWORD" /> 
     </property> 
     <set name="orders" table="ORDERS" inverse="true" cascade="all" lazy="true"> 
      <key> 
       <column name="EMPLOYEEID" /> 
      </key> 
      <one-to-many class="database.entities.Order" /> 
     </set> 
    </class> 
</hibernate-mapping> 



<hibernate-mapping> 
    <class name="database.entities.Customer" table="CUSTOMER"> 
     <id name="customerId" type="java.lang.Long"> 
      <column name="CUSTOMERID" /> 
      <generator class="identity" /> 
     </id> 
     <property name="customerName" type="java.lang.String"> 
      <column name="CUSTOMERNAME" /> 
     </property> 
     <set name="orders" table="ORDERS" inverse="true" cascade="all" lazy="true"> 
      <key> 
       <column name="CUSTOMERID" /> 
      </key> 
      <one-to-many class="database.entities.Order" /> 
     </set> 
    </class> 
</hibernate-mapping> 


<hibernate-mapping> 
    <class name="database.entities.Order" table="ORDERS"> 
     <id name="orderId" type="java.lang.Long"> 
      <column name="ORDERID" /> 
      <generator class="identity" /> 
     </id> 
     <property name="orderDate" type="java.util.Date"> 
      <column name="ORDERDATE" /> 
     </property>   
     <property name="quantity" type="java.lang.Integer"> 
      <column name="QUANTITY" /> 
     </property> 
     <property name="quantityMargin" type="java.lang.Long"> 
      <column name="QUANTITYMARGIN" /> 
     </property> 
     <property name="port" type="java.lang.String"> 
      <column name="PORT" /> 
     </property> 
     <property name="orderState" type="java.lang.String"> 
      <column name="ORDERSTATE" /> 
     </property> 
     <many-to-one name="customer" class="database.entities.Customer" cascade="all" fetch="join"> 
      <column name="CUSTOMERID" /> 
     </many-to-one> 
     <many-to-one name="associate" column="EMPLOYEEID" class="database.entities.Employee" cascade="all" fetch="join"> 

     </many-to-one> 
    </class> 
</hibernate-mapping> 

UDATE 2:

class Employee{ 
//Various members 
    Set<Order> orders = new HashSet<Order>(); 
    public void addOrder(Order order){ 
    order.setEmployee(this); 
    orders.add(order);  
    } 
} 

另請參見:

class Customer{ 
//Various members 
    Set<Order> orders = new HashSet<Order>(); 
    public void addOrder(Order order){ 
    order.setCustomer(this); 
    orders.add(order);  
    } 
} 
+0

你能告訴你的Hibernate映射? – artgon

+0

我已經用映射更新了文章。 – Cratylus

+0

您的交易處理是完全錯誤的。交易不僅僅是存儲實體所需的東西。它應該是一個原子工作單位。必須保持一致的數據庫所有更新必須在唯一事務內完成。 –

回答

2

在我看來,在DBUtils代碼,你是在包裹外部事務既節約,而在utils的代碼,你在兩個完全獨立的交易讓他們沒有外一個。

根據您的級聯,Hibernate必須找出哪些對象需要被拯救。當您運行Utils代碼時,有兩個單獨的事務,它將首先保存訂單。既然你的級聯所有,這意味着它將保存訂單第一,那麼客戶。但是,你創建的相同訂單同時爲客戶員工。因此,員工也會保存在第一筆交易中(由於級聯)。第二筆交易,因爲它是分開的,所以不會意識到這一點,並節省另一筆僱員

在另一方面,如果你包起來外部事務,Hibernate可以找出所有對象的身份,並保存它們正確。

+0

好的。但是爲什麼客戶沒有按照你描述的原因再次保存兩次? – Cratylus

+0

因爲你告訴Hibernate在第一個事務中保存客戶,所以它從那裏開始下降。 – artgon

1

嘗試只保存訂單而不是分別保存員工和客戶。使用現有的cascade = all設置,兩個父對象都將保存而不會創建任何重複項。

+0

但是,爲什麼只有在使用'DBUtils.saveObject'時纔會發生這種情況? – Cratylus

+0

當您使用DBUtils.saveObject對象正在保存在兩個獨立的事務中。 – kunal