一、 表和表之间的关系
1. 一对多
顾客和商品关系,一个顾客可以选购多件商品。
一对多关系:通过外键建表
在1的一方的实体类中,使用集合表达持有多的一方的引用。
private Set<Order> orders = new HashSet<Order>();
在多的一方,引入1的一方:
private Customer customer;
2. 多对多
一个学生可以选择多门课程,一门课程可以有多个学生选择。
多对多关系:使用中间表进行表示
在两个实体类中,使用集合引入多的一方
private Set<Course> courses = new HashSet<Course>();
private Set<Course> courses = new HashSet<Course>();
3. 一对一
一个公司只有一个总部,一个总部对应一个公司。
一对一关系有两种配置:方式一:外键生成策略 方式二:主键同步策略
二、 一对多操作
1. 一对多关系配置
创建实体类
Customer是1的一方,Order是多的一方。在Customer中使用集合表示持有多的一方
public class Customer {
private Integer id;
private String name;
//在1的一方,使用集合表达持有多的一方的引用
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<Order> getOrders() {
return orders;
}
public void setOrders(Set<Order> 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;
}
}
配置映射关系:创建实体类的映射文件
customer.hbm.xml
<hibernate-mapping package="com.scong.domain">
<class name="Customer" table="t_customer">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name" column="name"></property>
<!--
Set:表示一对多关系中的集合
name : 集合的属性名称
key : 用来描述外键
column: 外键的值
one-to-many: 表达Customer 和 Order之间的关系是一对多
class: 表达关联的另一方的完整类名
-->
<set name="orders">
<key column="cid"></key>
<one-to-many class="Order"/>
</set>
</class>
</hibernate-mapping>
order.hbm.xml
<hibernate-mapping package="com.scong.domain">
<class name="Order" table="t_order">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name" column="name"></property>
<!--
many-to-one: 表示多对一关系
name:引用属性的名称
column:外键的列名
class:引用的customer的完整类名
-->
<many-to-one name="customer" class="Customer" column="cid"></many-to-one>
</class>
</hibernate-mapping>
测试:
@Test
public void fun01(){
Configuration conf = new Configuration().configure();
SessionFactory sf = conf.buildSessionFactory();
Session session = sf.openSession();
session.beginTransaction();
Customer c1 = new Customer();
c1.setName("jack");
Order o1 = new Order();
o1.setName("啤酒");
Order o2 = new Order();
o2.setName("可乐");
c1.getOrders().add(o1);
c1.getOrders().add(o2);
o1.setCustomer(c1);
o2.setCustomer(c1);
session.save(c1);
session.save(o1);
session.save(o2);
session.getTransaction().commit();
session.close();
}
2. 关系的维护
inverse属性:是否放弃对外键的维护,默认值是false
在一对多开发中,1的一方一般都放弃对外键的维护,即<set inverse="true">
在上述Customer映射文件中,若将inverse属性设置为true,则上述测试代码中,可以去掉以下两行代码
c1.getOrders().add(o1);
c1.getOrders().add(o2);
同时,也无法直接删除数据库中的t_customer表
3. 级联操作
cascade属性,级联操作。
取值:可以同时配置多项,使用逗号分隔。
- save-update:级联保存,级联修改。例如保存A的时候,同时保存B
- delete:级联删除,全部删除。删除A的同时删除B
- delete-orphan:孤儿删除。当A解除与B的关系时,会将B删除,A仍然存在。
- all : save-update 和 delete 整合
- all-delete-orphan : 三个整合
3.1 设置cascade属性的值为save-update时,保存Customer对象的同时将自动级联保存Order对象
@Test
public void test01(){
Configuration conf = new Configuration().configure();
SessionFactory sf = conf.buildSessionFactory();
Session session = sf.openSession();
session.beginTransaction();
Customer c1 = new Customer();
c1.setName("jack");
Order o1 = new Order();
o1.setName("啤酒");
Order o2 = new Order();
o2.setName("可乐");
c1.getOrders().add(o1);
c1.getOrders().add(o2);
o1.setCustomer(c1);
o2.setCustomer(c1);
session.save(c1);//设置cascade: save-update,只需要保存c1即可
session.getTransaction().commit();
session.close();
}
3.2 设置cascade属性的值为delete时,删除Customer数据会将与之相关联的Order数据全部删除
注意:如果同时设置两个映射文件的cascade属性为delete时,删除它们当中任意一个数据的时候,将同时删除与之关联的所有数据。
Customer c = (Customer) session.get(Customer.class, 2);
session.delete(c); //配置cascade属性的值为:delete,既可级联删除两张表数据
3.3 设置cascade属性的值为delete-orphan时,当解除Customer与Order的关联时,Hibernate将会自动删除Order中的相关数据
Customer c = (Customer) session.get(Customer.class, 4);
Iterator<Order> it= c.getOrders().iterator();
while (it.hasNext()) {
it.next();
it.remove(); //解除与Order的关系后,相关的数据将会被删除
}
三、 多对多操作
1. 多对多关系的配置
创建实体类,Student表示学生类,Course表示课程类
Student.java
public class Student {
private Integer id;
private String name;
private Set<Course> courses = new HashSet<Course>();
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<Course> getCourses() {
return courses;
}
public void setCourses(Set<Course> courses) {
this.courses = courses;
}
}
Course.java
public class Course {
private Integer id;
private String name;
private Set<Student> students = new HashSet<Student>();
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<Student> getStudents() {
return students;
}
public void setStudents(Set<Student> students) {
this.students = students;
}
}
配置映射关系:创建实体类的映射文件
Student.hbm.xml
<hibernate-mapping package="com.scong.domain">
<class name="Student" table="t_student">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name" column="name"></property>
<!--
Set:表示集合
name : 集合的属性名称
table : 配置中间表的表名
key : 用来描述外键
column: 对方引用“我”的外键的值
one-to-many: 表达关系是多对多
class: 表达关联的另一方的完整类名
colum: 对方在中间表的外键名
-->
<set name="courses" table="t_student_course">
<key column="sid"></key>
<many-to-many class="Course" column="cid"></many-to-many>
</set>
</class>
</hibernate-mapping>
Course.hbm.xml
<hibernate-mapping package="com.scong.domain">
<class table="t_course" name="Course">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name" column="name"></property>
<set name="students" table="t_student_course">
<key column="cid"></key>
<many-to-many class="Student" column="sid"></many-to-many>
</set>
</class>
</hibernate-mapping>
配置Hibernate.cfg.xml文件,导入映射文件,进行测试
@Test
public void test01(){
Configuration conf = new Configuration().configure();
SessionFactory sf = conf.buildSessionFactory();
Session session = sf.openSession();
session.beginTransaction();
session.getTransaction().commit();
session.close();
}
结果:数据库中新增三张表,表结构和预想中一致。
2. 级联操作
在多对多关系中,一般只由一方来维护关系,另一方放弃维护,即设置inverse属性为true。在业务上讲,哪方维护关系多点就使用哪一方来维护关系。
多对多关系的级联操作和一对多的级联操作相似,因此只演示级联保存。
1. 设置Student.hbm.xml文件中inverse属性为false,cascade属性为save-update
2. 设置Course.hbm.xml文件中inverse属性为true
3. 测试级联保存
Student stu1 = new Student();
stu1.setName("tom");
Student stu2 = new Student();
stu2.setName("jack");
Course c1 = new Course();
c1.setName("java语言");
Course c2 = new Course();
c2.setName("c语言");
Course c3 = new Course();
c3.setName("Python语言");
stu1.getCourses().add(c1);
stu1.getCourses().add(c2);
stu1.getCourses().add(c3);
stu2.getCourses().add(c1);
stu2.getCourses().add(c2);
stu2.getCourses().add(c3);
session.save(stu1);
session.save(stu2);
4. 结果:保存Student的同时也保存了Course
四、 一对一关系的配置
方式一:外键生成策略
先创建实体类,Company为公司类,Address为地址类
company.java
public class Company {
private Integer id;
private String name;
private Address address;
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 Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
address.java
public class Address {
private Integer id;
private String name;
private Company company;
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 Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
配置映射关系:创建实体类的映射文件
Company.hbm.xml
<hibernate-mapping package="com.scong.domain">
<class name="Company" table="t_company">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name" column="name"></property>
<!--
配置一对一关系
one-to-one:默认使用主键同步来完成
property-ref:指定一对一关联时,指向哪个属性
-->
<one-to-one name="address" class="Address" property-ref="company"></one-to-one>
</class>
</hibernate-mapping>
Address.hbm.xml
<hibernate-mapping package="com.scong.domain">
<class name="Address" table="t_address">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name"></property>
<!-- unique:外键唯一 -->
<many-to-one name="company" class="Company" column="cid" unique="true"></many-to-one>
</class>
</hibernate-mapping>
配置hibernate.cfg.xml文件,将映射文件导入,进行测试
@Test
public void test01(){
Configuration conf = new Configuration().configure();
SessionFactory sf = conf.buildSessionFactory();
Session session = sf.openSession();
session.beginTransaction();
Company comp = new Company();
comp.setName("google");
Address addr = new Address();
addr.setName("USA");
//使用外键生成策略时,外键所在的对象才能维护关系,另一方无法维护
addr.setCompany(comp);//维护关系
session.save(comp);
session.save(addr);
session.getTransaction().commit();
session.close();
}
方式二:主键同步策略(推荐使用)
修改映射文件,其他不变
Company.hbm.xml
<hibernate-mapping package="com.scong.domain">
<class name="Company" table="t_company">
<id name="id" column="id">
<generator class="native"></generator>
</id>
<property name="name" column="name"></property>
<one-to-one name="address" class="Address"></one-to-one>
</class>
</hibernate-mapping>
Address.hbm.xml
<hibernate-mapping package="com.scong.domain">
<class name="Address" table="t_address">
<id name="id" column="id">
<!-- foreign:既做主键又做外键 -->
<generator class="foreign">
<!-- 作为外键时引用的是哪个元素 -->
<param name="property">company</param>
</generator>
</id>
<property name="name" column="name"></property>
<!-- constrained:主键约束 -->
<one-to-one name="company" class="Company" constrained="true"></one-to-one>
</class>
</hibernate-mapping>