我正在研究 Hibernate 的父/子关系。
我有 3 个实体Employee
Customers
和Orders
。
他们的关系是
员工
1 <-> N 订单
客户
1 <-> N 订单
我希望能够保存/更新/删除 Customer
对象以及 Employees
和 Orders
但我想使用一些接口(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 条(正确的)记录。
为什么会发生这种情况?
发生这种情况是否是因为 Customer
和 Employee
都保存了相同的 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/