我正在研究Hibernate的父/子关系。
我有3个实体Employee
Customers
和Orders
。
他们的关系是
Employee
1< - > N Orders
Customer
1< - > N Orders
我希望能够保存/更新/删除Customer
个对象以及Employees
和Orders
但是我想使用一些接口,这样调用代码就不会处理任何Hibernate或JPA的东西。
E.g。我试过以下的东西:
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条(正确)记录。
为什么会这样?
是否会发生这种情况,因为Order
和Customer
同时保存了相同的Employee
对象? 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);
}
}
答案 0 :(得分:2)
在我看来,在DBUtils代码中,您将两个保存包装在外部事务中,而在Utils代码中,您将它们放在两个完全独立的事务中而没有外部事务。
根据您的级联,Hibernate必须确定需要保存哪些对象。当您使用两个单独的事务运行Utils代码时,它将首先保存 Order 。由于您正在级联所有,这意味着它将首先保存订单,然后保存客户。但是,您已为客户和员工创建了SAME 订单。因此, Employee 也会在第一个事务中保存(由于级联)。第二个交易,因为它是独立的,不会意识到这一点,并保存另一个员工。
另一方面,如果将它们包装在外部事务中,Hibernate可以找出所有对象的身份并正确保存它们。
答案 1 :(得分:1)
尝试仅保存订单,而不是单独保存员工和客户。使用现有的cascade = all设置,两个父对象都将被保存,而不会创建任何重复项。