目录
第一节 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&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 多对多级联保存
- 注意保存前的外键维护和级联是如何的配置
- 一般操作多的一方选择级联
- 保存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();
}
- 这里保存的是学生对象,所以在Student.hbm.xml中配置级联保存
- 运行,保存成功
1.9 外键维护注意事项
- 这里是保存Student对象在Student配置了级联保存,默认的inverse=“false”,由Student来维护外键关系,中间表有数据。
- 如果在Student配置inverse=“true”,将由Course来维护外键关系,中间表没数据,需要在Course中配置级联保存,且执行test中要保存的是Course对象中间表才有数据。
- 多对多,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)数据库有良好的表连接性能。 |