Hibernate3 关联关系映射

多表设计

l 在开发中,前期进行需求分析,需求分析提供E--R图,根据ER图编写表结构。

l 表之间关系存在3种:一对多、多对多、一对一。(回顾)

 

一对多:1表(主表)必须主键 和 多表(从表)必须外键,主表的主键 与 从表外键 形成主外键关系

多对多:提供中间表(从表),提供2个字段(外键)分别对应两个主表。

一对一:???

 

 

 

l 面单对象描述 对象与对象 之间关系?【掌握】

一对多:客户和订单

private class Customer{

//一对多:一个客户 拥有 多个订单

private Set<Order> orderSet;

}

private class Order{

//多对一:多个订单 属于 一个客户

private Customer customer;

}

多对多:Student学生 和 Course课程

private class Student{

//多对多:多个学生(当前)学习 【不同课程】

private Set<Course> courseSet ...;

}

private class Course{

//多对多:多个课程 可以被 【不同学生】学习

private Set<Student> student = ...;

}

一对一:公司company 和 地址address

private class Company{

private Address address;

}

private class Address{

private Company company;

}

 

关联关系映射

2.1 一对多实现【掌握】

2.1.1 实现类

public class Customer {

 

private Integer cid;

private String cname;

 

//一对多:一个客户(当前客户) 拥有 【多个订单】

// * 需要容器存放多个值,一般建议Set (不重复、无序)

// * 参考集合:ListMapArray等 

// ** 建议实例化--使用方便

private Set<Order> orderSet = new HashSet<Order>();

 

public class Order {

private Integer xid;

private String price;

 

//多对一:多个订单属于【一个客户】

private Customer customer;

 

 

2.1.2 配置文件

l Customer.hbm.xml

<class name="com.hibernate.b_onetomany.Customer" table="t_customer">

<id name="cid">

<generator class="native"></generator>

</id>

<property name="cname"></property>

 

<!-- 一对多:一个客户(当前客户) 拥有 【多个订单】

确定容器  set <set>

2 name确定对象属性名

确定从表外键的名称

确定关系,及另一个对象的类型

注意:

hibernate中可以只进行单向配置

每一个配置项都可以完整的描述彼此关系。

一般情况采用双向配置,双方都可以完成描述表与表之间关系。

 -->

<!-- 一对多:一个客户(当前客户) 拥有 【多个订单】 -->

<set name="orderSet" cascade="delete-orphan">

<key column="customer_id"></key>

<one-to-many class="com.it.b_onetomany.Order"/>

</set>

</class>

 

l Order.hbm.xml

<class name="com.hibernate.b_onetomany.Order" table="t_order">

<id name="xid">

<generator class="native"></generator>

</id>

<property name="price"></property>

 

<!-- 多对一:多个订单属于【一个客户】 

* name 确定属性名称

* class 确定自定义类型

* column 确定从表的外键名称

-->

<many-to-one name="customer" class="com.hibernate.b_onetomany.Customer" column="customer_id"></many-to-one>

 

</class>

 

 

 

 

2.2 一对多操作

2.2.1 保存客户

@Test

public void demo01(){

// 1 创建客户,并保存客户--成功

Session session = factory.openSession();

session.beginTransaction();

 

Customer customer = new Customer();

customer.setCname("田志成");

 

session.save(customer);

 

session.getTransaction().commit();

session.close();

}

 

2.2.2 保存订单

@Test

public void demo02(){

// 2 创建订单,保存订单--成功,外键为null

Session session = factory.openSession();

session.beginTransaction();

 

Order order = new Order();

order.setPrice("998");

 

session.save(order);

 

session.getTransaction().commit();

session.close();

}

 

2.2.3 客户关联订单,只保存客户

@Test

public void demo03(){

// 3 创建客户和订单,客户关联订单,保存客户?

Session session = factory.openSession();

session.beginTransaction();

 

//1 客户和订单

Customer customer = new Customer();

customer.setCname("成成");

 

Order order = new Order();

order.setPrice("998");

 

//2 客户关联订单

customer.getOrderSet().add(order);

 

//3 保存客户

session.save(customer);

 

session.getTransaction().commit();

session.close();

}

 

 

 

2.2.4 双向关联,使用inverse映射

 

 

@Test

public void demo04(){

// 4  创建客户和订单,客户关联订单,订单也关联客户,保存客户和订单? 

// * 开发中优化程序 , n + 1 问题?

// ** 解决方案1:客户不关联订单 ,不建议

// ** 解决方案2:客户放弃对订单表外键值的维护。

// **** Customer.hbm.xml <set name="orderSet" inverse="true">

// ** inverse 将维护外键值的权利交予对象。相当于自己放弃。(反转)

Session session = factory.openSession();

session.beginTransaction();

 

//1 客户和订单

Customer customer = new Customer();

customer.setCname("成成");

 

Order order = new Order();

order.setPrice("998");

 

//2 客户关联订单

customer.getOrderSet().add(order);

//3 订单也关联客户

order.setCustomer(customer);

 

//4 保存客户

// * 1 save(order) -- insert  --> 1,998 null

// * 2 订单管理客户,此时null --预留update --> 更新所有(正常设置)

// * 3 save(customer) -- insert --> 1,成成

// * 4 客户关联订单  --> 预留update --> 更新订单外键 (维护外键)

// * 5 提交commit --> 执行和 

session.save(order);

session.save(customer);

 

session.getTransaction().commit();

session.close();

}

 

l 在一对多开发中,一方一般都放弃对外键值的维护。及<set inverse="true

 

 

 

2.3 级联操作(读、理解)

2.3.1 save-update  级联保存或更新

 

 

@Test

public void demo032(){

// 32 创建客户和订单,客户关联订单,保存客户? --抛异常

// ** 解决方案2:级联操作--级联保存或更新

// ** Customer.hbm.xml <set cascade="save-update">

// ** 在保存客户的同时,一并保存订单

Session session = factory.openSession();

session.beginTransaction();

 

//1 客户和订单

Customer customer = new Customer(); //瞬时态

customer.setCname("成成");

 

Order order = new Order(); //瞬时态

order.setPrice("998");

 

//2 客户关联订单

customer.getOrderSet().add(order);

 

//3 保存客户

session.save(customer); //持久态

// 关联操作都是持久态的,此时 持久态Customer 引用 一个 瞬时态的Order 抛异常

 

session.getTransaction().commit();

session.close();

}

 

2.3.2 delete 级联删除

 

@Test

public void demo05(){

// 5 查询客户,并删除(持久态)

// 默认:当删除客户,默认将订单外键设置成null

// 级联删除:删除客户时,并将客户的订单删除。

// ** Customer.hbm.xml <set name="orderSet" cascade="delete">

Session session = factory.openSession();

session.beginTransaction();

 

Customer customer = (Customer) session.get(Customer.class, 10);

 

session.delete(customer);

 

 

session.getTransaction().commit();

session.close();

}

 

2.3.3 孤儿删除

l 一对多关系,存在父子关系。1表(主表)可以成为父表,多表(从表)也可以子表。

 

 

总结:

主表不能删除,从表已经引用(关联)的数据

从表不能添加,主表不存在的数据。

 

 

@Test

public void demo06(){

// 6 查询客户,并查询订单,解除客户和订单订单的关系

// * 默认:客户和订单解除关系后,外键被设置成null,此时订单就是孤儿。客户和订单都存在。

// * 孤儿删除(孤子删除),当订单称为孤儿,一并删除。客户仍存在。

Session session = factory.openSession();

session.beginTransaction();

 

//1 查询客户

Customer customer = (Customer) session.get(Customer.class, 9);

 

//2查询订单

Order order = (Order) session.get(Order.class, 8);

 

//3 解除关系

customer.getOrderSet().remove(order);

 

session.getTransaction().commit();

session.close();

}

 

2.3.4 总结

save-updateA保存,同时保存B

delete:删除A,同时删除BAB都不存在

delete-orphan:孤儿删除,解除关系,同时将B删除,A存在的。

如果需要配置多项,使用逗号分隔。<set cascade="save-update,delete">

 

all : save-update 和 delete 整合

all-delete-orphan : 三个整合

 

 

1.1.1 Hibernate的关联关系映射:(多对多)

1.1.1.1 多对多的配置:

:

Student:

public class Student {

private Integer sid;

private String sname;

// 学生选择多门课程.

private Set<Course> courses = new HashSet<Course>();

...

}

 

Course:

public class Course {

private Integer cid;

private String cname;

// 课程可以被多个学生选择:

private Set<Student> students = new HashSet<Student>();

...

}

 

Student.hbm.xml

<hibernate-mapping>

<class name="cn.itcast.demo3.Student" table="student">

<id name="sid" column="sid">

<generator class="native"/>

</id>

<property name="sname" column="sname"/>

<!-- 配置多对多关联关系 -->

<set name="courses" table="stu_cour">

<key column="sno"/>

<many-to-many class="cn.itcast. demo3.Course" column="cno"/>

</set>

</class>

</hibernate-mapping>

 

Course.hbm.xml

<hibernate-mapping>

<class name="cn.itcast. demo3.Course" table="course">

<id name="cid" column="cid">

<generator class="native"/>

</id>

 

<property name="cname" column="cname"/>

 

<!-- 配置多对多关联关系映射 -->

<set name="students" table="stu_cour">

<key column="cno"/>

<many-to-many class="cn.itcast. demo3.Student" column="sno"/>

</set>

</class>

</hibernate-mapping>

 

抓取策略(优化)

2.1 检索方式

l 立即检索:立即查询,在执行查询语句时,立即查询所有的数据。

l 延迟检索:延迟查询,在执行查询语句之后,在需要时在查询。(懒加载)

 

2.2 检查策略

l 类级别检索:当前的类的属性获取是否需要延迟。

l 关联级别的检索:当前类 关联 另一个类是否需要延迟。

 

 

2.3 类级别检索

l get:立即检索。get方法一执行,立即查询所有字段的数据。

l load:延迟检索。默认情况,load方法执行后,如果只使用OID的值不进行查询,如果要使用其他属性值将查询 。 Customer.hbm.xml  <class  lazy="true | false">

lazy 默认值true,表示延迟检索,如果设置false表示立即检索。

 

 

@Test

public void demo02() {

//类级别

Session session = factory.openSession();

session.beginTransaction();

 

//1立即

// Customer customer = (Customer) session.get(Customer.class, 1);

//2延迟

Customer customer = (Customer) session.load(Customer.class, 1);

 

 

 

//打印

System.out.println(customer.getCid());

System.out.println(customer.getCname());

 

session.getTransaction().commit();

session.close();

}

 

 

 

2.4 关联级别检索

2.4.1 一对多或多对多

2.4.1.1 介绍

l 容器<set> 提供两个属性:fetchlazy

fetch:确定使用sql格式

lazy:关联对象是否延迟。

l fetchjoinselectsubselect

join:底层使用迫切左外连接

select:使用多个select语句(默认值)

subselect:使用子查询

l lazyfalsetrueextra

false:立即

true:延迟(默认值)

extra:极其懒惰

 

 

2.4.1.2 fetch="join"

l fetch="join" lazy无效。底层使用迫切左外连接,使用一条select将所有内容全部查询。

 

@Test

public void demo03() {

//关联级别:一对多,

// * Customer.hbm.xml  <set fetch="join">

// *** select语句使用左外连接,一次性查询所有

Session session = factory.openSession();

session.beginTransaction();

 

//1 查询客户

Customer customer = (Customer) session.get(Customer.class, 1);

System.out.println(customer.getCname());

 

//2 查询客户订单数

Set<Order> orderSet = customer.getOrderSet();

System.out.println(orderSet.size());

 

//3 查询客户订单详情

for (Order order : orderSet) {

System.out.println(order);

}

 

 

session.getTransaction().commit();

session.close();

}

 

2.4.1.3 fetch="select"

l 当前对象 和 关联对象 使用多条select语句查询。

l lazy="false" , 立即,先查询客户select,立即查询订单select

l lazy="true",延迟,先查询客户select,需要订单时,再查询订单select

l lazy="extra",极其懒惰(延迟),先查询客户select, 如果只需要订单数,使用聚合函数(不查询详情)

 

 

 

 

 

 

2.4.1.4 fetch="subselect"

l 将使用子查询。注意:必须使用Query否则看不到效果。

l lazy= 同上

@Test

public void demo04() {

//关联级别:一对多,

// 演示3* Customer.hbm.xml  <set fetch="subselect">

Session session = factory.openSession();

session.beginTransaction();

 

//1 查询客户

List<Customer> allCustomer = session.createQuery("from Customer").list();

Customer customer = allCustomer.get(0);

System.out.println(customer.getCname());

 

//2 查询客户订单数

Set<Order> orderSet = customer.getOrderSet();

System.out.println(orderSet.size());

 

//3 查询客户订单详情

for (Order order : orderSet) {

System.out.println(order);

}

 

 

session.getTransaction().commit();

session.close();

}

 

 

 

 

 

 

2.4.2 多对一

2.4.2.1 介绍

l <many-to-one fetch="" lazy="">  (<one-to-one>)

l fetch取值:joinselect

join:底层使用迫切左外连接

select:多条select语句

l lazy取值:falseproxyno-proxy

false:立即

proxy:采用关联对象 类级别检索的策略。

订单 关联 客户 (多对一)

订单 立即获得 客户,需要在客户Customer.hbm.xml <class lazy="false"> 

订单 延迟获得 客户,需要在客户Customer.hbm.xml <class lazy="true"> 

 

no-proxy 不研究

 

 

 

2.4.2.2 fetch="join"

l fecth="join" select语句使用左外连接,此时lazy无效。

 

 

 

@Test

public void demo05() {

//关联级别:多对一,

// 演示1* Order.hbm.xml  <set fetch="join">  lazy无效

// * 注意:检查Customer.hbm.xml 和 Order.hbm.xml 没有额外的配置

Session session = factory.openSession();

session.beginTransaction();

 

//1 查询订单

Order order = (Order) session.get(Order.class, 1);

System.out.println(order.getPrice());

 

//2 查询订单客户信息

Customer customer = order.getCustomer();

System.out.println(customer.getCname());

 

 

session.getTransaction().commit();

session.close();

}

2.4.2.3 fetch="select"

l 将采用多条select语句,lazy="proxy"是否延迟,取决关联对象 类级别检索策略。

 

l lazy="false"

 

l lazy="proxy"

 

 

 

2.5 批量查询

l 当客户 关联查询 订单,给每一个客户生产一个select语句查询订单。批量查询使用in语句减少查询订单语句个数。

默认:select * from t_order where customer_id = ?

批量:select * from t_order where customer_id in (?,?,?,?)

l <set batch-size="5"> 5表示括号中?个数。

 

 

@Test

public void demo06() {

//批量查询

Session session = factory.openSession();

session.beginTransaction();

 

//1 查询所有客户

List<Customer> allCustomer = session.createQuery("from Customer").list();

 

//2遍历

for (Customer customer : allCustomer) {

System.out.println(customer.getCname());

System.out.println(customer.getOrderSet().size());

}

 

 

session.getTransaction().commit();

session.close();

}

2.6 检索总结

 

检索策略

优点

缺点

优先考虑使用的场合

立即检索

对应用程序完全透明,不管对象处于持久化状态还是游离状态,应用程序都可以从一个对象导航到关联的对象

(1)select语句多

(2)可能会加载应用程序不需要访问的对象,浪费许多内存空间。

(1)类级别

(2)应用程序需要立即访问的对象

(3)使用了二级缓存

延迟检索

由应用程序决定需要加载哪些对象,可以避免执行多余的select语句,以及避免加载应用程序不需要访问的对象。因此能提高检索性能,并节省内存空间。

应用程序如果希望访问游离状态的代理类实例,必须保证她在持久化状态时已经被初始化。

(1)一对多或者多对多关联

(2)应用程序不需要立即访问或者根本不会访问的对象

 

表连接检索

(1)对应用程序完全透明,不管对象处于持久化状态还是游离状态,都可从一个对象导航到另一个对象。

(2)使用了外连接,select语句少

(1)可能会加载应用程序不需要访问的对象,浪费内存。

(2)复杂的数据库表连接也会影响检索性能。

(1)多对一或一对一关联

(2)需要立即访问的对象

(3)数据库有良好的表连接性能。

 

Customer  Get(int id)

Return Session.load(Customer.class,id);

  1. layz=false
  2. Service层获得在页面要上要用到的属性=> Service层中确保数据已经

查询方式总结

1.通过OID检索(查询)

get()立即、如果没有数据返回null

load()延迟,如果没有数据抛异常。

2.导航对象图检索方式:关联查询

customer.getOrderSet()

user.getPost().getDepartment().getDepName();

3.原始sql语句

SQLQuery sqlQuery = session.createSQLQuery("sql 语句")   --->表,表字段(列)

sqlQuery.list() 查询所有

sqlQuery.uniqueResult() 查询一个

4.HQLhibernate query language hibernate 查询语言【1

Query query = session.createQuery("hql语句")  --> 对象,对象属性

5.QBCquery by criteria 纯面对对象查询语言【2

 

posted @ 2017-07-24 14:48  白夜梦想家  阅读(192)  评论(0编辑  收藏  举报