Hibernate的多表关联关系映射、案例:一对多、多对一查询与删除、cascade级联保存、修改、删除、级联组合、案例:多对多、级联保存与外键维护、fetch、批量加载、检测策略-day03

第一节 Hibernate的多表关联关系映射

1.1 多表关系

  • 表之间关系存在3种:一对多、多对多、一对一
    • 一对多:主表的主键从表外键 形成 主外键关系
    • 多对多:提供中间表(从表),提供2个字段(外键)分别对应两个主表
    • 一对一: 主外键关系

在这里插入图片描述

  • 举例:
    • 多对多关系:学生与课程
    • 一对多关系:客户与订单
    • 一对一关系:人与身份证

1.2 案例:一对多、多对一

  • 例如:客户和订单的关系是一对多的关系,订单和客户的关系是多对一的

第一步:实体类的编写

  • Customer.java
package com.it.hibernate.domain;

import java.util.HashSet;
import java.util.Set;

/**
 * @ClassName Customer
 * @Author shuyy
 * @Date 2020/8/29 
 **/
public class Customer {
    
    

    private Integer customerId;//客户id
    private String customerName;//客户名

    //一对多,一的一方,使用set集合
    private Set<Order> orders = new HashSet<Order>();

    public Integer getCustomerId() {
    
    
        return customerId;
    }

    public void setCustomerId(Integer customerId) {
    
    
        this.customerId = customerId;
    }

    public String getCustomerName() {
    
    
        return customerName;
    }

    public void setCustomerName(String customerName) {
    
    
        this.customerName = customerName;
    }

    public Set<Order> getOrders() {
    
    
        return orders;
    }

    public void setOrders(Set<Order> orders) {
    
    
        this.orders = orders;
    }
}

注意:set集合一定要在定义的时候初始化对象,而且需要提供属性的get/set方法

  • Order.java
package com.it.hibernate.domain;

/**
 * @ClassName Order
 * @Author shuyy
 * @Date 2020/8/29 
 **/
public class Order {
    
    

    private Integer orderId;//订单id
    private String orderName;//订单名

    //多对一
    private Customer customer;//订单所属的客户

    public Integer getOrderId() {
    
    
        return orderId;
    }

    public void setOrderId(Integer orderId) {
    
    
        this.orderId = orderId;
    }

    public String getOrderName() {
    
    
        return orderName;
    }

    public void setOrderName(String orderName) {
    
    
        this.orderName = orderName;
    }

    public Customer getCustomer() {
    
    
        return customer;
    }

    public void setCustomer(Customer customer) {
    
    
        this.customer = customer;
    }
}

第二步:映射文件的编写

  • Customer.hbm.xml
<!DOCTYPE hibernate-mapping PUBLIC
		"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
		"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.it.hibernate.domain">
	<!--package包名写在这,简化下面书写-->
	<!--  name:模型的全名称-->
	<class name="Customer" table="t_customer"
	dynamic-update="true" dynamic-insert="true">
		<!-- name:模型属性名  -->
		<id name="customerId" column="customerId">
			<!-- generator:id的生成策略
				increment:也会自动增长id,但是它的这种增长是hibernate自己实现的
							执行select max(id)查询,但是这种会有线程并发问题(如果2个数据同时插入,后面的数据插不进去)

				sequence:一般在oracle数据库使用
				hilo:hibernate:自己实现的id规则【一般不用,不学】

				native:【经常常用】【这个自动增长的实现是mysql实现的,mysql它会记录上一条记录的id值然后加1】
						如果是mysql数据库,id会自动增长
						如果是oracle数据库,也会自动增长,一般是与sequence相关
				uuid:【经常常用】一个长字符串,需要把模型的id改成字符串
						保存的时候,不用自己设置id,hibernate会设置id

				assigned:【经常常用】需要手动设置id属性
			 -->
			<generator class="native"></generator>
		</id>

		<!-- 如果模型的属性和数据库的列名一样,就不用写column -->
		<property name="customerName" type="string" length="20"></property>
		<!-- 一个客户可以有多个订单,hibernate可以双向描述一对多的关系,set中name写的是实例的属性-->
		<set name="orders">
			<!--column指的是Order表中的外键-->
			<!--
				one-to-many:一对多,里面写class,写多的一方
			-->
			<key column="customerId"></key>
			<one-to-many class="Order"></one-to-many>
		</set>
	</class>
</hibernate-mapping>
  • Order.hbm.xml
<!DOCTYPE hibernate-mapping PUBLIC
		"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
		"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.it.hibernate.domain">
	<!--package包名写在这,简化下面书写-->
	<!--  name:模型的全名称-->
	<class name="Order" table="t_order"
	dynamic-update="true" dynamic-insert="true">
		<!-- name:模型属性名  -->
		<id name="orderId" column="orderId">
			<!-- generator:id的生成策略
				increment:也会自动增长id,但是它的这种增长是hibernate自己实现的
							执行select max(id)查询,但是这种会有线程并发问题(如果2个数据同时插入,后面的数据插不进去)

				sequence:一般在oracle数据库使用
				hilo:hibernate:自己实现的id规则【一般不用,不学】

				native:【经常常用】【这个自动增长的实现是mysql实现的,mysql它会记录上一条记录的id值然后加1】
						如果是mysql数据库,id会自动增长
						如果是oracle数据库,也会自动增长,一般是与sequence相关
				uuid:【经常常用】一个长字符串,需要把模型的id改成字符串
						保存的时候,不用自己设置id,hibernate会设置id

				assigned:【经常常用】需要手动设置id属性
			 -->
			<generator class="native"></generator>
		</id>

		<!-- 如果模型的属性和数据库的列名一样,就不用写column -->
		<property name="orderName" type="string" length="32"></property>
		<!-- 多个订单对应一个客户,hibernate可以双向描述一对多的关系,set中name写的是实例的属性-->
		<!--
			many-to-one:多对一
			class:写一的一方
			column:外键
		-->
		<many-to-one name="customer" class="Customer" column="customerId"></many-to-one>
	</class>
</hibernate-mapping>

第三步:配置文件添加映射

  • hibernate.cfg.xml
<!DOCTYPE hibernate-configuration PUBLIC
	"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
	"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
	<session-factory>
		<!-- 1.配置数据库连接的4个参数 -->
		<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
		<property name="hibernate.connection.url">jdbc:mysql:///hibernate_day1?useUnicode=true&amp;characterEncoding=utf8</property>
		<property name="hibernate.connection.username">root</property>
		<property name="hibernate.connection.password">root</property>

		<!-- 2.是否显示sql语句 -->
		<property name="show_sql">true</property>
		
		<!-- 3.是否格式化sql语句 -->
		<property name="format_sql">true</property>
		
		<!-- 4.是否自动提交事务:针对insert有效,针对delete无效 -->
		<property name="hibernate.connection.autocommit">true</property>
		
		<!-- 5.开启与当前线程绑定session的功能
			ThreadLocal<Connection>
			ThreadLocal<ActionContext>
			ThreadLocal<Session>
		 -->
		<property name="hibernate.current_session_context_class">thread</property>

		<!-- 6.hibernate.hbm2ddl.auto
			ddl数据库定义语言
			配置映射文件与数据库表的关系
			update:如果数据库有没表,自动帮你创表【常用】
				   如果hbm与数据表不一样,会更新【例如在User实体类中添加一个字段,程序执行,数据库会自动帮你更新表结构】
			create:每次启动hibernate都帮你创建表【所以插入数据后再执行程序,会覆盖刷新】
			create-drop,每次启动hibernate都帮你创建表,执行完后删除表【要你何用,用完就删,可以测试时使用,哈哈】
			validate:检验hbm文件,如果与数据库的字段不一致,就抛出异常【还好】
		 -->
		<property name="hibernate.hbm2ddl.auto">update</property>

		<!-- 7.hibernate.dialect:数据库方言
			每个数据库都有自己的方言,MySQL有MySQL的方式,oracle有oracle的方式
			因为一个意思有不同的表达方式,就如同不同地方有不同的方言
			MySQL与oracle的分页语句不同:
			 mysql:分页使用limit
			 oracle:分页使用rownum
			 可以查看MySQLDialect和OracleDialect的getLimitString
		 -->
		<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
		<!--<property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>-->

		<!-- 8.配置JavaBean与表的映射文件 -->
		<mapping resource="com/it/hibernate/domain/User.hbm.xml" />
		<mapping resource="com/it/hibernate/domain/Customer.hbm.xml" />
		<mapping resource="com/it/hibernate/domain/Order.hbm.xml" />
	</session-factory>
</hibernate-configuration>

第四步:单元测试

package com.it.hibernate.test;

import com.it.hibernate.domain.Customer;
import com.it.hibernate.domain.Order;
import com.it.hibernate.uitls.HibernateUtils;
import org.hibernate.Session;
import org.junit.Test;

public class Demo7 {
    
    
	@Test
	public void test7(){
    
    
		Session session = HibernateUtils.openSession();
		session.beginTransaction();
		//创建一个客户
		Customer customer = new Customer();
		customer.setCustomerName("shu");
		//创建多个订单
		Order order1 = new Order();
		order1.setOrderName("iphone11");

		Order order2 = new Order();
		order2.setOrderName("iphone12");
		//维护客户与订单的关系
		customer.getOrders().add(order1);
		customer.getOrders().add(order2);

		//维护订单与客户的关系
		order1.setCustomer(customer);
		order2.setCustomer(customer);

		session.save(customer);//执行3条insert sql语句
		session.save(order1);//执行1条update sql语句,维护外键关系
		session.save(order2);//执行1条update sql语句,维护外键关系

		session.getTransaction().commit();
		session.close();
	}

}

在这里插入图片描述

  • 更新客户id
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

1.3 设置外键维护的方式

  • 修改Customer的配置文件,添加一个inverse选项
  • inverse是hibernate双向关系中的基本概念。inverse的真正作用就是指定由哪一方来维护外键的关联关系。当一方中指定了inverse=“false”(默认)(就是自己处理)。当一方中指定了inverse=“true”(另外一方处理)。
    在这里插入图片描述
  • 执行后,发现只有3条insert语句执行,没有update
    在这里插入图片描述
  • 执行效果一样
    在这里插入图片描述
    在这里插入图片描述

从性能上来看,会用inverse=“true”,少执行了2条update语句,效果一样。

1.4 查询一对多与删除

查询一对多

	@Test
	public void test2(){
    
    
		Session session = HibernateUtils.openSession();
		//查询一对多
		/*
		 默认情况下,只有当使用Customer的orders数据,会执行SQL查询
		*/
		//1.查询客户id为1的客户
		Customer customer = (Customer) session.get(Customer.class, 1);
		//2.获取名字
		System.out.println("客户名:"+customer.getCustomerName());
		//3.获取这个客户的所有订单(这里可以不用hql或sql)
		Set<Order> orders = customer.getOrders();
		for (Order order : orders) {
    
    
			System.out.println("购买订单的名称:"+order.getOrderName());
		}
		session.close();
	}

在这里插入图片描述

  • 如果注释3,不查询orders,不会执行对应sql
    在这里插入图片描述

删除一对多

  • 直接删除会报错,因为有外键约束
    在这里插入图片描述
  • 去除外键约束,设置为null即可
	@Test
	public void test3(){
    
    
		Session session = HibernateUtils.openSession();
		session.getTransaction().begin();
		//删除一对多
		//1.查询客户id为1的客户
		Customer customer = (Customer) session.get(Customer.class, 1);
		//如果要删除一对多,(要把多的那一方)order的约束去除
		for (Order order : customer.getOrders()) {
    
    //getOrders会执行select查询订单
			//设置一个空就会把对象的约束去除
			order.setCustomer(null);//会执行update语句,把数据库的外键设置为null
		}
		//2.删除客户
		session.delete(customer);
		session.getTransaction().commit();
		session.close();
	}

在这里插入图片描述
在这里插入图片描述

  • 主表t_customer客户删除成功
    在这里插入图片描述
  • 外键id删除成功
    在这里插入图片描述

1.5 cascade级联

save-update级联保存、级联修改

  • 例如:保存A(客户)时,会同时保存B(订单)。(优点:简化我们的操作)
  • 之前的案例,保存客户不保存订单信息,将有客户信息而没有对应的订单。
    在这里插入图片描述
  • 有客户4
    在这里插入图片描述
  • 无客户4的订单信息
    在这里插入图片描述
  • 在Customer的映射文件Customer.hbm.xml中配置
    在这里插入图片描述
  • 再运行一下
	@Test
	public void test1(){
    
    
		Session session = HibernateUtils.openSession();
		session.beginTransaction();
		//创建一个客户
		Customer customer = new Customer();
		customer.setCustomerName("shuSave-update");
		//创建多个订单
		Order order1 = new Order();
		order1.setOrderName("iphone11");

		Order order2 = new Order();
		order2.setOrderName("iphone12");
		//维护客户与订单的关系
		customer.getOrders().add(order1);
		customer.getOrders().add(order2);

		//维护订单与客户的关系
		order1.setCustomer(customer);
		order2.setCustomer(customer);

		session.save(customer);//执行3条insert sql语句
		//订单不保存
		/*session.save(order1); //执行1条update sql语句,维护外键关系
		session.save(order2);*/ //执行1条update sql语句,维护外键关系

		session.getTransaction().commit();
		session.close();
	}
  • 看sql已插入订单信息
    在这里插入图片描述
  • 有客户5,也有订单
    在这里插入图片描述

在这里插入图片描述

级联删除 delete

  • 删除A(客户)时,同时删除B(订单)
  • 在Customer.hbm.xml中修改cascade=“delete”
    在这里插入图片描述
	@Test
	public void test2(){
    
    
		Session session = HibernateUtils.openSession();
		session.getTransaction().begin();
		//级联删除,删除客户的同时删除订单,会先删除订单(外键)再删除客户
		Customer customer = (Customer) session.get(Customer.class, 2);
		session.delete(customer);
		session.getTransaction().commit();
		session.close();
	}
  • 直接运行不会报错,因为设置了级联删除
  • 查询客户、查询订单
    在这里插入图片描述
  • 先删除2个订单再删除客户
    在这里插入图片描述
  • id为2的客户被删除成功
    在这里插入图片描述
  • 订单3、4被删除成功
    在这里插入图片描述

delete-orphan 级联孤儿删除

  • 孤儿删除,解除关系,将B(订单)删除,A(客户)还存在。
	@Test
	public void test3(){
    
    
		Session session = HibernateUtils.openSession();
		session.getTransaction().begin();
		//1.获取客户
		Customer customer = (Customer) session.get(Customer.class, 3);
		//2.孤儿删除(把客户里的所有订单移除)
		//不能使用下面这2种方法移除订单,因为客户不知道自己的订单,会报错
		//customer.setOrders(null);
		//customer.setOrders(new HashSet<Order>());

		//把订单从Customer的Set集合中删除
		Set<Order> orders = customer.getOrders();
		//使用迭代器删除(让一个集合一边遍历、一边删除或一边修改,用for循环会报错,用迭代器不会,内部允许)
		Iterator<Order> iterator = orders.iterator();
		while (iterator.hasNext()){
    
    
			iterator.next();//取出下一个元素
			iterator.remove();//移除当前元素
		}
		//session.delete(customer);//无需写
		session.getTransaction().commit();
		session.close();
	}

在这里插入图片描述
在这里插入图片描述

  • 客户3的订单删除成功
    在这里插入图片描述
  • 客户3还在
    在这里插入图片描述

级联组合

  • 如果需要配置多项,使用逗号分隔。<set cascade=“save-update,delete”>
  • all:【save-update,delete】save-update 和 delete 整合
  • all-delete-orphan: 【save-update,delete,delete-orphan】三个整合

1.6 Hibernate实体状态回顾

在这里插入图片描述

1.7 案例:多对多

  • 以学生和课程为例,一个学生可以对应多个课程,多个学生可以对应一个课程。

第一步:实体类编写

  • Student.java 学生类
package com.it.hibernate.domain;

import java.util.HashSet;
import java.util.Set;

/**
 * @ClassName Student
 * @Author shuyy
 * @Date 2020/8/31
 **/
public class Student {
    
    
    private Integer studentId;//学生id
    private String studentName;//学生名

    private Set<Course> courses = new HashSet<Course>();//学生可以选择多个课程

    public Student() {
    
    
    }

    public Student(String studentName) {
    
    
        this.studentName = studentName;
    }

    public Integer getStudentId() {
    
    
        return studentId;
    }

    public void setStudentId(Integer studentId) {
    
    
        this.studentId = studentId;
    }

    public String getStudentName() {
    
    
        return studentName;
    }

    public void setStudentName(String studentName) {
    
    
        this.studentName = studentName;
    }

    public Set<Course> getCourses() {
    
    
        return courses;
    }

    public void setCourses(Set<Course> courses) {
    
    
        this.courses = courses;
    }
}

  • Course.java 课程类
package com.it.hibernate.domain;
 
import java.util.HashSet;
import java.util.Set;

/**
 * @ClassName Course
 * @Author shuyy
 * @Date 2020/8/31 
 **/
public class Course {
    
    
    private Integer courseId;//课程id
    private String courseName;//课程名

    private Set<Student> students = new HashSet<Student>();//课程可以被多个学生选择

    public Course() {
    
    
    }

    public Course(String courseName) {
    
    
        this.courseName = courseName;
    }

    public Integer getCourseId() {
    
    
        return courseId;
    }

    public void setCourseId(Integer courseId) {
    
    
        this.courseId = courseId;
    }

    public String getCourseName() {
    
    
        return courseName;
    }

    public void setCourseName(String courseName) {
    
    
        this.courseName = courseName;
    }

    public Set<Student> getStudents() {
    
    
        return students;
    }

    public void setStudents(Set<Student> students) {
    
    
        this.students = students;
    }
}

第二步:映射文件编写

  • Student.hbm.xml
<!DOCTYPE hibernate-mapping PUBLIC
		"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
		"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.it.hibernate.domain">
	<class name="Student" table="t_student">
		<id name="studentId" column="sid">
			<generator class="native"></generator>
		</id>
		<property name="studentName" column="name"></property>
		<!--
			set:多对多
			table:写中间表的表名
			key中的column:写中间表当前模型(当前是Student)的外键名
			many-to-many:中class,写Course类名
			many-to-many:中column,写中间表的另外一个模型外键名
		-->
		<set name="courses" table="t_student_course">
			<key column="studentId"></key>
			<many-to-many class="Course" column="courseId"/>
		</set>
	</class>
</hibernate-mapping>
  • Course.hbm.xml
<!DOCTYPE hibernate-mapping PUBLIC
		"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
		"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.it.hibernate.domain">
	<class name="Course" table="t_course">
		<id name="courseId" column="cid">
			<generator class="native"></generator>
		</id>
		<property name="courseName" column="name"></property>

		<!--复制Student.hbm.xml中的set,取反即可-->
		<set name="students" table="t_student_course">
			<key column="courseId"></key>
			<many-to-many class="Student" column="studentId"/>
		</set>
	</class>
</hibernate-mapping>

第三步:配置文件添加映射

  • 在hibernate.cfg.xml中添加
<mapping resource="com/it/hibernate/domain/Student.hbm.xml" />
<mapping resource="com/it/hibernate/domain/Course.hbm.xml" />

在这里插入图片描述

第四步:测试

	@Test
	public void test1(){
    
    
		Session session = HibernateUtils.openSession();
		//测试映射文件是否配置正确,直接执行
		session.close();
	}
  • 成功运行
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

1.8 多对多级联保存

  • 注意保存前的外键维护和级联是如何的配置
  • 一般操作多的一方选择级联
  1. 保存2个学生和2个课程
	@Test
	public void test2(){
    
    
		Session session = HibernateUtils.openSession();
		session.getTransaction().begin();
		//1.创建2个学生
		Student student1 = new Student("shu01");
		Student student2 = new Student("shu02");

		//2.创建2个课程
		Course course1 = new Course("Java从入门到精通");
		Course course2 = new Course("Java架构师之路");

		//3.绑定学生课程的关系
		student1.getCourses().add(course1);
		student1.getCourses().add(course2);

		student2.getCourses().add(course1);
		student2.getCourses().add(course2);

		//4.保存
		/*
		 	注意事项:
		 	1.配置级联保存,只保存学生对象
		 	分析:插入2个学生、插入2个课程、插入中间表4条,共8条sql
		*/
		session.save(student1);
		session.save(student2);

		session.getTransaction().commit();
		session.close();
	}
  1. 这里保存的是学生对象,所以在Student.hbm.xml中配置级联保存
    在这里插入图片描述
  2. 运行,保存成功
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

1.9 外键维护注意事项

  1. 这里是保存Student对象在Student配置了级联保存,默认的inverse=“false”,由Student来维护外键关系,中间表有数据。
  2. 如果在Student配置inverse=“true”,将由Course来维护外键关系,中间表没数据,需要在Course中配置级联保存,且执行test中要保存的是Course对象中间表才有数据。
  3. 多对多,inverse不能两边都为true,如果两边都为true,不管保存哪个对象,中间表都没有数据。(不能踢皮球,必须要有一方来维护外键关系,否则中间表没有数据)
  • 如果Student.hbm.xml中配置inverse=“true”
    在这里插入图片描述
  • Course.hbm.xml中,默认为inverse=“false”,将由它来维护外键关系
    在这里插入图片描述
  • test3保存课程对象
@Test
	public void test3(){
    
    
		Session session = HibernateUtils.openSession();
		session.getTransaction().begin();
		//1.创建2个学生
		Student student1 = new Student("shu03");
		Student student2 = new Student("shu04");

		//2.创建2个课程
		Course course1 = new Course("Java是最好的语言");
		Course course2 = new Course("Java实战");

		//3.绑定学生课程的关系
		course1.getStudents().add(student1);
		course1.getStudents().add(student2);

		course2.getStudents().add(student1);
		course2.getStudents().add(student2);

		//4.保存
		session.save(course1);
		session.save(course2);

		session.getTransaction().commit();
		session.close();
	}
  • 中间表有数据
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

1.10 类级别的加载策略

  • get:立即检索。get方法一执行,立即查询所有字段的数据。
	@Test
	public void test1(){
    
    
		Session session = HibernateUtils.openSession();
		Student student = (Student) session.get(Student.class,1);
		System.out.println("---------");
		System.out.println(student.getStudentName());
		session.close();
	}

在这里插入图片描述

  • load:延迟检索(懒加载)。默认情况,load方法执行后,如果只使用OID的值不进行查询,如果要使用其他属性值才查询。
	@Test
	public void test2(){
    
    
		Session session = HibernateUtils.openSession();
		Student student = (Student) session.load(Student.class,1);
		System.out.println("---------");
		System.out.println(student.getStudentName());
		session.close();
	}

在这里插入图片描述

  • 这种情况使用id不查询,因为先get获取了id
	@Test
	public void test3(){
    
    
		Session session = HibernateUtils.openSession();
		Student student = (Student) session.load(Student.class,1);
		System.out.println("---------");
		System.out.println(student.getStudentId());
		session.close();
	}

在这里插入图片描述

  • 如果在类级别上配置lazy为false,那load方法就会即时加载,否则为延时加载。

  • 在Student.hbm.xml的class中配置
    在这里插入图片描述

  • 再运行load就是即时加载
    在这里插入图片描述

1.11 多对多中toString的注意事项

	@Test
	public void test1(){
    
    
		Session session = HibernateUtils.openSession();
		Student student = (Student) session.get(Student.class,1);
		System.out.println(student);
		session.close();
	}
  • 在实体类中没有提供(或重写)toString方法打印的是对象的地址
    在这里插入图片描述
  • 提供toString方法时注意不要全选,不要把集合也选上,会死循环报错
  • 调用toString方法,Student中集合courses对象会去找Course中的toString,而它里面又有students集合对象,又要去找Student的toString…死循环。
    在这里插入图片描述
  • 解决方法:生成toString时不选集合即可。

1.12 关联级别的加载策略

  • 关联级别的集合加载的策略是默认是懒加载
  • Student下的courses也是懒加载,只有访问的时候,才会执行sql
	@Test
	public void test1(){
    
    
		Session session = HibernateUtils.openSession();
		Student student = (Student) session.get(Student.class,1);
		System.out.println(student);
		System.out.println("-------------------");
		//在set中配置lazy="false",不用访问course属性,也会执行SQL查询数据
		Set<Course> courses = student.getCourses();
		System.out.println(courses);
		session.close();
	}

在这里插入图片描述

  • 可以设置为即时加载,如图,即加载学生时,学生所属的课程也会加载进来
  • 在set中配置lazy=“false”,不用访问course属性,也会执行SQL查询数据
    在这里插入图片描述
	@Test
	public void test1(){
    
    
		Session session = HibernateUtils.openSession();
		Student student = (Student) session.get(Student.class,1);
		System.out.println(student);
		System.out.println("-------------------");
		/*Set<Course> courses = student.getCourses();
		System.out.println(courses);*/
		session.close();
	}
  • 查询学生的时候也会使用内连接inner join查询课程
    在这里插入图片描述

1.13 set中的fetch

  • fetch:拿取、获取数据
  • fetch:是指查询集合的sql方式
    • select:【默认】,普通select查询语句
    • join:表连接语句查询
    • subselect:使用子查询

fetch:select 默认select查询

  • set集合默认的sql查询方式就是fectch=“select”,一个普通的select查询语句
    在这里插入图片描述
  • 上面的set中已经设置了lazy=false,所以下面程序执行查询学生信息也会关联查询课程信息
	@Test
	public void test1(){
    
    
		Session session = HibernateUtils.openSession();
		Student student = (Student) session.get(Student.class,1);
		System.out.println(student);
		session.close();
	}
  • 普通的select查询需要执行2条sql
    在这里插入图片描述

fetch:join 表查询

在这里插入图片描述

  • 执行同样的程序使用左外连接,一条sql搞定
    在这里插入图片描述

左外连接:左边没有匹配到右边,数据也会显示

fetch:subselect 子查询

  • 只能用于多对多,一对多
    在这里插入图片描述

  • 查询所有学生和学生对应的课程信息

	@Test
	public void test1(){
    
    
		Session session = HibernateUtils.openSession();
		Query from_student = session.createQuery("from Student");
		List<Student> list = from_student.list();
		for (Student student : list) {
    
    
			System.out.println("学生名:"+student.getStudentName());
			System.out.println("课程名:"+student.getCourses());
		}
		session.close();
	}
  • 执行2个sql,先查询所有的学生信息
    在这里插入图片描述
  • 再使用内连接查询课程信息,where条件中使用子查询
    在这里插入图片描述

1.14 多对一加载策略

  • 这里使用上面Customer和Order多对一案例来学习
  • 多对一标签:<many-to-one fetch="" lazy="">

注意:fetch:subselect不能使用在多对一,subselect返回的是一个集合,而多对一返回的是一个对象,不能使用,而且fetch标签在多对一里也没有subselect选项,只有select和join选项。

  • lazy有三个选项:
    • false:【即时加载】
    • no-proxy:无代理【很少使用】
    • proxy:使用代理,根据类级别的加载顺序策略来决定,还要依赖fetch【多种情况】
class:lazy many-to-one:lazy many-to-one:fetch 效果
false proxy join Order和Customer都是即时加载,一条sql搞定【使用连接】
false proxy select Order是即时加载,Customer是懒加载
true proxy join Order是懒加载,Customer是即时加载
true proxy select Order和Customer都是懒加载
	@Test
	public void test1(){
    
    
		Session session = HibernateUtils.openSession();
		//查看订单
		Order order = (Order) session.load(Order.class, 1);
		System.out.println("-----------------");//load默认是懒加载,select会在分割线后
		System.out.println("订单名:"+order.getOrderName());

		System.out.println("-----------------2");
		//查看订单所属的客户
		Customer customer = order.getCustomer();//获取客户默认也是懒加载
		System.out.println("订单所属客户:"+customer.getCustomerName());
		session.close();
	}

在这里插入图片描述
在这里插入图片描述

1.15 批量加载【了解】

  • set标签可以配置一个batch-size=“3”,表示每次可以加载3条数据
	@Test
	public void test1(){
    
    
		Session session = HibernateUtils.openSession();
		//查询所有客户
		List<Customer> list = session.createQuery("from Customer ").list();

		//遍历客户查看客户有多少订单
		for (Customer customer : list) {
    
    
			System.out.println("客户名:"+customer.getCustomerName()+" 订单数量:"+customer.getOrders().size());
		}

		session.close();
	}
  • 如果不设置batch-size,默认是有多少客户就查询一下每个客户对应的订单数量
    在这里插入图片描述
    在这里插入图片描述
  • 设置后
    在这里插入图片描述
  • 查订单时直接一次性查出来
    在这里插入图片描述

一般不使用,可以考虑使用分页。

1.16 检索策略

检索策略 优点 缺点 优先考虑使用的场合
立即检索(即时加载) 对应用程序完全透明,不管对象处于持久化状态还是游离状态,应用程序都可以从一个对象导航到关联的对象 (1)select语句多(2)浪费内存空间。 (1)类级别(2)应用程序需要立即访问的对象(3)使用了二级缓存
延迟检索(懒加载) 由应用程序决定需要加载哪些对象,可以避免执行多余的select语句,以及避免加载应用程序不需要访问的对象。因此能提高检索性能,并节省内存空间。 应用程序如果希望访问游离状态的代理类实例,必须保证它在持久化状态时已经被初始化。 (1)一对多或者多对多关联 (2)应用程序不需要立即访问或者根本不会访问的对象
表连接检索 (1)对应用程序完全透明,不管对象处于持久化状态还是游离状态,都可从一个对象导航到另一个对象。 (2)使用了外连接,select语句少 (1)可能会加载应用程序不需要访问的对象,浪费内存。 (2)复杂的数据库表连接也会影响检索性能。 (1)多对一或一对一关联 (2)需要立即访问的对象 (3)数据库有良好的表连接性能。

猜你喜欢

转载自blog.csdn.net/qq_43414199/article/details/108293166
今日推荐