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

标签 java database hibernate jakarta-ee hibernate-mapping

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

我有 3 个实体Employee CustomersOrders

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

我希望能够保存/更新/删除 Customer 对象以及 EmployeesOrders 但我想使用一些接口(interface),以便调用代码不处理任何 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(客户);

仅创建了 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();

再次,它按预期工作。 IE。仅创建 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>
<小时/>

更新日期 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);     
  }  
}  

最佳答案

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

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

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

关于java - Hibernate 父/子关系。为什么对象被保存两次?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7621810/

相关文章:

java - 如何在不连接数据库的情况下通过Java创建BLOB对象

java - 为什么我的代码只用字符串替换方法替换某些字符?

mysql - 当我用信息填充表时,我是否会丢失表的 3NF

database - Oracle Advanced Security 的数据加密问题

java - 如何在事务期间将只读 hibernate session 转换为写入(主/从数据库)

java - Hibernate NamedQuery,设置值

java - Input In Java - 它是如何工作的?

java - 如何避免在打字时触发 JComboBox 中的 itemStateChanged 事件?

mysql - 自引用表。 MySQL

mysql - 异常(exception)是 org.hibernate.hql.internal.ast.QuerySyntaxException : books is not mapped [FROM books. ..