1.一对多关联关系
* 单项关联:仅仅建立从Order到Customer的多对一关联,即仅仅在Order类中定义customer属性。或者仅仅建立从Customer到Order的一对多关联,即仅仅在Customer类中定义orders集合
* 双项关联:既建立从Order到Customer的多对一关联,又建立从Customer到Order的一对多关联
<many-to-one name="customer" class="cn.itcast.many2one.Customer" not-null="true">
<!-- 映射的表中的列 -->
<column name="customer_id"/>
</many-to-one>
* customer要获取的是Customer对象,该Customer对象通过orders表的外键customer_id来查询customers表
* many-to-one:使用该标签来映射多对一关联:
name:映射的持久化类中的属性
class:映射的持久化类中的属性的类型 not-null:是否允许为空
2.先保存订单,再保存客户
@Test
public void saveOrderAndCustomer(){
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
Customer c=new Customer();
c.setName("关羽");
Order o=new Order();
o.setOrderNumber("NO1");
o.setPrice(12d);
//建立订单和客户的单向关联
o.setCustomer(c);
//保存订单
session.save(o);
//保存客户
session.save(c);
tx.commit();
session.close();
}
* 先插入订单 外键列为null 再插入客户 最后更新订单表外键
* 先插入订单时,客户还不存在,所以外键只能最后更新
* 建立表时,也是也把两张表建立,再把外键约束更新上去
3.先保存客户,再保存订单
@Test
public void saveCustomerAndOrder(){
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
Customer c=new Customer();
c.setName("张飞");
Order o=new Order();
o.setOrderNumber("NO2");
o.setPrice(12d);
//建立订单和客户的单向关联
o.setCustomer(c);
//保存客户
session.save(c);
//保存订单
session.save(o);
tx.commit();
session.close();
}
* 先插入客户,再插入订单,外键一起插入
4.查询订单
@Test
public void getOrder(){
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
Order o=(Order)session.get(Order.class, 2);
System.out.println(o.getId()+" "+o.getOrderNumber()+" "+o.getPrice());
System.out.println(o.getCustomer().getId()+" "+o.getCustomer().getName());
tx.commit();
session.close();
}
* 查询时是分开查询的 不是左外查询
5.先客户,再保存订单在下面的代码中注释掉session.save(c),会有什么后果
@Test
public void saveCustomerAndOrderCase(){
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
//临时对象
Customer c=new Customer();
c.setName("张飞");
//临时对象
Order o=new Order();
o.setOrderNumber("NO2");
o.setPrice(12d);
//建立订单和客户的单向关联
o.setCustomer(c);
//不保存客户
//session.save(c);
//保存订单
session.save(o);
tx.commit();
session.close();
}
* 会抛出一个临时对象异常 org.hibernate.TransientObjectException
* 保存订单时 order对象变为持久对象,而与order对象关联的customer对象仍然是临时对象
* session.save(c) 把对象保存在一级缓存里,把临时对象变为持久对象
* 提交事务 刷新一级缓存
* 持久对象 在数据库中有对应的记录
6.级联保存和更新
<many-to-one name="customer" class="cn.itcast.many2one.Customer" cascade="save-update">
<!-- 映射的表中的列 -->
<column name="customer_id"/>
</many-to-one>
* 当hibernate持久化一个临时对象时,在默认情况下,他不会自动持久化所关联的其他临时对象,会抛出TransientObjectException.如果设定many-to-one元素的cascade属性为save-update的话,可实现自动持久化所关联的对象
* 就是将order对象所关联的临时对象Customer变成持久对象(就在session的一级缓存中,就能插入到数据库中)
* 此时先插入的是客户,再插入订单,外键跟随订单一起插入
7.建立一对多的双向关联关系
* 双向 1-n 与 双向 n-1 是完全相同的两种情形
//一个客户对应多个订单
private Set<Order> orderes=new HashSet<Order>(0);
* new 一个HashSet避免空指针异常
后面加个0 一开始Set集合没有东西 长度为0
* 用Set集合不用List集合是因为 Set存储元素无序 不可以重复 而 查询的订单 有主键约束 都不是重复的 所以用Set集合
<set name="orderes" table="orders" >
<key>
<!-- 对应的是orders表的外键,可以理解为, orderes集合中的订单对象是通过orders表的外键customer_id查询出来的
select * from orders where cutomer_id=客户id 结构是多条记录
-->
<column name="customer_id"/>
</key>
<!--
one-to-many:表示一对多,class表示集合中存放的对象是Order对象
-->
<one-to-many class="cn.itcast.many2onedouble.Order"/>
</set>
* 配置set集合
set:使用set标签配置客户对应的订单集合
table:订单集合中的订单对应的表,可以不加
* 由于外键在orders表 所以加个 table属性 不过可以不加
* javaBean定义的orderes集合 只是知道他存储的是Order 而程序不知道 所以指定 class属性
* order在mysql关键字 native在MyEclipse关键字
8.保存客户和订单(客户和订单建立双向关联)
@Test
public void saveCustomerAndOrder(){
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
Customer c=new Customer();
c.setName("关羽");
Order o=new Order();
o.setOrderNumber("NO1");
o.setPrice(12d);
//建立订单和客户的双向关联
//建立订单和客户的关联
o.setCustomer(c);
//建立客户和订单的关联
c.getOrderes().add(o);
//保存客户
session.save(c);
//保存订单
session.save(o);
tx.commit();
session.close();
}
* 双向关联 先保存客户 再保存订单 最后有一条更新语句
Hibernate: insert into customers (name, id) values (?, ?)
Hibernate: insert into orders (orderNumber, price, customer_id, id) values (?, ?, ?, ?)
Hibernate: update orders set customer_id=? where id=?
* 双向关联 先保存订单 再保存客户 最后有两条更新语句
Hibernate: insert into orders (orderNumber, price, customer_id, id) values (?, ?, ?, ?)
Hibernate: insert into customers (name, id) values (?, ?)
Hibernate: update orders set orderNumber=?, price=?, customer_id=? where id=?
Hibernate: update orders set customer_id=? where id=?
9.保存客户和不保存订单
* 无论是保存客户和不保存订单或者是保存订单不保存客户都会抛出临时对象异常 在配置文件加上cascade="save-update"就可以了 原因同上
* 改成级联 无论是保存客户和不保存订单或者是保存订单不保存客户都是3条执行语句Hibernate: insert into customers (name, id) values (?, ?)
Hibernate: insert into orders (orderNumber, price, customer_id, id) values (?, ?, ?, ?)
Hibernate: update orders set customer_id=? where id=?
* 上面单向关联 级联的时候 无论保存顺序都是两条语句 先插入客户 再插入订单
10.保持程序的健壮性
* 为了保持程序的健壮性,最好是建立双向关联
11.定义为接口类型
* 传递的参数是Hibernate自定义的实现该接口类的实例。如果定义成类(如HashSet)型,强迫hibernate把该类型的实例传给他
* 传递过来的参数是PersistentSet,class PersistentSet implements java.util.Set
* 通常在定义集合属性时,直接初始化为一个实现类的实例。private Set orders = new HashSet(0);可避免空指针异常
12.订单变更客户
@Test
public void changeOrderHander(){
Session session=sf.openSession();
Transaction tx=session.beginTransaction();
Order o6=(Order)session.get(Order.class, 6);
Customer c4=(Customer)session.get(Customer.class, 4);
o6.setCustomer(c4);
c4.getOrderes().add(o6);
tx.commit();
session.close();
}
Hibernate: update orders set orderNumber=?, price=?, customer_id=? where id=?
Hibernate: update orders set customer_id=? where id=?
* 没有update语句怎么会有update语句呢? 从数据库查询出来的结果,一级缓存有一份 快照也有一份 当缓存中的持久化对象属性变化的时候,清理一级缓存时,一级缓存与快照一对比,发现有属性不一样,就会产生update语句 tx.commit()先执行session.flush()方法 再提交 提交相当于盖一个戳 盖了这个戳 数据库才会有数据
* 这里没有级联也可以执行 因为两个对象都是临时对象 不会报临时对象异常
* 为什么会有两条update语句呢? 因为没有设置inverse属性 所以不知道以谁为主动方 当以o6为主动方时,o6.setCustomer(c4); 清理缓存,与快照对比,属性发生变化,c4对象和快照对比,没有发生变化,所以只有一条update语句,当以c4为主动方时,c4.getOrderes().add(o6); 清理缓存,c4的集合属性发生变化,o6没有变化,所以只有一条update语句
* 在hibernate中通过对 inverse 属性的值决定是由双向关联的哪一方来维护表和表之间的关系. inverse=false 的为主动方,inverse=true 的为被动方, 由主动方负责维护关联关系
* inverse 反转 false就是不反转就是主动方
* 在没有设置 inverse=true 的情况下,默认情况,父子两边都维护父子关系 默认就是inverse=false
* 在 1-N 关系中,若将 1 方设为主控方 会额外多出 update 语句 一般都是以多的一方为主控方
* 如果一的一端对应的订单集合中的订单发生变化时,
因为一的一端说了不算,所以不会按照订单集合中订单的变化执行update语句
* 在映射一对多的双向关联关系时,应该在one方把inverse属性设为true,这可以提高性能
* 建立双向关联,这样才会使程序更加健壮,提高业务逻辑层的独立性,使业务逻辑层的程序代码
不受Hibernate实现类的影响。同理,当删除双向关联的关系时,也应该修改
关联两端的对象的相应属性
13.级联删除删除1号客户的同时,删除1号客户所关联的订单
Could not execute JDBC batch update
Cannot delete or update a parent row: a foreign key constraint fails (`test/orders`, CONSTRAINT `FKC3DF62E5DF4F9543` FOREIGN KEY (`customer_id`) REFERENCES `customers` (`id`))
* 有外键关联 不能删除
* 如果cascade属性取默认值none,不会自动删除和customer关联的其他持久化对象。如果希望删除customer时,自动删除和customer关联的order对象,可把cascade属性设为delete
14.解除关联关系 ---父子关系 解除6号订单和3号客户的关联,同时删除6号订单
* 如果希望程序自动删除不再和customer关联的order对象,可以把cascade属性设为
all-delete-orphan 或 delete-orphan 默认是null 什么都不干
15.在数据库中对集合排序
* <set> 元素有一个 order-by 属性, 如果设置了该属性, 当 Hibernate 通过 select 语句到数据库中检索集合对象时, 利用 order by 子句进行排序
* order-by 属性中还可以加入 SQL 函数 order-by="id desc"