Hibernate的关联关系映射
三种实体关系分别是:
- 一对多:在多的一方,添加一的一方的主键作为外键
- 多对多:产生中间关系表,引入两张表的主键作为外键,两个主键称为联合主键。
- 一对一:在任意一方引入对方主键作为外键(开发中使用非常少,基本都合成一张表了)
一对多关联关系映射
双向关联
一对多映射关系是由“多”的一方指向“一”的一方。在表示“多”的一方数据表中增加一个外键,来指向“一”的一方的数据表,“一”的一方作为主表,而“多”的一方作为从表。
一个客户可以有多个订单。
public class Customer{
private Integer id;
private String name;
private Set<Order> orders=new HashSet<Order>();//用户有多个订单
//这里省略了get,set方法
...
}
public class Order {
private Integer id;
private String address;
private Double price;
private Customer customer;//订单属于某一个用户
//这里省略了get,set方法
...
}
Order.hbm.xml
<!--多对一关系映射-->
<many-to-one name="customer" class="cn.eaglezsx.Customer" column="cid"/>
name属性用来描述customer在Order类中的属性的名称,class属性用来指定映射的类,column属性值对应表中的外键列名,可以任意取名。
Customer.hbm.xml
<!--一对多关系映射-->
<set name="orders">
<key column="cid"/>
<one-to-many class="cn.eaglezsx.Order"/>
</set>
public class OneToManyTest{
@Test
public void test(){
...
//1.创建一个客户
Customer c=new Customer();
c.setName("张三");
//2.创建两个订单
Order o1=new Order();
o1.setAddress("北京");
o1.setPrice("1000d");
Order o2=new Order();
o2.setAddress("广州");
o2.setPrice(800d);
//3.描述关系---订单属于某个客户
o1.setCustomer(c);
o2.setCustomer(c);
//4.描述关系---客户有多个订单
c.getOrders().add(o1);
c.getOrders().add(o2);
//5.先存客户再存订单
session.save(c);
session.save(o1);
session.save(o2);
...
}
}
运行程序,控制台输出的SQL语句为
Hibernate: insert into customer (name) values (?)
Hibernate: insert into orders (address, price, cid) values (?, ?, ?)
Hibernate: insert into orders (address, price, cid) values (?, ?, ?)
Hibernate: update orders set cid=? where id=?
Hibernate: update orders set cid=? where id=?
三条insert语句,两条update语句。后两条insert语句已经对cid进行设置了,后边的update又设置了一遍。
使用双向关联会执行两遍SQL语句,比较浪费资源。
单向关联
删除Order类中的Customer字段,Order.hbm.xml中配置的信息,测试类中的o1.setCustomer(c);和 o2.setCustomer(c);。
运行程序,控制台的打印信息为
Hibernate: insert into customer (name) values (?)
Hibernate: insert into orders (address, price) values (?, ?)
Hibernate: insert into orders (address, price) values (?, ?)
Hibernate: update orders set cid=? where id=?
Hibernate: update orders set cid=? where id=?
后两个insert语句不再对cid进行操作。
或者删除Customer类中的orders字段,Customer.hbm.xml中配置的信息,测试类中的c.getOrders().add(o1);和c.getOrders().add(o2);。
运行程序,控制台的打印信息为
Hibernate: insert into customer (name) values (?)
Hibernate: insert into orders (address, price, cid) values (?, ?, ?)
Hibernate: insert into orders (address, price, cid) values (?, ?, ?)
只有三个insert语句,没有update语句。
反转操作
如果还是用双向关联,但不想产生多余的SQL语句,可以在配置文件中设置一下让一方放弃对关联关系的控制。
在一对多关系中,会将一的一方的inverse属性设置为true,即由多的一方来维护关系。
在Customer.hbm.xml文件中,给set元素添加inverse属性,设置为true。该属性默认值false,两端都能控制关系。
<set name="orders" inverse="true">
<key column="cid"/>
<one-to-many class="cn.itcast.hibernate3.demo1.Order"/>
</set>
测试类中的c.getOrders().add(o1);和c.getOrders().add(o2);也可以删除了。
运行程序,控制台的打印信息为
Hibernate: insert into customer (name) values (?)
Hibernate: insert into orders (address, price, cid) values (?, ?, ?)
Hibernate: insert into orders (address, price, cid) values (?, ?, ?)
在一对多关系中inverse只能设置在一的一方,many-to-one元素没有此属性。
级联操作
级联操作是指当主控方执行保存、更新或者删除操作时,其关联对象(被控方)也执行相同的操作。
更改Customer.hbm.xml,给set元素添加cascade属性
<set name="orders" cascade="save-update">
<key column="cid">
<one-to-many class="cn.eaglezsx.Order"/>
</set>
这时可以删除session.save(o1);和session.save(o2);这两句话。保存客户的时候也会把订单保存了。
- save-update:在执行save、update或saveOrUpdate时进行关联操作。
- delete:在执行delete时进行关联操作
- delete-orphan
- all
- all-delete-orphan
- none
级联操作:简化操作,目的就是为了少写两行代码(不用也行)。用的话可以用save-update,不建议使用delete
级联操作可以设置在一的一方也可以设置在多的一方。在多对多中,不用使用delete,比如我把课程删了,对应的学生也会被删除,这样不合逻辑。
多对多关联关系映射
学生和课程之间存在多对多的关系。
Student.java
public class Student {
private Integer id;
private String sname;
private Set<Course> courses=new HashSet<Course>();
//此处省略get和set方法
...
}
Course.java
public class Course {
private Integer id;
private String cname;
private Set<Student> students=new HashSet<Student>();
//此处省略get和set方法
...
}
Student.hbm.xml
<set name="courses" table="s_c">
<key column="sid"/>
<many-to-many class="cn.itcast.hibernate3.demo1.Course" column="cid"/>
</set>
set元素多了个table属性,这个属性表示中间表的名称。key标签的column属性用于描述student表在中间表中的外键列名称。many-to-many标签中的column属性描述course课程表在中间表中的列名。
Course.hbm.xml
<set name="students" table="s_c">
<key column="cid"/>
<many-to-many class="cn.itcast.hibernate3.demo1.Student" column="sid"/>
</set>
ManyToManyTest.java
public class ManyToManyTest {
@Test
public void test(){
...
//1.创建两个学生
Student s1=new Student();
s1.setSname("张三");
Student s2=new Student();
s2.setSname("李四");
//2.创建两个科目
Course c1=new Course();
c1.setCname("Java");
Course c2=new Course();
c2.setCname("C#");
//3.建立关联关系
//学生关联科目
s1.getCourses().add(c1);
s2.getCourses().add(c1);
s1.getCourses().add(c2);
s2.getCourses().add(c2);
//4.存储
session.save(c1);
session.save(c2);
session.save(s1);
session.save(s2);
...
}
}
运行程序,控制台输出
Hibernate: insert into course (cname) values (?)
Hibernate: insert into course (cname) values (?)
Hibernate: insert into student (sname) values (?)
Hibernate: insert into student (sname) values (?)
Hibernate: insert into s_c (sid, cid) values (?, ?)
Hibernate: insert into s_c (sid, cid) values (?, ?)
Hibernate: insert into s_c (sid, cid) values (?, ?)
Hibernate: insert into s_c (sid, cid) values (?, ?)