Hibernate父/子关系。为什么对象保存两次?

时间:2011-10-01 17:28:22

标签: java database hibernate java-ee hibernate-mapping

我正在研究Hibernate的父/子关系。

我有3个实体Employee CustomersOrders

他们的关系是  Employee 1< - > N Orders
 Customer 1< - > N 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(客户);

仅创建1条(正确)记录。

为什么会这样? 是否会发生这种情况,因为OrderCustomer同时保存了相同的Employee对象? cascade="all"会产生这种副作用吗?

现在,如果我不使用DBUtils方法并直接执行:

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

同样,它按预期工作。即仅创建1个员工记录。

我在这里做错了什么?


更新:

Hibernate映射:

<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);     
  }  
}  

2 个答案:

答案 0 :(得分:2)

在我看来,在DBUtils代码中,您将两个保存包装在外部事务中,而在Utils代码中,您将它们放在两个完全独立的事务中而没有外部事务。

根据您的级联,Hibernate必须确定需要保存哪些对象。当您使用两个单独的事务运行Utils代码时,它将首先保存 Order 。由于您正在级联所有,这意味着它将首先保存订单,然后保存客户。但是,您已为客户员工创建了SAME 订单。因此, Employee 也会在第一个事务中保存(由于级联)。第二个交易,因为它是独立的,不会意识到这一点,并保存另一个员工

另一方面,如果将它们包装在外部事务中,Hibernate可以找出所有对象的身份并正确保存它们。

答案 1 :(得分:1)

尝试仅保存订单,而不是单独保存员工和客户。使用现有的cascade = all设置,两个父对象都将被保存,而不会创建任何重复项。