前言:在使用JDBC进行多表操作之前,建议多看看我这篇博客《Java Web基础入门第四十四讲 数据库表的设计》。
我们以部门和员工的关系来说明使用JDBC是如何操作一对多或多对一关系的数据库表的。
首先,使用如下SQL建表语句在数据库中创建department表和employee表。
create table department
(
id varchar(40) primary key,
name varchar(40)
);
create table employee
(
id varchar(40) primary key,
name varchar(40),
salary double,
department_id varchar(40),
constraint department_id_FK foreign key(department_id) references department(id)
);
然后,在cn.liayun.domain包下创建两个封装数据的JavaBean——Department.java和Employee.java。
Department类的具体代码如下:
package cn.liayun.domain;
import java.util.HashSet;
import java.util.Set;
public class Department {
private String id;
private String name;
private Set employees = new HashSet();
public Set getEmployees() {
return employees;
}
public void setEmployees(Set employees) {
this.employees = employees;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Employee类的具体代码如下:
package cn.liayun.domain;
public class Employee {
private String id;
private String name;
private double salary;
private Department department;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
}
这时,部门和员工对象的关系和部门表和员工表在数据库里的关系如下图:
最后,还要在cn.liayun.dao包下创建一个类——DepartmentDao.java,下面用它来操作数据库中的部门表和员工表。
我们在应用程序中加入了DBCP连接池,还有关于JdbcUtils类怎么写,可以参考我的笔记《Java Web基础入门第六十一讲 Apache DBUtils框架的学习》。
在实际开发里面,关于一对多和多对一,有一个设计原则:尽量避免去使用一对多这种关系,而应该多使用多对一这种关系。但有时候是不可避免的,例如,订单和订单项就是一对多这种关系,一个订单里面有多个订单项,在显示订单的时候一定要显示多方的数据,即订单项,这时候我们就必须设计这种关系了。如果你没有设计这种关系,这时候从数据库里面找出订单的基本信息,人家一看订单,只能看到订单的基本信息,不能看到订单项,这是不行的。如果这时候不可避免要设计这种关系(一对多这种关系),如果订单项有上千万条的数据,千万不能直接将其找出来,一定要采用分页的方式去找。
在一方记住了多方,在多方也记住了一方,我们这样设计就是双向的关联,在实际开发里面,这种双向的关联是不推荐使用的!而且一对多这种关系最好就不要设计,即不要在一方记住了多方,如果你设计了,在找出一方的数据时就要取多方的数据,如果多方的数据超多的话,很容易导致内存溢出。那么在一方不用记住多方,而在多方记住了一方,我们这样设计就是单向的关联。假设一个部门下面有10万个员工,你首先找部门的信息,然后就要将这10万个员工全部找出来存放在List集合里面,那内存就蹦了。所以在实际开发过程中,在写一对多的find方法时,找出一方的数据,再找多方的数据,你这时候千万要小心,一定要注意多方的数据多不多,如果多方的数据多的话,千万不能直接将其找出来,要采用分页的方式去找。
我们可采用两种方式来删除某部门,其中第一种方式如下:
第二种方式:数据库支持级联,如果设置了级联,只需要根据id删除部门即可,由于你设置了级联,数据库系统会自动把employee表的外键列department_id置空。首先,删除employee表的外键约束:
再在employee表的外键列department_id设置级联:
SET NULL
仅仅把employee表的外键列department_id置空。我们还可以这样为employee表的外键列department_id设置级联:
CASCADE
表示部门被删除时,所有的员工都会被删掉(主表一删,从表中的所有数据就都要删掉)。
在employee表的外键列department_id设置好级联之后,删除部门的代码就变为:
编写完DepartmentDao类的代码之后,我们来测试一把,测试代码为:
package cn.liayun.service;
import java.sql.SQLException;
import org.junit.Test;
import cn.liayun.dao.DepartmenDao;
import cn.liayun.dao.TeacherDao;
import cn.liayun.domain.Department;
import cn.liayun.domain.Employee;
import cn.liayun.domain.Student;
import cn.liayun.domain.Teacher;
public class BService {
@Test
public void add() throws SQLException {
Department d = new Department();
d.setId("10001");
d.setName("研发部");
Employee e1 = new Employee();
e1.setId("1");
e1.setName("李阿昀");
e1.setSalary(10000);
Employee e2 = new Employee();
e2.setId("2");
e2.setName("李燕琼");
e2.setSalary(4000);
d.getEmployees().add(e1);
d.getEmployees().add(e2);
DepartmenDao dao = new DepartmenDao();
dao.add(d);
}
@Test
public void find() throws SQLException {
DepartmenDao dao = new DepartmenDao();
Department d = dao.find("10001");
System.out.println(d);
}
@Test
public void delete() throws SQLException {
DepartmenDao dao = new DepartmenDao();
dao.delete("10001");
}
}
我们以老师和学生的关系来说明使用JDBC是如何操作多对多关系的数据库表的。
首先,使用如下SQL建表语句在数据库中创建teacher表和student表以及teacher_student表(中间表)。
create table teacher
(
id varchar(40) primary key,
name varchar(40),
salary double
);
create table student
(
id varchar(40) primary key,
name varchar(40)
);
create table teacher_student
(
teacher_id varchar(40),
student_id varchar(40),
primary key(teacher_id, student_id),
constraint teacher_id_FK foreign key(teacher_id) references teacher(id),
constraint student_id_FK foreign key(student_id) references student(id)
);
数据库表结构如下图:
然后,在cn.liayun.domain包下创建两个封装数据的JavaBean——Teacher.java和Student.java。
Teacher类的具体代码如下:
package cn.liayun.domain;
import java.util.HashSet;
import java.util.Set;
public class Teacher {
private String id;
private String name;
private double salary;
private Set students = new HashSet();
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Set getStudents() {
return students;
}
public void setStudents(Set students) {
this.students = students;
}
}
Student类的具体代码如下:
package cn.liayun.domain;
import java.util.HashSet;
import java.util.Set;
public class Student {
private String id;
private String name;
private Set teachers = new HashSet();
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set getTeachers() {
return teachers;
}
public void setTeachers(Set teachers) {
this.teachers = teachers;
}
}
最后,还要在cn.liayun.dao包下创建一个类——TeacherDao.java,用它来操作数据库中的老师表、学生表以及中间表。
首先,只在teacher_student表的外键列teacher_id上设置级联。
删除老师的代码为:
此时删除teacher表中的某一条记录(例如id为1的老师),那么在teacher_student中间表中teacher_id为1的记录都被删掉了,而student表是不受任何影响的。
现在我们不仅在teacher_student表的外键列teacher_id上设置级联,还在teacher_student表的外键列student_id上也设置级联,如下:
此时删除teacher表中的某一条记录(例如id为1的老师),那么在teacher_student中间表中teacher_id为1的记录都被删掉了,而student表还是不会受任何影响。
温馨提示:在学Hibernate框架的时候,要设置级联删除时候,有一方要放弃关系的维护,不然会陷入死循环。