开发者

Hibernate parent/child relationship. Why is object saved twice?

I am looking into Hibernate's parent/child relationships.

I have 3 entities Employee Customers and Orders.

Their relationship is

Employee 1 <-> N Orders

Customer 1 <-> N Orders

I want to be able to save/update/delete Customer objects and Employees and Orders but I want to use some interface so that the calling code does not deal with any Hibernate or JPA stuff.

E.g. I tried something like the following:

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

and in the calling code I do:

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

Now I noticed that with this code, 2 records of employee are created instead of 1.

If I only do:

Utils.saveObject(customer);

Only 1 (correctly) record is created.

Why does this happen?

Does this happens because the same Order object is saved by both Customer and Employee? And the cascade="all" makes this side-effect?

Now if I do not use the DBUtils method and do directly:

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

Again, it works as expected. I.e. only 1 employee record is created.

What am I doing something wrong here?


UPDATE:

Hibernate mappings:

<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="PAS开发者_Python百科SWORD" />
        </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);     
  }  
}  

Also:

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


It seems to me that in the DBUtils code, you are wrapping both saves in an outer transaction, whereas in the Utils code, you have them in two completely separate transactions without an outer one.

Depending on your cascading, Hibernate has to figure out which objects need to be saved. When you run the Utils code, with two separate transactions, it will save the Order first. Since you're cascading all, this means it will save the Order first, then the Customer. However, you've created the SAME Order for both the Customer and Employee. Therefore, the Employee also gets saved in the first transaction (due to cascading). The second transaction, since it is separate, will not be aware of this and save another Employee.

On the other hand, if you wrap them in an outer transaction, Hibernate can figure out the identities of all the objects and save them properly.


try saving only Order instead of saving Employee and Customer separately. With your existing cascade=all setting both parent object will get saved without creating any duplicates.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜