Ⅰ. 什么是 JDBC
JDBC(Java Data Base Connectivity, Java数据库连接)是 Java 程序和数据库之间的桥梁,包含了一套 Java 定义的用于执行 SQL 语句的接口,使开发者能够编写数据库的程序。
JDBC 的主要作用是:与数据库建立连接、发送 SQL 语句和处理数据库执行结果。

客户端操作数据库的过程主要分为以下几步:
-
连接到数据库服务
-
发送
SQL语句 -
得到返回结果并显示
-
关闭连接
Java 采取的做法是把以上操作步骤定义了相应的接口,具体的实现交给数据库厂商去做,Java程序员只需要按照需要调用接口中定义的方法即可 ,这样不论使用什么数据库,都对于 Java 程序没有任何影响,即便是换一个数据库,也只需要换一下相应厂商的实现依赖。
JDBC 使用过程可以概括为:
-
加载数据库厂商的驱动包
-
建立连接
-
创建
Statement -
执行
SQL -
处理结果释放资源和关闭连接。
Ⅱ. 使用 JDBC
一、创建 Maven 工程并配置国内镜像
Maven 相当于 Java 世界里的应用(依赖)市场,管理市面上所有的应用(依赖),自动化地帮你拉库、编译、打包、测试、部署,一键搞定项目构建 。
创建 maven 工程如下图所示:

下面是工程的目录构成:

然后到 maven仓库 查找 MySQL Connector/j,然后点进去复制 maven 代码,如下所示:

复制到 pom.xml 的 dependencies 依赖中,如下图所示:

下载完成就能使用了!
点击编译器右边的 maven 图标,可以看到下面的自动化操作,如下所示,这些后面会系统学!

在下载依赖的时候,可能速度比较慢,这时候就需要去修改配置文件 settings.xml 中的源,改成阿里云镜像,速度会加快:

<mirrors>
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
<mirror>
<id>central</id>
<mirrorOf>*</mirrorOf>
<name>aliyun central</name>
<url>https://maven.aliyun.com/repository/central</url>
</mirror>
<mirror>
<id>spring</id>
<mirrorOf>*</mirrorOf>
<name>aliyun spring</name>
<url>https://maven.aliyun.com/repository/spring</url>
</mirror>
</mirrors>二、建立数据库连接
DriverManager 驱动管理类,⽤于管理 JDBC 驱动程序,可以从驱动程序中获取数据库连接,始于 JDK1.1。
DataSource 数据源是 DriverManager 的替代方案,始于 JDK1.4,是获取数据库连接的首选方法,推荐使用。
这两者都可以获取到数据库连接,只不过连接管理方式和资源利用效率不同,如下所示:
-
DriverManager每次调用getConnection方法都会初始化一个新的连接,使用完成后会关闭真实连接,导致资源浪费。 -
DataSource使用了连接池 的技术,会在初始化时创建一定数量的数据库连接,这些连接可以重复使用,关闭时并不是真正关闭连接,而是将连接归还给连接池 ,以供后续使用,有效地提高资源利用率和性能。
① 通过 DriverManager 静态方法获取
*// 1. 加载驱动注册程序,只有注册了驱动才能使用*
Class.*forName*("com.mysql.cj.jdbc.Driver");
*// 2. 获取数据库连接*
connection = DriverManager.*getConnection*("jdbc:mysql://localhost:3306/javalearning?useSSL=false&serverTimezone=UTC&characterEncoding=utf8",
"root", "123123");☠ 注意事项:
-
MySQL8之后驱动类名是com.mysql.cj.jdbc.Driver,而不是旧版本的com.mysql.jdbc.Driver -
JDBC连接的url的格式是:jdbc:mysql://主机:端口/数据库名?参数键=值&参数键=值
② 通过 DataSource 对象获取(推荐⭐⭐⭐)

*// 1. 设置mysql数据源的连接信息*
MysqlDataSource mds = new MysqlDataSource();
mds.setUrl("jdbc:mysql://localhost:3306/javalearning?useSSL=false&serverTimezone=UTC&characterEncoding=utf8");
mds.setUser("root");
mds.setPassword("123123");
*// 2. 向上转型成DataSource接口*
DataSource ds = mds;
// 3. 获取连接
connection = ds.getConnection();三、创建语句对象
① 创建 Statement
Statement 是用于执行静态 SQL 语句并返回执行结果的对象。
// 通过connection获取statement对象
statement = connection.createStatement();② 创建 PreparedStatement(推荐⭐⭐⭐)
这是预编译 SQL 语句对象,SQL 语句被预编译并存储在 PreparedStatement 对象中,可以使用该对象多次执行 SQL 语句,同时解决了SQL注入问题 。
在使用 sql 的时候用 ?号进行占位 ,然后通过 set系列函数进行参数绑定 。
*// 4. 定义sql语句并创建PreparedStatement对象
String sql = "select id, name, sno, gender, enroll_date, class_id from student where name = ?";
prestatement = connection.prepareStatement(sql);
// 5. 进行占位符绑定
**prestatement .setString(1, "宋江");*四、执行 SQL 语句
-
执行
select查询时返回的是一个结果集,用ResultSet进行接收,通常用executeQuery()接口。 -
执行
insert、update、delete操作时,返回的是受影响的行数,用int类型接收然后判断正确即可,通常用executeUpdate()接口。
*// 执行select语句, 并接收结果集 *
ResultSet resultSet = statement.executeQuery("select id, name, sno, age, gender, enroll_date, class_id from student");
*// 执行insert, update, delete语句,并接收受影响的行数 *
int row = statement.executeUpdate("update student set age = 20 where id = 2");五、处理结果集 ResultSet
如果返回的是一个结果集,也就是进行 select 查询,则需要遍历这个集合获取对应列的值。
此时使用结果集的 next() 函数,如果游标还没走到最后,则返回 true,而游标走到最后则会返回 false,然后可以使用一系列的 getxxx() 方法获取对应的字段,具体代码如下所示:
*// 7. 处理结果集*
while (resultSet.next()) {
Long id = resultSet.getLong("id");
String name = resultSet.getString("name");
String sno = resultSet.getString("sno");
boolean gender = resultSet.getBoolean("gender");
Date enrollDate = resultSet.getDate("enroll_date");
Long classId = resultSet.getLong("class_id");
System.*out*.println(id + "\t" + name + "\t" + sno + "\t" + gender + "\t" + enrollDate + "\t" + classId + "\t");
}六、释放资源
最后在 finally 代码块中进行资源释放,否则会出现内存泄漏问题:
finally {
*// 8. 释放资源*
* *if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}Ⅲ. DAO
DAO 是 Data Access Object(数据访问对象)的缩写,是 Java 等编程语言中常见的一种设计模式,用来封装对数据库的操作逻辑。
简单的说,DAO 就是专门负责 "跟数据库打交道" 的模块,把 SQL操作从业务代码里分离出来 ,让业务代码更干净、更易维护、更专业。
比如我们要对学生表和班级表进行增删改查,以及一些特定要求的查询,那我们会创建以下内容:

比如 Classes 表,存放对应的数据字段,如下所示:
package com.liren.model;
public class Classes {
private Long id; // 主键编号
private String name; // 班级名
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}然后对应的 ClassesDAO.java 中实现对应的增删查改,如下所示:
package com.liren.dao;
import com.liren.model.Classes;
import utils.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
*/***
* * 专门针对班级表的数据库查询对象*
* */*
public class ClassesDAO {
// 增加操作
public void insert(Classes cs) {
Connection conn = null;
PreparedStatement ps = null;
try {
conn = DBUtil.*getConnection*();
String sql = "insert into class(id, name) values(?, ?)";
ps = conn.prepareStatement(sql);
ps.setLong(1, cs.getId());
ps.setString(2, cs.getName());
int ret = ps.executeUpdate();
if(ret > 0) {
System.*out*.println("插入班级成功!");
} else {
System.*out*.println("插入班级失败!");
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
DBUtil.*closeConnection*(null, ps, conn);
}
}
// 删除、更新、查询操作......
}Ⅳ. 工具类
上面提到将一些数据源初始化、关闭数据源等操作专门做成一个工具类,可以减少代码冗余,如下所示:
// DBUtil.java文件
package utils;
import com.mysql.cj.jdbc.MysqlDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class DBUtil {
private static String *url *= "jdbc:mysql://localhost:3306/javalearning?useUnicode=true&characterEncoding=utf8";
private static String *user *= "root";
private static String *password *= "123123";
private static DataSource *ds *= null;
*// 在类加载的时候初始化dataSource对象*
* *static {
MysqlDataSource mds = new MysqlDataSource();
mds.setURL(*url*);
mds.setUser(*user*);
mds.setPassword(*password*);
*ds *= mds;
}
*// 构造方法私有化,防止其他的地方通过new来创建这个类的对象*
* *private DBUtil() {}
// 获取连接接口
public static Connection getConnection() throws SQLException {
return *ds*.getConnection();
}
// 获取关闭连接接口
public static void closeConnection(ResultSet resultSet, Statement statement, Connection conn) {
if (resultSet != null) {
try {
resultSet.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (Exception e) {
e.printStackTrace();
}
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}Ⅴ. 多表查询时的注意事项
在 select 的时候难免会有多表查询,如下所示:
String sql = "select s.id, s.name, s.age, c.id c_id, c.name c_name from student s, class c where s.class_id = c.id and c.name = ?";此时结果集该如何表示呢❓❓❓
这个时候可以在 Student表中添加 Classes对象,进行关联 ,如下所示:
package com.liren.model;
import java.util.Date;
public class Student {
private Long id;
private String name;
private String sno;
private Integer age;
private byte gender;
private Date enroll_date;
private Long class_id;
private Classes cs; *// 与课程表关联*
* *public Classes getCs() {
return cs;
}
public void setCs(Classes cs) {
this.cs = cs;
}
// 一系列get、set方法
}这样子只需要在结果集中构造出 Classes 对象,然后设置到 Student 中的 Classes 字段,最后拿到的 List<Student> 返回就能存在 Classes 的字段了!
package com.liren.dao;
import com.liren.model.Classes;
import com.liren.model.Student;
import utils.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
*/***
* * 专门针对学生表的数据库访问对象*
* */*
public class StudentDAO {
*// 增删改查。。。*
* // 还有专门针对学生表的业务逻辑处理方法*
* // 比如下面的:根据班级名查询班级中所有的同学信息*
* *public List<Student> selectByClassname(String classname) {
if(classname == null || classname.isEmpty()) {
System.*out*.println("班级名不能为空!");
return null;
}
Connection conn = null;
ResultSet rs = null;
PreparedStatement ps = null;
try {
List<Student> ret = new ArrayList<Student>();
conn = DBUtil.*getConnection*();
String sql = "select s.id, s.name, s.age, s.sno, s.gender, s.enroll_date, c.id c_id, c.name c_name from student s, class c where s.class_id = c.id and c.name = ?";
ps = conn.prepareStatement(sql);
ps.setString(1, classname);
rs = ps.executeQuery();
// ❗❗❗❗❗❗❗❗下面是重点❗❗❗❗❗❗❗❗❗
while(rs.next()) {
*// 封装Student对象*
* *Student s = new Student();
s.setId(rs.getLong("id"));
s.setName(rs.getString("name"));
s.setAge(rs.getInt("age"));
s.setSno(rs.getString("sno"));
s.setGender(rs.getByte("gender"));
s.setEnroll_date(rs.getDate("enroll_date"));
s.setClass_id(rs.getLong("c_id"));
*// 封装Classes对象*
* *Classes c = new Classes();
c.setId(rs.getLong("c_id"));
c.setName(rs.getString("c_name"));
*// 设置Student对象中的Classes对象,完成两者的关联,然后添加到List中*
* *s.setCs(c);
ret.add(s);
}
return ret;
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
DBUtil.*closeConnection*(rs, ps, conn);
}
}
}