前言:在使用JDBC进行多表操作之前,建议多看看我这篇博客《Java Web基础入门第四十四讲 数据库表的设计》。
使用JDBC操作一对多或多对一关系的数据库表
我们以部门和员工的关系来说明使用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操作多对多关系的数据库表
我们以老师和学生的关系来说明使用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框架的时候,要设置级联删除时候,有一方要放弃关系的维护,不然会陷入死循环。
总结
- 不管Java的对象存在何种关系,反映到关系型数据库中,都是使用外键表示记录(即对象)的关联关系;
- 设计Java对象如涉及到多个对象相互引用,要尽量避免使用一对多,或多对多关系,而应使用多对一描述对象之间的关系(或使用延迟加载的方式)。