一、数据库中表之间的关系以及如何创建:
数据库中表的关系有以下三种:
1、一对多(也即多对一):在“多”的一方设置与“一”的一方的主键关联的外键;
2、多对多:创建一个至少有两个字段的中间表,其中两个字段分别设置成与关系表的主键关联的外键。多对多关系可以看作是两个一对多关系:“一”的一方是关系表,“多”的一方是中间表;
3、一对一:创建一对“一对多”关系的表,把“多”的一方的外键设置为unique;或者将两个表的主键设置为相等的将其关联起来;或者干脆就合并为一张表更为省事。
二、一对多关系的表在hibernate中的配置:
1、如何在数据库中创建“一对多”关系的表:
在“多”的一方设置与“一”的一方的主键关联的外键
##一的一方:部门表
CREATE TABLE `com_department` (
`dep_id` bigint(20) NOT NULL AUTO_INCREMENT,
`dep_name` varchar(255) DEFAULT NULL,
`dep_manager` varchar(255) DEFAULT NULL,
PRIMARY KEY (`dep_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
##多的一方:员工表,设置外键关联部门表的主键
CREATE TABLE `com_employee` (
`emp_id` bigint(20) NOT NULL AUTO_INCREMENT,
`emp_dep_id` bigint(20) DEFAULT NULL,
`emp_age` int(3) DEFAULT NULL,
`emp_gender` varchar(3) DEFAULT NULL,
`emp_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`emp_id`),
constraint employee_department_id_fk foreign key(emp_dep_id) references com_department(dep_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2、如何在实体类中表明对应数据库中的表的“一对多”关系:
在“一”的一方声明一个Set集合类型的成员变量,存放“多”的一方的对象:
package cn.company.vo;
import java.util.HashSet;
import java.util.Set;
public class Department {
private Long id;
private String name;
private String manager;
//声明一个Set集合类型的成员变量,存放“多”的一方的对象
private Set<Employee> employee = new HashSet<Employee>();
//...节省篇幅,此处省略get/set方法
@Override
public String toString() {
return "Department [id=" + id + ", name=" + name + ", manager=" + manager + "]";
}
}
在“多”的一方声明一个“一”的一方对应的类类型,保存“一”的一方的对象:
package cn.company.vo;
public class Employee {
private Long id;
private Integer age;
private String gender;
private String name;
//声明一个“一”的一方对应的类类型,保存“一”的一方的对象
private Department department;
//...节省篇幅,此处省略get/set方法
@Override
public String toString() {
return "Employee [id=" + id + ", age=" + age + ", gender=" + gender + ", name=" + name + "]";
}
}
3、如何在映射文件中表明对应数据库中的表的“一对多”关系:
在“一”的一方设置set标签:
- set标签属性name表示当前类中声明的多的一方的Set集合的成员属性名称;
- set标签的子标签key的column属性表示外键名称;
- set标签的子标签one-to-many的class属性表示“多”的一方对应类的全路径。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.company.vo.Department" table="com_department">
<id name="id" column="dep_id">
<generator class="native"/>
</id>
<property name="name" column="dep_name"/>
<property name="manager" column="dep_manager"/>
<!-- 在“一”的一方设置set标签 -->
<set name="employee">
<key column="emp_dep_id"/>
<one-to-many class="cn.company.vo.Employee"/>
</set>
</class>
</hibernate-mapping>
在“一”的一方配置many-to-one标签:
- many-to-one标签的属性name表示当前类中声明的“一”的一方对应类的属性名称;
- many-to-one标签的属性column表示外键名称;
- many-to-one标签的属性class表示“一”的一方对应类的全路径。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.company.vo.Employee" table="com_employee">
<id name="id" column="emp_id">
<generator class="native"/>
</id>
<property name="age" column="com_age"/>
<property name="gender" column="com_gender"/>
<property name="name" column="com_name"/>
<!-- 在“一”的一方配置many-to-one标签 -->
<many-to-one name="department" column="emp_dep_id" class="cn.company.vo.Department"/>
</class>
</hibernate-mapping>
4、测试:
下面编写个测试类:
此时部门表和员工表如下:
三、级联删除、保存或更新
从上面的测试类可以发现保存部门和员工的时候,每个对象都要单独做一次保存,这样是非常麻烦的,那么有没有更加简便的方法呢?当然是有的,可以用级联保存或更新。
1、级联保存或更新:
即在保存有关联关系的持久类对象的一方时,自动保存另外一方。这样做的前提是在保存的一方的映射文件中进行配置:
- 如果保存的一的一方级联保存多的一方,则在一的一方的映射文件中为set标签的cascade属性赋值为“save-update”;
- 如果保存的多的一方级联保存一的一方,则在多的一方的映射文件中为many-to-one标签的cascade属性赋值为“save-update”;
例如:
a.为上面的Department.hbm.xml文件中的set标签配置cascade属性:
<!-- 在“一”的一方设置set标签 -->
<set name="employee" cascade="save-update">
<key column="emp_dep_id"/>
<one-to-many class="cn.company.vo.Employee"/>
</set>
则上面测试类中的保存操作可以进行简化:
b.为上面的Employee.hbm.xml文件中的many-to-one标签配置cascade属性:
<!-- 在“一”的一方配置many-to-one标签 -->
<many-to-one cascade="save-update" name="department" column="emp_dep_id" class="cn.company.vo.Department"/>
则上面测试类中的保存操作可以进行简化:
2、级联删除:
从上面的级联保存或更新我们可以推断出级联删除的作用,即删除一方时,被关联的另外一方也会被删除。其配置也与级联保存或更新一样,只不过cascade的属性值要改为“delete”或者改为“save-update,delete”。
注意:级联删除在一对多关系中通常用在一的一方,这也很好理解。拿上面部门和员工的关系来说,删除部门级联删除部门里的员工是符合逻辑的,但是某个员工离职或者转移出该部门了,总不能把该部门也级联删除了吧?
四、inverse:是否放弃对外键的维护
在进行涉及到两个表关系的修改操作时,如果没有考虑inverse属性的配置,那么程序将对表的外键进行两次修改,例如下面的操作:
当执行之后我们可以发现,程序发送了两次修改外键的SQL语句:
第二次的修改其实是多余的,有人也许对此不以为意,认为这都是细枝末节不用理会。这种观点是错误的,要知道频繁的对操作数据库是很消耗性能的,而且多余的操作是耗时的。为了避免这个问题,我们需要配置inverse属性。
inverse属性:该属性在于使有外键关联的两个表之一放弃对外键的维护权,这样的话在进行涉及到两个表关系的操作时就只会修改一次外键。该属性的使用位置与cascade一样,true表示放弃,false表示不放弃。例:
<!-- 在“一”的一方设置set标签 -->
<set name="employee" cascade="save-update" inverse="true">
<key column="emp_dep_id"/>
<one-to-many class="cn.company.vo.Employee"/>
</set>
此时就没有多余的SQL语句了。
注意:
- a.该属性一般用在一的一方,原因很好理解,让全国14亿人记住国家主席的姓名是很简单的,但是让国家主席记住14亿人的姓名是不可能的;
- b.该属性用在保存操作中时需要小心,如果一的一方即设置了级联保存又设置了放弃维护外键,执行操作之后外键将为null,因为此时外键仅由一的一方维护而一的一方却将之放弃了。
五、多对多关系的表在hibernate中的配置:
1、如何在数据库中创建“一对多”关系的表:
创建一个至少有两个字段的中间表,其中两个字段分别设置成与关系表的主键关联的外键。多对多关系可以看作是两个一对多关系:“一”的一方是关系表,“多”的一方是中间表。
##多的一方:课程表
CREATE TABLE sch_course (
cou_id bigint(20) NOT NULL AUTO_INCREMENT,
cou_name varchar(255) DEFAULT NULL,
role_credit int(3) DEFAULT NULL,
PRIMARY KEY (cou_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
##多的一方:学生表
CREATE TABLE sch_student (
stu_id bigint(20) NOT NULL AUTO_INCREMENT,
stu_name varchar(255) DEFAULT NULL,
stu_grade varchar(255) DEFAULT NULL,
PRIMARY KEY (stu_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
##中间表
CREATE TABLE sch_student_course (
stu_id bigint(20) NOT NULL,
cou_id bigint(20) NOT NULL,
PRIMARY KEY (stu_id,cou_id),
KEY `FK_stu_cou_stu_id` (cou_id),
CONSTRAINT `FK_stu_cou_cou_id` FOREIGN KEY (stu_id) REFERENCES sch_student (stu_id) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `FK_stu_cou_stu_id` FOREIGN KEY (cou_id) REFERENCES sch_course (cou_id) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2、如何在实体类中表明对应数据库中的表的“多对多”关系:
在每方各自声明一个存储对方对应的类类型的Set集合,存储对方类的对象:
package cn.company.vo;
import java.util.HashSet;
import java.util.Set;
public class Course {
private Long id;
private String name;
private Integer credit;
//声明一个存储对方对应的类类型的Set集合
private Set<Student> students = new HashSet<Student>();
//...节省篇幅,此处省略get/set方法
@Override
public String toString() {
return "Course [id=" + id + ", name=" + name + ", credit=" + credit + "]";
}
}
package cn.company.vo;
import java.util.HashSet;
import java.util.Set;
public class Student {
private Long id;
private String name;
private Integer grade;
//声明一个存储对方对应的类类型的Set集合
private Set<Course> courses = new HashSet<Course>();
//...节省篇幅,此处省略get/set方法
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", grade=" + grade + "]";
}
}
3、如何在映射文件中表明对应数据库中的表的“一对多”关系:
在各自的映射文件中设置set标签:
- set标签属性name表示当前类中声明的对方的Set集合的成员属性名称;
- set标签属性table表示中间表的名称;
- set标签的子标签key的column属性表示当前表在中间表中对应的外键名称;
- set标签的子标签many-to-many的class属性表示对方对应类的全路径;
- set标签的子标签many-to-many的column属性表示对方表在中间表中对应的外键名称。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.company.vo.Course" table="sch_course">
<id name="id" column="cou_id">
<generator class="native"/>
</id>
<property name="name" column="cou_name"/>
<property name="credit" column="cou_credit"/>
<set name="students" table="sch_student_course">
<key column="cou_id"/>
<many-to-many class="cn.company.vo.Student" column="stu_id"/>
</set>
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.company.vo.Student" table="sch_student">
<id name="id" column="stu_id">
<generator class="native"/>
</id>
<property name="name" column="stu_name"/>
<property name="grade" column="stu_grade"/>
<set name="courses" table="sch_student_course">
<key column="stu_id"/>
<many-to-many class="cn.company.vo.Course" column="cou_id"/>
</set>
</class>
</hibernate-mapping>
六、多对多关系的表在hibernate中的操作:
1、级联删除、保存或更新:
其操作可参照一对多关系的操作,但级联删除一般不使用,因为多对多级联关系复杂,甚至有可能把整个表都给级联删除了。
2、inverse:
其操作可参照一对多关系的操作,可以任意选择一方放弃外键维护权,该属性用在保存操作中时需要小心,如果一的一方即设置了级联保存又设置了放弃维护外键,执行操作之后外键将为null,因为此时外键仅由一的一方维护而一的一方却将之放弃了。
3、其他操作:
- 给某一学生增选某一课程;
- 给某一学生改选某一课程;
- 给某一学生删除某一课程;