Java 休眠父/子关系。为什么对象保存两次?
我正在研究Hibernate的父/子关系 我有3个实体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
员工客户和订单
他们的关系是
员工
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”打破了会话范围,它讨论了糟糕的设计。也许应该通过休息模式来改变它,但为什么呢