一、概述
例如,以客户(Customer)和订单(Order)为例,一个客户能有多个订单,一个订单只能有一个客户。
从Customer到Order是一对多关联,在java类中的面向对象设计应该一个Customer对象包含多个Order对象,因此应该定义一个集合,来包含所有的Order对象。
从Order到Customer是多对一关联,在java类中设计每个Order对象需要关联一个Customer对象,因此Order类中应该定义一个Cutomer类型的属性,来引用关联的customer对象。
但是在关系数据库中,只存在主外键参照关系来表达两者的关联。
二、实例
(1)先创建两个实体类 Customer和Order
public class Customer {
private Integer id;
private String name;
private Set <Order> orders=new HashSet<Order>();
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set getOrders() {
return orders;
}
public void setOrders(Set orders) {
this.orders = orders;
}
}
public class Order {
private Integer id;
private String name;
private Customer customer;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
(2)实体类写好了,使用Hibernate映射文件来映射关系
创建Customer.hbm.xml:
<hibernate-mapping >
<!--指定实体类和表的映射关系-->
<class name="com.cad.domain.Customer" table="customer">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name" column="name"></property>
<!--使用<set>元素来映射set集合类型-->
<!--
name:持久化类中的属性名
-->
<set name="orders">
<!--<key>元素设定所关联的持久化类对应的表的外键-->
<!--<one-to-many>元素设定关联的持久化类-->
<key column="cid"/>
<one-to-many class="com.cad.domain.Order"/>
</set>
</class>
</hibernate-mapping>
Hibernate根据映射文件获得以下信息
-<set>元素表明Customer类中的orders属性为java.util.Set集合
-<one-to-many>元素表明orders集合中存放的是一组order对象
-<key>元素表明orders表通过外键cid关联customer表
创建Order.hbm.xml
<hibernate-mapping >
<class name="com.cad.domain.Order" table="order">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name" column="name"></property>
<!--<many-to-one>元素建立了customer属性和对应表中外键的映射-->
<!--
name:持久化类中的属性名
column:表中的外键
class:关键的Customer对象实现类
-->
<many-to-one name="customer" column="cid" class="com.cad.domain.Customer"></many-to-one>
</class>
</hibernate-mapping>
(3)在hibernate.cfg.xml中配置映射文件
<mapping resource="com/cad/domain/Customer.hbm.xml"/>
<mapping resource="com/cad/domain/Order.hbm.xml"/>
(4)测试
//添加方法
@Test
public void test() {
//读取配置文件
Configuration conf=new Configuration().configure();
//根据配置创建factory
SessionFactory sessionfactory=conf.buildSessionFactory();
session = sessionfactory.openSession();
Transaction ts=session.beginTransaction();
//创建Customer
Customer c=new Customer();
c.setName("张三");
//创建订单
Order o1=new Order();
o1.setName("矿泉水");
Order o2=new Order();
o2.setName("方便面");
//双向关联
c.getOrders().add(o1);
c.getOrders().add(o2);
o1.setCustomer(c);
o2.setCustomer(c);
//保存
session.save(c);
session.save(o1);
session.save(o2);
ts.commit();
session.close();
sessionfactory.close();
}
然后执行,控制台会打印如下语句
Hibernate:
insert
into
customer
(name)
values
(?)
Hibernate:
insert
into
orders
(name, cid)
values
(?, ?)
Hibernate:
insert
into
orders
(name, cid)
values
(?, ?)
Hibernate:
update
orders
set
cid=?
where
id=?
Hibernate:
update
orders
set
cid=?
where
id=?
我们来执行删除操作,直接删除Customer,但由于还有Order关联着Customer 会执行成功么?
@Test
public void test() {
//读取配置文件
Configuration conf=new Configuration().configure();
//根据配置创建factory
SessionFactory sessionfactory=conf.buildSessionFactory();
session = sessionfactory.openSession();
Transaction ts=session.beginTransaction();
Customer c=session.get(Customer.class, 2);
//删除Customer
session.delete(c);
ts.commit();
session.close();
sessionfactory.close();
}
执行,打印如下语句
Hibernate:
select
customer0_.id as id1_0_0_,
customer0_.name as name2_0_0_
from
customer customer0_
where
customer0_.id=?
Hibernate:
update
orders
set
cid=null
where
cid=?
Hibernate:
delete
from
customer
where
id=?
我们会发现Hibernate会自动将Order中的cid设置为null,然后执行删除操作
我们发现Hibernate还是挺智能的,但这是由inverse属性操控的。
三、< set >元素的inverse属性
inverse所描述的是对象之间关联关系的维护方式。 inverse属性指定由哪方来维护关联关系。
inverse默认为false,即关联关系由自己控制,若为true,则反转,关联关系由对方控制.
Inverse属性的作用是:是否将对集合对象的修改反映到数据库中。
在映射一对多的双向关联关系中,应该在"一"方把inverse属性设为true,由对方来维护主键关联关系.
所以上述例子中,inverse默认是false.即Customer维护关联关系,所以Customer会执行两条更新语句来更新Order的cid.
但是我们Order在插入的时候已经插入cid,所以这样会影响性能,我们只需要将Customer的<set>元素的inverse属性改为true即可。
我们的删除案例中也是,Customer执行删除时,会先去把Order的主键约束解除,然后删除。
我们只需要将Customer的inverse设置为true,然后由对方维护关联关系,我们再进行删除时,就会出现异常,因为有主键约束,我们Customer不再维护关联关系。
四、级联操纵
在实际应用中,对象和对象之间是相互关联的。例如我们的一对多关联关系。
在关系-对象映射文件中,用于映射持久化类之间关联关系的元素,如 < set>,< many-to-one>,< one-to-many>,都有一个cascade属性,用来指定如何操纵与当前对象关联的其他对象。
我们先看下面的例子,我们创建一个Customer,再创建两个Order,然后关联
我们只保存Customer,会抛出org.hibernate.TransientObjectException异常,这是为什么呢?
这是因为我们的Customer的inverse为false,关联关系由Customer维护。我们保存Customer时,
会维护Customer中orders中的所有Order的主键,但是Order是临时对象,并没有转变为持久状态,这时候就会抛出异常。
@Test
public void test() {
//读取配置文件
Configuration conf=new Configuration().configure();
//根据配置创建factory
SessionFactory sessionfactory=conf.buildSessionFactory();
session = sessionfactory.openSession();
Transaction ts=session.beginTransaction();
Customer c=new Customer();
c.setName("jack");
Order o1=new Order();
o1.setName("苹果");
Order o2=new Order();
o2.setName("香蕉");
c.getOrders().add(o1);
c.getOrders().add(o2);
o1.setCustomer(c);
o2.setCustomer(c);
session.save(c);
ts.commit();
session.close();
sessionfactory.close();
}
当Hibernate持久化一个临时对象时,并不会自动持久化所关联的其他临时对象,所以会抛出异常。
如果我们希望Hibernate持久化对象时自动持久化所关联的其他对象,那么就需要指定cascade属性
(1)级联保存和更新
当我们持久化对象时自动持久化所关联的其他对象。
把cascade属性设置为save-update ,这时候我们再执行上面的代码就会自动帮我们保存Customer关联的Order对象。
当cascade属性为save-update时,表明保存或更新当前对象时,会级联保存或更新与它关联的对象。
(2)级联删除
如果我们的cascade属性为delete时,我们删除当前对象,会自动删除与之关联的对象。
慎用这个delete属性。
例如:我们的Order配置了这个属性,Customer也配置了这个属性,我们删除订单时,因为是级联删除
所以会查找Customer,删除Customer,但Customer也配置了级联删除,所以会查找所有关联的订单,最后会删除该客户的所有订单和该客户。
(3)孤儿删除
如果我们的对象和关联的对象解除关系后,希望自动删除不再关联的对象。
需要将cascade设置为delete-orphan.
例如 ,我们设置cascade="delete-orphan"
Transaction ts=session.beginTransaction();
Customer c=session.get(Customer.class, 7);
Order order=(Order) c.getOrders().iterator().next();
c.getOrders().remove(order);
order.setCustomer(null);
ts.commit();
我们解除Customer和Order的关系,Hibernate就会自动删除Order。
当cascade的值为all时,是save-update和delete的整合。
当cascade的值为all-dalete-orphan时,是all和delete-orphan的整合。