Hibernate关联关系(一对一、一对多)、级联新增、级联查询以及普通删除

版权声明:本文为博主原创文章,未经博主允许不得转载。https://blog.csdn.net/cms18374672699/article/details/83374577

1.Hibernate关联关系

1.关联的分类:关联可以分为一对一、一对多/多对一、多对多关联、关联是有方向的

在实际开发中我进行操们不可能只是对单表作,必然要进行多表操作,接下来由我来讲解多表操作中的一对一关联关系和一对多(或多对一)关联映射,一对一关系我只做个简单的讲解,关于用户和订单的关系,请看下图:

 

        Hibernate关联关系(一对一、一对多)、级联新增、级联查询以及普通删除_第1张图片

一对一关系:从实际的角度出发,一个订单信息只能是一个人下的订单,一个订单对应着一个人,故订单与用户的关系是一对一关系。

一对多关系:从订单角度出发,多个订单可属于一个用户,故订单与用户的关系是一对多(或多对一)的关系。如果从用户信息出发查询用户下的订单信息则为一对多查询,一个用户可以对应多个订单。

 

2.一对多关系

#insert属性设置中主控方概念理解:
3. 以客户和订单的一对多双向关联为例,讲解Set元素中的cascade|inverse|outter-join|lazy属性作用
    3.1 lazy:默认值为true,true延迟加载,false立即加载(一般设置为true,不使用立即加载,因为影响查询性能)
    3.2 outter-join:默认值为false,true使用左外联接查询关联的(但一般不用,因为当我们把该属性设置为true时,所有的查询语句都会默认左外联,那样性能不高)
    3.3 inverse:默认值为false,true表示将对方设置为主控方(一对多双向关联中一般将多方设置为主控方,这样可以减少SQL语句的数量,减少多余的操作)
    3.4 cascade:用来控制如何操作关联的持久化对象的
    3.4.1 none:保存,更新或删除当前对象时,忽略其它关联的对象
    3.4.2 save-update:保存、更新时级联保存所有的临时对象,并且级联更新关联的游离对象
    3.4.3 delete:通过session的delete方法删除当前对象,级联删除关联的对象
    3.4.4 all:等于save-update操作+delete操作

 

4. 案例:菜单对象的一对多双向自关联
  
  

  
  
       
       
  

5. 如何将多方映射成一个有序的集合
  
  
        
        
  

 

根据数据库的表及字段创建实体类  订单类(order)、订单项类(orderItem)

订单类(order)

public class Order {
	
	private Integer oid;
	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 getOid() {
		return oid;
	}
	public void setOid(Integer oid) {
		this.oid = oid;
	}
	public String getOrderNo() {
		return orderNo;
	}
	public void setOrderNo(String orderNo) {
		this.orderNo = orderNo;
	}
	
}

注意:定义一对多的关系时,一定需要采用接口方式,不许使用实现类

配置订单类order.hbm.xml映射文件




	
		
			
		
		
		
		
		
		
		
		
		

		
	


订单项类(orderItem)

public class OrderItem {
	
	private Integer orderItemId;
	private Integer productId;
	private Integer quantity;
	private Integer oid;
	
	//一对多的关系    一个订单项对应着多个订单
	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;
	}
	
}

配置订单项类orderItem.hbm.xml映射文件




	
		
			
		
		
		
		
		
		
		
		
	

在主配置文件   配置订单映射文件(order.hbm.xml)以及订单项映射文件(orderItem.hbm.xml)

主配置文件hibernate.cfg.xml






	
		
		root
		chenmingsheng
		jdbc:mysql://127.0.0.1:3306/mybatis_ssm?useUnicode=true&characterEncoding=UTF-8
		com.mysql.jdbc.Driver
		
		org.hibernate.dialect.MySQLDialect
		
		

		
		thread
		
		
		true
		true
		
		
		
		
		
	

配好这些主配置文件之后,再进行检验所有映射的配置文件配置是否准确

在工具类SessionFactoryUtils类中检测

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

/**
 *  1、方便获取session会话,用来操作数据库(操作重复性代码)
 *  2、用来检验所有映射的配置文件配置是否准确
 * @author Administrator
 *
 */
public class SessionFactoryUtils {
	
	private static SessionFactory sessionFactory;
	static {
		Configuration cfg = new Configuration().configure("hibernate.cfg.xml");
		sessionFactory = cfg.buildSessionFactory();
	}
	
	//获取session会话
	public static Session getSession() {
		Session session = sessionFactory.getCurrentSession();//本地会话
		if(session == null) {//如果本地没有会话
			session = sessionFactory.openSession();//就创建一个会话
		}
		return session;
	}
	
	//关闭会话
	public static void closeSession() {
		Session session = sessionFactory.getCurrentSession();
		if(session != null && session.isOpen()) {//如果本地有会话,并且打开的
			session.close();//就把会话关闭
		}
	}
	
	public static void main(String[] args) {
		Session session = SessionFactoryUtils.getSession();
		session.beginTransaction();//开始事务
		System.out.println(session.isConnected());
		SessionFactoryUtils.closeSession();//关闭事务
		System.out.println(session.isConnected());
	}
}

出现以下信息,就说明你配置成功了!

Hibernate关联关系(一对一、一对多)、级联新增、级联查询以及普通删除_第2张图片

 

2.Hibernate的级联新增

根据以上配置给对应类创建dao方法

在dao方法里面新增订单项方法addOrderItem

public class OrderDao {
	
	//新增订单项
	public Integer addOrderItem(OrderItem orderItem) {
		Session session = SessionFactoryUtils.getSession();//获取session对象
		Transaction transaction = session.beginTransaction();//开始事务
		Integer otid = (Integer) session.save(orderItem);//新增方法
		transaction.commit();//关闭事务
		session.close();//关闭会话
		return otid;
	}
}

在OrderDaoTest类中进行测试

public class OrderDaoTest {

	private OrderDao orderDao = new OrderDao();
	
	@Test
	public void testAddOrderItem() {
		OrderItem orderItem = new OrderItem();
		orderItem.setOid(3);
		orderItem.setProductId(89);
		orderItem.setQuantity(78);
		this.orderDao.addOrderItem(orderItem);
	}

会出现以下错误:

                                                      重复的     列        映射到     实体
	  Caused by: org.hibernate.MappingException: Repeated column in mapping for entity: com.zking.four.entity.OrderItem column: oid (should be mapped with insert="false" update="false")
	  错误信息:也就是同一个字段被映射了两次    

 外键处理的三种解决方案
     1.在对应的数据库以及实体类中把被映射的字段(oid属性)给删除   (数据库的oid字段一般都不允许随便进行修改,一般不采用这种方法,也不建议使用这种方法)      删除从表对应的实体类中的外键属性
     2.在配置的xml中外键属性上添加 insert=false,update=false的设置。  ①在property标签上添加 

     3.在配置的xml中的manyToOne标签中添加insert=false,update=false的设置。  ②在many-to-one标签上添加

  ①在property标签上添加 

注意:在property标签添加  insert="false" update="false",测试类中的给它设置的oid就没有用了,因为这里的oid字段设置为false

不交由oid字段维护,而是many-to-one中的order维护

Hibernate关联关系(一对一、一对多)、级联新增、级联查询以及普通删除_第3张图片

按照以上的方法就可以解决了 

 

②在many-to-one标签上添加

在many-to-one标签上可以直接用传统的添加

在测试类进行测试

Hibernate关联关系(一对一、一对多)、级联新增、级联查询以及普通删除_第4张图片

执行结果后会添加到数据库中

Hibernate关联关系(一对一、一对多)、级联新增、级联查询以及普通删除_第5张图片

例如:下面我们提交一个具有6个订单项的订单

数据库原有数据

                            Order订单表原有数据                                                           orderItem订单项表原有数据

    Hibernate关联关系(一对一、一对多)、级联新增、级联查询以及普通删除_第6张图片       Hibernate关联关系(一对一、一对多)、级联新增、级联查询以及普通删除_第7张图片                                                                                                                                                                                                                         

执行代码

        //提交一个具有6个订单的订单项
	@Test
	public void testAddOrder() {
		Order order = new Order();
		order.setOid(6);
		order.setOrderNo("b6");
		
		OrderItem orderItem;
		for (int i = 1; i < 7; i++) {
			orderItem = new OrderItem();
			orderItem.setProductId(i);
			orderItem.setQuantity(i);
			//加关系
			order.getOrderItems().add(orderItem);
			orderItem.setOrder(order);
		}
		this.orderDao.addOrder(order);
	}

数据库数据的变化

Hibernate关联关系(一对一、一对多)、级联新增、级联查询以及普通删除_第8张图片

当你运行方法时,数据库就提交一个具有6个订单的订单项

 

3.Hibernate的级联查询

前面配置和方法都已经做好了铺垫,我们直接写dao方法

        public Order getOrder(Order order) {
		Session session = SessionFactoryUtils.getSession();
		Transaction transaction = session.beginTransaction();
		Order o = session.get(Order.class, order.getOid());
		transaction.commit();
		session.close();
		return o;
	}

测试类

        @Test
	public void testGetOrder() {
		Order order = new Order();
		order.setOid(5);
		Order o = this.orderDao.getOrder(order);
		System.out.println(o.getOrderNo());
		System.out.println(o.getOrderItems().size());//打印订单项
		for (OrderItem ot : o.getOrderItems()) {
			System.out.println(ot.getProductId());
		}
	}

执行会报以下错误:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.zking.four.entity.Order.orderItems, could not initialize proxy - no Session

错误原因:懒加载策略,之前把oid保存到代理对象里面,而当我使用订单项的时候,session对象已经关闭了,才会出现这个错误。这里再三强调:定义一对多关系时一定要采用接口方式,不许使用实现类

 

两种处理方法: ①懒加载   ②强制加载

①懒加载 

1.在对应的xml文件set标签中添加lazy="false",,懒加载行不通,我们给它设置为false(立即加载),这是网上常用来解决的方法,数据是可以测试出来(这个方法一般不可取)

 注意:默认懒加载   改为lazy="false"立即加载的后果就是超耗性能,不管你数据库有多少条数据,都帮你加载出来

 

②强制加载
 解决方案:通过字段控制,强制加载。Hibernate.initialize()

三个步骤

①在订单类添加字段,给它设置强制加载

Hibernate关联关系(一对一、一对多)、级联新增、级联查询以及普通删除_第9张图片

②在order的dao方法类中进行判读是否使用强制加载

Hibernate关联关系(一对一、一对多)、级联新增、级联查询以及普通删除_第10张图片

 

③在test测试类方法中添加order.setInitOrderItems(1);给它设置强制加载

Hibernate关联关系(一对一、一对多)、级联新增、级联查询以及普通删除_第11张图片

测试结果为

Hibernate关联关系(一对一、一对多)、级联新增、级联查询以及普通删除_第12张图片

然后数据库的数据就加载出来了

 

 

4、Hibernate的普通删除

我们直接写dao方法

        public void deleteOrder(Order order){
		Session session = SessionFactoryUtils.getSession();
		Transaction transaction = session.beginTransaction();
		session.delete(order);
		transaction.commit();
		session.close();
	}

test测试类

        @Test
	public void testDeleteOrder(){
		Order order = new Order();
		order.setOid(6);
		this.orderDao.deleteOrder(order);
	}

直接执行代码是删除不掉数据的,会报以下错误

ERROR: Cannot delete or update a parent row: a foreign key constraint fails (`mybatis_ssm`.`t_hibernate_order_item`, CONSTRAINT `t_hibernate_order_item_ibfk_1` FOREIGN KEY (`oid`) REFERENCES `t_hibernate_order` (`oid`))

错误信息:存在主外键,因为删除表中存在的外键关系,只删除一个表中数据是删除不掉的

解决办法:

Hibernate关联关系(一对一、一对多)、级联新增、级联查询以及普通删除_第13张图片

 

      数据库原有数据

     Hibernate关联关系(一对一、一对多)、级联新增、级联查询以及普通删除_第14张图片          Hibernate关联关系(一对一、一对多)、级联新增、级联查询以及普通删除_第15张图片                               

执行以上代码后

数据库的数据就删除了,原本订单表中的oid为6订单和订单项表中oid为6的全部订单项都删除了

   Hibernate关联关系(一对一、一对多)、级联新增、级联查询以及普通删除_第16张图片        Hibernate关联关系(一对一、一对多)、级联新增、级联查询以及普通删除_第17张图片

你可能感兴趣的:(Hibernate学习及应用)