以订单和订单项为例来讲解Hibernate关联映射中的一对多关联关系。两个实体类如下:
订单类:
package com.zking.four.entity;
import java.util.HashSet;
import java.util.Set;
/**
-
订单实体类(一对多中的一)
-
@author LJ
-
@Date 2018年10月23日
-
@Time 下午10:08:48
*/
public class Order {private Integer orderId;//订单ID(主键)
private String orderNo;//订单编号private Set orderItems=new HashSet();//描述一个订单可以有多个订单项
private Integer initOrderItems = 0;//0:懒加载 ,1:立即加载public Integer getInitOrderItems() {
return initOrderItems;
}
public void setInitOrderItems(Integer initOrderItems) {
this.initOrderItems = initOrderItems;
}
public Set getOrderItems() {
return orderItems;
}
public void setOrderItems(Set orderItems) {
this.orderItems = orderItems;
}
public Integer getOrderId() {
return orderId;
}
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
}
订单项类:
package com.zking.four.entity;
/**
-
订单项实体类(一对多中的多)
-
@author LJ
-
@Date 2018年10月23日
-
@Time 下午10:08:25
*/
public class OrderItem {private Integer orderItemId;//订单项ID(主键)
private Integer productId;//商品ID
private Integer quantity;//数量
private Integer oid;//订单ID(外键)private Order order;//订单项与订单关联,描述订单项属于某个订单
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
public Integer getOrderItemId() {
return orderItemId;
}
public void setOrderItemId(Integer orderItemId) {
this.orderItemId = orderItemId;
}
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public Integer getQuantity() {
return quantity;
}
public void setQuantity(Integer quantity) {
this.quantity = quantity;
}
public Integer getOid() {
return oid;
}
public void setOid(Integer oid) {
this.oid = oid;
}
}
Order.hbm.xml配置文件:
<property name="oid" type="java.lang.Integer" column="oid" insert="false" update="false"></property>
<many-to-one name="order" class="com.zking.four.entity.Order" column="oid"></many-to-one>
</class>
1、级联新增
/**
* 新增订单项表
* @author LJ
* @Date 2018年10月23日
* @Time 下午8:03:18
* @param orderItem
* @return
*/
public Integer saveOrderItem(OrderItem orderItem) {
Session session = SessionFactoryUtil.getSession();//获取session
Transaction transaction = session.beginTransaction();//开启事务
Integer oid = (Integer) session.save(orderItem);//新增订单项
transaction.commit();//提交事务
SessionFactoryUtil.closeSession();//关闭session
return oid;
}
@Test
public void testSaveOrderItem() {
OrderItem orderItem=new OrderItem();
Order order=new Order();
order.setOrderId(1);
orderItem.setOrder(order);
orderItem.setProductId(3);
orderItem.setQuantity(45);
this.saveOrderItem(orderItem);
}
若不对外键进行处理的话,会出现异常java.lang.ExceptionInInitializerError,这一异常导致的原因是Repeated column in mapping for entity: com.zking.four.entity.OrderItem column: oid (should be mapped with insert=“false” update=“false”),也就是因为OrderItem.hbm.xml配置文件里对外键重复配置了,所以才出现的异常
解决方式:a、删除从表对应的实体类中的外键属性
b、在配置的xml中外键属性上添加 insert=false,update=false的设置。
c、在配置的xml中的manyToOne标签中添加insert=false,update=false的设置。我这里选的是第二种方式
/**
* 新增订单表
* @author LJ
* @Date 2018年10月23日
* @Time 下午8:03:31
* @param order
* @return
*/
public Integer saveOrder(Order order) {
Session session = SessionFactoryUtil.getSession();
Transaction transaction = session.beginTransaction();
Integer oid = (Integer) session.save(order);
transaction.commit();
SessionFactoryUtil.closeSession();
return oid;
}
/**
* 提交订单中含有5个订单项的方法
* @author LJ
* @Date 2018年10月23日
* @Time 下午5:40:05
*/
@Test
public void testSaveOrder() {
Order order=new Order();
order.setOrderNo("2");
OrderItem orderItem;
for (int i = 1; i < 6; i++) {
orderItem=new OrderItem();
orderItem.setProductId(i);
orderItem.setQuantity(i);
orderItem.setOrder(order);
order.getOrderItems().add(orderItem);
}
this.saveOrder(order);
}
2、级联查询
/**
* 根据订单编号查询订单
* @author LJ
* @Date 2018年10月23日
* @Time 下午8:03:39
* @param order
* @return
*/
public Order getOrder(Order order) {
Session session = SessionFactoryUtil.getSession();
Transaction transaction = session.beginTransaction();
Order o = session.get(Order.class, order.getOrderId());
transaction.commit();
SessionFactoryUtil.closeSession();
return o;
}
@Test
public void testGetOrder() {
Order order=new Order();
order.setOrderId(2);//查询订单编号为2的订单
Order o = this.getOrder(order);
System.out.println(o.getOrderNo());
System.out.println(o.getOrderItems().size());
}
运行以上代码会发现只输出了o.getOrderNo(),而o.getOrderItems().size()没有输出,而且还报了异常org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.zking.four.entity.Order.orderItems, could not initialize proxy - no Session,这是懒加载的异常,因为级联查询默认是懒加载,所以要等到调用了o.getOrderItems().size()才会执行查询语句,但这时session已经关闭了,所以出现这一异常。
也许你会想到将lazy属性设为false就能解决这一问题,并不是这样,因为将lazy属性设为false后,虽然查询单个时不会出现异常,但查询所有时会存在问题,就是查询所有订单时,它会默认把订单下的所有订单项也给查询出来,但我们有时并不需要用到,所以这就浪费了资源,消耗了不该消耗的性能。综上所述,最好的解决方式就是通过字段控制是否需要懒加载。
3、普通删除
大家都知道,删除主键表里的某条数据时,要先删除外键表里的数据,所以删除订单时的代码如下:
/**
* 删除订单
* @author LJ
* @Date 2018年10月23日
* @Time 下午8:04:18
* @param order
*/
public void delOrder(Order order) {
Session session = SessionFactoryUtil.getSession();//获取session
Transaction transaction = session.beginTransaction();//开启事务
Order o = session.get(Order.class, order.getOrderId());//根据订单ID获取到该订单
for (OrderItem oi : o.getOrderItems()) {//遍历该订单所有的订单项
session.delete(oi);//先删除订单项
}
session.delete(o);//在删除该订单
transaction.commit();//提交事务
session.close();//关闭session
}
@Test
public void testDelOrder() {
Order order=new Order();
order.setOrderId(1);//删除订单编号为1的订单
this.delOrder(order);
}