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

Java 休眠父/子关系。为什么对象保存两次?,java,database,hibernate,jakarta-ee,hibernate-mapping,Java,Database,Hibernate,Jakarta Ee,Hibernate Mapping,我正在研究Hibernate的父/子关系 我有3个实体员工客户和订单 他们的关系是 员工1 N订单 客户1 N订单 我希望能够保存/更新/删除客户对象和员工和订单,但我希望使用一些接口,以便调用代码不处理任何Hibernate或JPA内容 例如,我尝试了以下方法: class Utils{ public static void saveObject(Object o){ logger.debug(o.toString()); Session session

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

我有3个实体
员工
客户
订单

他们的关系是
员工
1 N
订单

客户
1 N
订单

我希望能够保存/更新/删除
客户
对象和
员工
订单
,但我希望使用一些接口,以便调用代码不处理任何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条employee记录,而不是1条

如果我这样做:

Utils.saveObject(客户)

仅创建了1条(正确的)记录

为什么会发生这种情况?
发生这种情况是因为相同的
订单
对象由
客户
员工
保存吗?而
cascade=“all”
会产生这种副作用吗

现在,如果我不使用
DBUtils
方法,直接执行以下操作:

 Session session = DBUtil.getSessionFactory().openSession();        
 Transaction tx = session.beginTransaction();
 session.save(employee);
 session.save(customer);
 tx.commit();
 session.close();
同样,它也如预期的那样工作。即,只创建1条员工记录

我做错什么了


更新:

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

<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雇员{
//各成员
Set orders=new HashSet();
公共无效附加命令(命令){
order.setEmployee(本);
订单。添加(订单);
}  
}  
此外:

class客户{
//各成员
Set orders=new HashSet();
公共无效附加命令(命令){
order.setCustomer(本);
订单。添加(订单);
}  
}  

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

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

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

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


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

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

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


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

您可以显示您的Hibernate映射吗?我已经用映射更新了post。您的事务处理完全错误。事务不仅仅是存储实体所需的东西。它应该是工作的一个原子单位。所有必须一致的数据库更新都必须在一个唯一的事务中完成。这就是为什么它在执行代码时不使用
DBUtils
?映射,并且还显示方法addOrder。如果“order”是一个类,那么如果您执行session.save(order),它应该保存在三个对象中。此外,对象“utils”打破了会话范围,它讨论了糟糕的设计。也许应该通过一个repositoy模式来改变它。你能显示你的hibernate映射吗?我已经用映射更新了帖子。你的事务处理完全错误。事务不仅仅是存储实体所需的东西。它应该是工作的一个原子单位。所有必须一致的数据库更新都必须在一个唯一的事务中完成。这就是为什么它在执行代码时不使用
DBUtils
?映射,并且还显示方法addOrder。如果“order”是一个类,那么如果您执行session.save(order),它应该保存在三个对象中。此外,对象“utils”打破了会话范围,它讨论了糟糕的设计。也许应该通过休息模式来改变它,但为什么呢