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

发布于 2024-12-07 17:24:20 字数 5659 浏览 0 评论 0原文

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

我有 3 个实体 Employee CustomersOrders

他们的关系是
员工 1 <-> N 订单
客户 1 <-> N Orders

我希望能够保存/更新/删除 Customer 对象以及 EmployeesOrders 但我想要使用某些接口,以便调用代码不处理任何 Hibernate 或 JPA 内容。

例如,我尝试了类似以下的操作:

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 条员工记录。

我在这里做错了什么?


更新:

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

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

Also:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

靖瑶 2024-12-14 17:24:21

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

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

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

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.

北方的韩爷 2024-12-14 17:24:21

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文