首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >《Java 程序设计》第 16 章 - JDBC 数据库编程

《Java 程序设计》第 16 章 - JDBC 数据库编程

作者头像
啊阿狸不会拉杆
发布2026-01-20 16:06:34
发布2026-01-20 16:06:34
1010
举报

前言

在当今的软件开发中,几乎所有应用程序都需要与数据库进行交互。无论是小型应用还是大型企业系统,数据的存储、检索和管理都是核心功能。JDBC(Java Database Connectivity)作为 Java 语言访问数据库的标准接口,为开发者提供了一种统一的方式来操作各种关系型数据库。

本章将详细介绍 JDBC 数据库编程的相关知识,从数据库基础知识到 JDBC API 的具体使用,再到实际应用案例,帮助读者掌握 Java 操作数据库的核心技能。

思维导图

16.1 数据库系统简介

数据库系统(Database System)是由数据库、数据库管理系统(DBMS)、应用程序和数据库管理员(DBA)组成的系统。它的主要功能是存储、管理和检索数据,同时保证数据的安全性、完整性和一致性。

16.1.1 关系数据库简述

关系数据库是目前应用最广泛的数据库类型,它以表格(Table)的形式组织数据,表格之间通过关系(Relationship)建立联系。

  • 表(Table):由行(Row)和列(Column)组成,用于存储特定类型的数据
  • 行(Row):也称为记录(Record),代表一条完整的数据
  • 列(Column):也称为字段(Field),代表数据的一个属性
  • 主键(Primary Key):用于唯一标识表中的每条记录,不能重复
  • 外键(Foreign Key):用于建立表之间的关系,指向另一个表的主键

关系数据库的主要特点是使用关系模型(二维表格模型)来组织数据,通过 SQL 语言进行操作。

16.1.2 数据库语言 SQL

SQL(Structured Query Language,结构化查询语言)是用于管理关系数据库的标准语言。它可以分为以下几类:

  1. 数据查询语言(DQL):用于查询数据,主要命令是SELECT
  2. 数据操纵语言(DML):用于插入、更新和删除数据,主要命令有INSERTUPDATEDELETE
  3. 数据定义语言(DDL):用于创建和修改数据库对象,主要命令有CREATEALTERDROP
  4. 数据控制语言(DCL):用于控制数据访问权限,主要命令有GRANTREVOKE

常用 SQL 语句示例:

代码语言:javascript
复制
-- 创建表
CREATE TABLE students (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL,
    age INT,
    gender VARCHAR(10),
    major VARCHAR(50)
);

-- 插入数据
INSERT INTO students (name, age, gender, major) VALUES ('张三', 20, '男', '计算机科学');

-- 查询数据
SELECT * FROM students WHERE age > 18;

-- 更新数据
UPDATE students SET age = 21 WHERE name = '张三';

-- 删除数据
DELETE FROM students WHERE id = 1;

16.2 MySQL 数据库

MySQL 是一种开源的关系型数据库管理系统,由于其开源免费、性能优良、易于使用等特点,被广泛应用于各种 Web 应用中。

16.2.1 MySQL 的下载与安装
  1. 下载 MySQL
  2. 安装 MySQL
    • Windows 系统:运行下载的安装文件,按照向导进行安装,可以选择 "Developer Default" 安装类型
    • macOS 系统:可以使用 DMG 文件安装,或者通过 Homebrew 安装
    • Linux 系统:可以使用包管理器安装,如apt-get install mysql-server(Ubuntu)
  3. 配置 MySQL
    • 安装过程中需要设置 root 用户密码
    • 可以选择是否允许远程访问
    • 配置端口号(默认 3306)
16.2.2 使用 MySQL 命令行工具

安装完成后,可以通过命令行工具操作 MySQL:

登录 MySQL

代码语言:javascript
复制
mysql -u root -p

输入密码后即可登录

常用命令

代码语言:javascript
复制
-- 显示所有数据库
SHOW DATABASES;

-- 创建数据库
CREATE DATABASE mydb;

-- 使用数据库
USE mydb;

-- 显示当前数据库中的所有表
SHOW TABLES;

-- 查看表结构
DESCRIBE students;

-- 退出MySQL
EXIT;
16.2.3 使用 Navicat 操作数据库

Navicat 是一款功能强大的数据库管理工具,支持多种数据库,包括 MySQL、Oracle、SQL Server 等。使用图形界面操作数据库,比命令行更直观方便。

  1. 连接数据库
    • 打开 Navicat,点击 "连接" 按钮,选择 "MySQL"
    • 输入连接名称、主机名(默认localhost)、端口号(默认 3306)、用户名和密码
    • 点击 "测试连接",成功后点击 "确定"
  2. 创建数据库和表
    • 在左侧导航栏中,右键点击连接名称,选择 "新建数据库"
    • 输入数据库名称和字符集(推荐 utf8mb4)
    • 双击数据库名称进入该数据库
    • 右键点击 "表",选择 "新建表",设计表结构并保存
  3. 数据操作
    • 可以通过 "查询" 标签执行 SQL 语句
    • 可以通过 "表" 标签直接查看、添加、修改和删除数据

16.3 JDBC 体系结构

JDBC(Java Database Connectivity)是 Java 语言访问数据库的标准接口,它为 Java 开发者提供了一种统一的方式来访问各种关系型数据库。

16.3.1 JDBC 访问数据库

JDBC 访问数据库的基本原理是通过驱动程序(Driver)与数据库进行通信。不同的数据库需要使用相应的驱动程序。

JDBC 访问数据库的流程如下:

16.3.2 JDBC API 介绍

JDBC API 主要包含以下核心接口和类:

  • DriverManager:用于管理数据库驱动程序,获取数据库连接
  • Connection:代表与数据库的连接
  • Statement:用于执行静态 SQL 语句
  • PreparedStatement:用于执行预编译的 SQL 语句,是 Statement 的子接口
  • CallableStatement:用于执行存储过程,是 PreparedStatement 的子接口
  • ResultSet:代表 SQL 查询的结果集
  • SQLException:处理数据库操作中可能出现的异常

这些接口和类都位于java.sql包中。

16.4 数据库访问步骤

使用 JDBC 访问数据库通常遵循以下步骤:

16.4.1 加载驱动程序

在使用 JDBC 访问数据库之前,需要先加载相应的数据库驱动程序。

对于 MySQL 8.0 及以上版本,驱动类为com.mysql.cj.jdbc.Driver,可以通过以下方式加载:

代码语言:javascript
复制
// 加载MySQL驱动
Class.forName("com.mysql.cj.jdbc.Driver");

注意:从 JDBC 4.0 开始,驱动程序可以自动加载,不需要显式调用Class.forName()方法,但为了兼容性,通常还是会显式加载。

16.4.2 建立连接对象

通过DriverManager.getConnection()方法建立与数据库的连接,需要提供数据库 URL、用户名和密码。

MySQL 的 URL 格式为:jdbc:mysql://主机名:端口号/数据库名?参数

代码语言:javascript
复制
// 数据库连接信息
String url = "jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC";
String username = "root";
String password = "123456";

// 建立连接
Connection conn = DriverManager.getConnection(url, username, password);
16.4.3 创建语句对象

通过Connection对象的方法创建语句对象,常用的有:

  • createStatement():创建Statement对象
  • prepareStatement(String sql):创建PreparedStatement对象
代码语言:javascript
复制
// 创建Statement对象
Statement stmt = conn.createStatement();

// 或者创建PreparedStatement对象
String sql = "SELECT * FROM students WHERE age > ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
16.4.4 ResultSet 对象

ResultSet对象代表 SQL 查询的结果集,通过它可以获取查询到的数据。

常用方法:

  • next():移动到下一行,返回true表示有数据
  • getXxx(String columnName):获取指定列名的 Xxx 类型数据
  • getXxx(int columnIndex):获取指定列索引(从 1 开始)的 Xxx 类型数据
代码语言:javascript
复制
// 执行查询
String sql = "SELECT id, name, age FROM students";
ResultSet rs = stmt.executeQuery(sql);

// 处理结果集
while (rs.next()) {
    int id = rs.getInt("id");
    String name = rs.getString("name");
    int age = rs.getInt("age");
    System.out.println("ID: " + id + ", 姓名: " + name + ", 年龄: " + age);
}
16.4.5 关闭有关对象

数据库操作完成后,需要关闭相关对象,释放资源。关闭顺序与创建顺序相反:

  1. 关闭ResultSet
  2. 关闭StatementPreparedStatement
  3. 关闭Connection
代码语言:javascript
复制
// 关闭资源
if (rs != null) {
    try {
        rs.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
}
if (stmt != null) {
    try {
        stmt.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
}
if (conn != null) {
    try {
        conn.close();
    } catch (SQLException e) {
        e.printStackTrace();
    }
}

16.5 访问 MySQL 数据库

下面通过一个完整的示例来演示如何使用 JDBC 访问 MySQL 数据库。

16.5.1 创建数据库和表

首先,我们需要创建一个数据库和表来存储学生信息:

代码语言:javascript
复制
-- 创建数据库
CREATE DATABASE IF NOT EXISTS studentdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 使用数据库
USE studentdb;

-- 创建学生表
CREATE TABLE IF NOT EXISTS students (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL,
    age INT,
    gender VARCHAR(10),
    major VARCHAR(50)
);

-- 插入测试数据
INSERT INTO students (name, age, gender, major) VALUES 
('张三', 20, '男', '计算机科学'),
('李四', 21, '女', '软件工程'),
('王五', 19, '男', '数据科学');
16.5.2 访问 MySQL 数据库

下面是一个完整的 Java 程序,演示如何连接 MySQL 数据库,并执行查询、插入、更新和删除操作:

代码语言:javascript
复制
import java.sql.*;

/**
 * JDBC访问MySQL数据库示例
 */
public class MySQLDemo {
    // 数据库连接信息
    private static final String URL = "jdbc:mysql://localhost:3306/studentdb?useSSL=false&serverTimezone=UTC";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "123456"; // 请替换为你的密码

    public static void main(String[] args) {
        Connection conn = null;
        try {
            // 1. 加载驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            
            // 2. 建立连接
            conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            System.out.println("数据库连接成功!");
            
            // 3. 执行操作
            // 查询所有学生
            System.out.println("\n===== 所有学生 =====");
            queryAllStudents(conn);
            
            // 添加新学生
            System.out.println("\n===== 添加新学生 =====");
            addStudent(conn, "赵六", 22, "男", "人工智能");
            queryAllStudents(conn);
            
            // 更新学生信息
            System.out.println("\n===== 更新学生信息 =====");
            updateStudentAge(conn, 4, 23);
            queryAllStudents(conn);
            
            // 删除学生
            System.out.println("\n===== 删除学生 =====");
            deleteStudent(conn, 4);
            queryAllStudents(conn);
            
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 4. 关闭连接
            if (conn != null) {
                try {
                    conn.close();
                    System.out.println("\n数据库连接已关闭!");
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    /**
     * 查询所有学生
     */
    public static void queryAllStudents(Connection conn) throws SQLException {
        Statement stmt = null;
        ResultSet rs = null;
        try {
            // 创建Statement对象
            stmt = conn.createStatement();
            
            // 执行查询
            String sql = "SELECT id, name, age, gender, major FROM students";
            rs = stmt.executeQuery(sql);
            
            // 处理结果集
            while (rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                int age = rs.getInt("age");
                String gender = rs.getString("gender");
                String major = rs.getString("major");
                
                System.out.printf("ID: %d, 姓名: %s, 年龄: %d, 性别: %s, 专业: %s%n",
                        id, name, age, gender, major);
            }
        } finally {
            // 关闭资源
            if (rs != null) rs.close();
            if (stmt != null) stmt.close();
        }
    }
    
    /**
     * 添加学生
     */
    public static void addStudent(Connection conn, String name, int age, String gender, String major) throws SQLException {
        Statement stmt = null;
        try {
            stmt = conn.createStatement();
            String sql = String.format(
                "INSERT INTO students (name, age, gender, major) VALUES ('%s', %d, '%s', '%s')",
                name, age, gender, major
            );
            int rows = stmt.executeUpdate(sql);
            System.out.println("添加了 " + rows + " 条记录");
        } finally {
            if (stmt != null) stmt.close();
        }
    }
    
    /**
     * 更新学生年龄
     */
    public static void updateStudentAge(Connection conn, int id, int newAge) throws SQLException {
        Statement stmt = null;
        try {
            stmt = conn.createStatement();
            String sql = "UPDATE students SET age = " + newAge + " WHERE id = " + id;
            int rows = stmt.executeUpdate(sql);
            System.out.println("更新了 " + rows + " 条记录");
        } finally {
            if (stmt != null) stmt.close();
        }
    }
    
    /**
     * 删除学生
     */
    public static void deleteStudent(Connection conn, int id) throws SQLException {
        Statement stmt = null;
        try {
            stmt = conn.createStatement();
            String sql = "DELETE FROM students WHERE id = " + id;
            int rows = stmt.executeUpdate(sql);
            System.out.println("删除了 " + rows + " 条记录");
        } finally {
            if (stmt != null) stmt.close();
        }
    }
}

注意:运行此程序前,需要添加 MySQL JDBC 驱动。如果使用 Maven 项目,可以在 pom.xml 中添加以下依赖:

xml

代码语言:javascript
复制
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.28</version>
</dependency>

如果是非 Maven 项目,需要下载 mysql-connector-java.jar 并添加到项目的类路径中。

16.6 使用 PreparedStatement 对象

PreparedStatementStatement的子接口,它可以预编译 SQL 语句,提高执行效率,并且可以防止 SQL 注入攻击,是推荐使用的方式。

16.6.1 创建 PreparedStatement 对象

通过Connection.prepareStatement()方法创建PreparedStatement对象:

代码语言:javascript
复制
String sql = "SELECT * FROM students WHERE age > ?";
PreparedStatement pstmt = conn.prepareStatement(sql);

?是参数占位符,将在执行前设置具体的值。

16.6.2 带参数的 SQL 语句

使用PreparedStatementsetXxx()方法设置参数,然后执行 SQL 语句:

代码语言:javascript
复制
import java.sql.*;

/**
 * PreparedStatement使用示例
 */
public class PreparedStatementDemo {
    private static final String URL = "jdbc:mysql://localhost:3306/studentdb?useSSL=false&serverTimezone=UTC";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "123456";

    public static void main(String[] args) {
        Connection conn = null;
        try {
            // 加载驱动并建立连接
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            
            // 使用PreparedStatement查询年龄大于20的学生
            System.out.println("===== 年龄大于20的学生 =====");
            queryStudentsByAge(conn, 20);
            
            // 使用PreparedStatement添加学生
            System.out.println("\n===== 添加新学生 =====");
            addStudent(conn, "孙七", 22, "女", "物联网工程");
            
            // 使用PreparedStatement更新学生信息
            System.out.println("\n===== 更新学生专业 =====");
            updateStudentMajor(conn, 5, "智能科学与技术");
            
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭连接
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    /**
     * 查询年龄大于指定值的学生
     */
    public static void queryStudentsByAge(Connection conn, int minAge) throws SQLException {
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            // 创建PreparedStatement对象
            String sql = "SELECT id, name, age, gender, major FROM students WHERE age > ?";
            pstmt = conn.prepareStatement(sql);
            
            // 设置参数(参数索引从1开始)
            pstmt.setInt(1, minAge);
            
            // 执行查询
            rs = pstmt.executeQuery();
            
            // 处理结果
            while (rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                int age = rs.getInt("age");
                String gender = rs.getString("gender");
                String major = rs.getString("major");
                
                System.out.printf("ID: %d, 姓名: %s, 年龄: %d, 性别: %s, 专业: %s%n",
                        id, name, age, gender, major);
            }
        } finally {
            // 关闭资源
            if (rs != null) rs.close();
            if (pstmt != null) pstmt.close();
        }
    }
    
    /**
     * 添加学生
     */
    public static void addStudent(Connection conn, String name, int age, String gender, String major) throws SQLException {
        PreparedStatement pstmt = null;
        try {
            String sql = "INSERT INTO students (name, age, gender, major) VALUES (?, ?, ?, ?)";
            pstmt = conn.prepareStatement(sql);
            
            // 设置参数
            pstmt.setString(1, name);
            pstmt.setInt(2, age);
            pstmt.setString(3, gender);
            pstmt.setString(4, major);
            
            // 执行更新
            int rows = pstmt.executeUpdate();
            System.out.println("添加了 " + rows + " 条记录");
        } finally {
            if (pstmt != null) pstmt.close();
        }
    }
    
    /**
     * 更新学生专业
     */
    public static void updateStudentMajor(Connection conn, int id, String newMajor) throws SQLException {
        PreparedStatement pstmt = null;
        try {
            String sql = "UPDATE students SET major = ? WHERE id = ?";
            pstmt = conn.prepareStatement(sql);
            
            // 设置参数
            pstmt.setString(1, newMajor);
            pstmt.setInt(2, id);
            
            // 执行更新
            int rows = pstmt.executeUpdate();
            System.out.println("更新了 " + rows + " 条记录");
        } finally {
            if (pstmt != null) pstmt.close();
        }
    }
}

PreparedStatementStatement相比有以下优势:

  1. 性能更好:预编译的 SQL 语句可以重复执行,提高效率
  2. 更安全:可以防止 SQL 注入攻击
  3. 代码更清晰:将 SQL 语句与参数分离,便于维护

16.7 DAO 设计模式

DAO(Data Access Object,数据访问对象)设计模式用于将数据访问逻辑与业务逻辑分离,提供一种统一的方式来访问数据。

DAO 模式的主要组件:

  • 实体类(Entity/Model):对应数据库中的表,封装数据
  • DAO 接口:定义数据访问的方法
  • DAO 实现类:实现 DAO 接口,具体处理数据库操作
  • DAO 工厂类:创建 DAO 实例

下面是一个使用 DAO 模式操作学生信息的完整示例:

  1. 学生实体类
代码语言:javascript
复制
/**
 * 学生实体类
 */
public class Student {
    private int id;
    private String name;
    private int age;
    private String gender;
    private String major;
    
    // 构造方法
    public Student() {}
    
    public Student(String name, int age, String gender, String major) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.major = major;
    }
    
    public Student(int id, String name, int age, String gender, String major) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.major = major;
    }
    
    // getter和setter方法
    public int getId() {
        return id;
    }
    
    public void setId(int id) {
        this.id = id;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
    
    public String getGender() {
        return gender;
    }
    
    public void setGender(String gender) {
        this.gender = gender;
    }
    
    public String getMajor() {
        return major;
    }
    
    public void setMajor(String major) {
        this.major = major;
    }
    
    @Override
    public String toString() {
        return "Student{id=" + id + ", name='" + name + "', age=" + age + 
               ", gender='" + gender + "', major='" + major + "'}";
    }
}

2.学生 DAO 接口

代码语言:javascript
复制
import java.util.List;

/**
 * 学生DAO接口
 */
public interface StudentDAO {
    // 添加学生
    void addStudent(Student student) throws SQLException;
    
    // 根据ID查询学生
    Student getStudentById(int id) throws SQLException;
    
    // 查询所有学生
    List<Student> getAllStudents() throws SQLException;
    
    // 更新学生信息
    void updateStudent(Student student) throws SQLException;
    
    // 删除学生
    void deleteStudent(int id) throws SQLException;
}

3.学生 DAO 实现类

代码语言:javascript
复制
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 学生DAO实现类
 */
public class StudentDAOImpl implements StudentDAO {
    private Connection conn;
    
    // 构造方法,接收数据库连接
    public StudentDAOImpl(Connection conn) {
        this.conn = conn;
    }
    
    @Override
    public void addStudent(Student student) throws SQLException {
        PreparedStatement pstmt = null;
        try {
            String sql = "INSERT INTO students (name, age, gender, major) VALUES (?, ?, ?, ?)";
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, student.getName());
            pstmt.setInt(2, student.getAge());
            pstmt.setString(3, student.getGender());
            pstmt.setString(4, student.getMajor());
            pstmt.executeUpdate();
        } finally {
            if (pstmt != null) pstmt.close();
        }
    }
    
    @Override
    public Student getStudentById(int id) throws SQLException {
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            String sql = "SELECT id, name, age, gender, major FROM students WHERE id = ?";
            pstmt = conn.prepareStatement(sql);
            pstmt.setInt(1, id);
            rs = pstmt.executeQuery();
            
            if (rs.next()) {
                return new Student(
                    rs.getInt("id"),
                    rs.getString("name"),
                    rs.getInt("age"),
                    rs.getString("gender"),
                    rs.getString("major")
                );
            }
            return null;
        } finally {
            if (rs != null) rs.close();
            if (pstmt != null) pstmt.close();
        }
    }
    
    @Override
    public List<Student> getAllStudents() throws SQLException {
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            String sql = "SELECT id, name, age, gender, major FROM students";
            pstmt = conn.prepareStatement(sql);
            rs = pstmt.executeQuery();
            
            List<Student> students = new ArrayList<>();
            while (rs.next()) {
                Student student = new Student(
                    rs.getInt("id"),
                    rs.getString("name"),
                    rs.getInt("age"),
                    rs.getString("gender"),
                    rs.getString("major")
                );
                students.add(student);
            }
            return students;
        } finally {
            if (rs != null) rs.close();
            if (pstmt != null) pstmt.close();
        }
    }
    
    @Override
    public void updateStudent(Student student) throws SQLException {
        PreparedStatement pstmt = null;
        try {
            String sql = "UPDATE students SET name = ?, age = ?, gender = ?, major = ? WHERE id = ?";
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, student.getName());
            pstmt.setInt(2, student.getAge());
            pstmt.setString(3, student.getGender());
            pstmt.setString(4, student.getMajor());
            pstmt.setInt(5, student.getId());
            pstmt.executeUpdate();
        } finally {
            if (pstmt != null) pstmt.close();
        }
    }
    
    @Override
    public void deleteStudent(int id) throws SQLException {
        PreparedStatement pstmt = null;
        try {
            String sql = "DELETE FROM students WHERE id = ?";
            pstmt = conn.prepareStatement(sql);
            pstmt.setInt(1, id);
            pstmt.executeUpdate();
        } finally {
            if (pstmt != null) pstmt.close();
        }
    }
}

4.DAO 工厂类

代码语言:javascript
复制
import java.sql.Connection;

/**
 * DAO工厂类
 */
public class DAOFactory {
    // 获取StudentDAO实例
    public static StudentDAO getStudentDAO(Connection conn) {
        return new StudentDAOImpl(conn);
    }
}

5.测试类

代码语言:javascript
复制
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.List;

/**
 * DAO模式测试类
 */
public class DAODemo {
    private static final String URL = "jdbc:mysql://localhost:3306/studentdb?useSSL=false&serverTimezone=UTC";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "123456";

    public static void main(String[] args) {
        Connection conn = null;
        try {
            // 加载驱动并建立连接
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            
            // 获取StudentDAO实例
            StudentDAO studentDAO = DAOFactory.getStudentDAO(conn);
            
            // 添加学生
            System.out.println("===== 添加学生 =====");
            Student newStudent = new Student("周八", 21, "男", "自动化");
            studentDAO.addStudent(newStudent);
            System.out.println("添加学生成功");
            
            // 查询所有学生
            System.out.println("\n===== 所有学生 =====");
            List<Student> students = studentDAO.getAllStudents();
            for (Student s : students) {
                System.out.println(s);
            }
            
            // 根据ID查询学生
            System.out.println("\n===== 查询ID为1的学生 =====");
            Student student = studentDAO.getStudentById(1);
            System.out.println(student);
            
            // 更新学生信息
            System.out.println("\n===== 更新学生信息 =====");
            if (student != null) {
                student.setAge(22);
                studentDAO.updateStudent(student);
                System.out.println("更新后:" + studentDAO.getStudentById(1));
            }
            
            // 删除学生
            System.out.println("\n===== 删除最后一个学生 =====");
            if (!students.isEmpty()) {
                int lastId = students.get(students.size() - 1).getId();
                studentDAO.deleteStudent(lastId);
                System.out.println("删除后所有学生:");
                for (Student s : studentDAO.getAllStudents()) {
                    System.out.println(s);
                }
            }
            
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭连接
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

DAO 设计模式的优点:

  • 分离数据访问逻辑和业务逻辑,降低耦合度
  • 便于维护和扩展,如果数据库类型改变,只需修改 DAO 实现类
  • 提高代码复用性

16.8 可滚动和可更新的 ResultSet

默认情况下,ResultSet只能向前移动,并且是只读的。但通过设置Statement的参数,可以创建可滚动和可更新的ResultSet

16.8.1 可滚动的 ResultSet

可滚动的ResultSet允许在结果集中自由移动,可以向前、向后,甚至直接跳到特定行。

创建可滚动的Statement需要指定两个参数:

  • 结果集类型:ResultSet.TYPE_SCROLL_INSENSITIVEResultSet.TYPE_SCROLL_SENSITIVE
  • 结果集并发性:ResultSet.CONCUR_READ_ONLY(默认,只读)
代码语言:javascript
复制
import java.sql.*;

/**
 * 可滚动的ResultSet示例
 */
public class ScrollableResultSetDemo {
    private static final String URL = "jdbc:mysql://localhost:3306/studentdb?useSSL=false&serverTimezone=UTC";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "123456";

    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        
        try {
            // 加载驱动并建立连接
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            
            // 创建可滚动的Statement
            // 参数1:ResultSet.TYPE_SCROLL_INSENSITIVE 表示可滚动且对数据库的更改不敏感
            // 参数2:ResultSet.CONCUR_READ_ONLY 表示只读
            stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
            
            // 执行查询
            String sql = "SELECT id, name, age FROM students";
            rs = stmt.executeQuery(sql);
            
            System.out.println("===== 正向遍历 =====");
            while (rs.next()) {
                System.out.printf("ID: %d, 姓名: %s, 年龄: %d%n",
                        rs.getInt("id"), rs.getString("name"), rs.getInt("age"));
            }
            
            System.out.println("\n===== 反向遍历 =====");
            while (rs.previous()) {
                System.out.printf("ID: %d, 姓名: %s, 年龄: %d%n",
                        rs.getInt("id"), rs.getString("name"), rs.getInt("age"));
            }
            
            System.out.println("\n===== 移动到第三行 =====");
            rs.absolute(3); // 移动到绝对位置
            System.out.printf("ID: %d, 姓名: %s, 年龄: %d%n",
                    rs.getInt("id"), rs.getString("name"), rs.getInt("age"));
            
            System.out.println("\n===== 移动到最后一行 =====");
            rs.last();
            System.out.printf("ID: %d, 姓名: %s, 年龄: %d%n",
                    rs.getInt("id"), rs.getString("name"), rs.getInt("age"));
            
            System.out.println("\n===== 移动到第一行 =====");
            rs.first();
            System.out.printf("ID: %d, 姓名: %s, 年龄: %d%n",
                    rs.getInt("id"), rs.getString("name"), rs.getInt("age"));
            
            System.out.println("\n===== 当前行号 =====");
            System.out.println("当前行号: " + rs.getRow());
            
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            try { if (rs != null) rs.close(); } catch (SQLException e) { e.printStackTrace(); }
            try { if (stmt != null) stmt.close(); } catch (SQLException e) { e.printStackTrace(); }
            try { if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); }
        }
    }
}

ResultSet的常用移动方法:

  • next():移动到下一行
  • previous():移动到上一行
  • first():移动到第一行
  • last():移动到最后一行
  • absolute(int row):移动到指定行(正数从开头计数,负数从结尾计数)
  • relative(int rows):相对当前位置移动指定行数
  • beforeFirst():移动到第一行之前
  • afterLast():移动到最后一行之后
  • getRow():获取当前行号
16.8.2 可更新的 ResultSet

可更新的ResultSet允许直接通过结果集修改数据库中的数据。

创建可更新的Statement需要指定两个参数:

  • 结果集类型:ResultSet.TYPE_SCROLL_INSENSITIVE
  • 结果集并发性:ResultSet.CONCUR_UPDATABLE
代码语言:javascript
复制
import java.sql.*;

/**
 * 可更新的ResultSet示例
 */
public class UpdatableResultSetDemo {
    private static final String URL = "jdbc:mysql://localhost:3306/studentdb?useSSL=false&serverTimezone=UTC";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "123456";

    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        
        try {
            // 加载驱动并建立连接
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            
            // 创建可更新的Statement
            // 参数1:ResultSet.TYPE_SCROLL_INSENSITIVE 表示可滚动
            // 参数2:ResultSet.CONCUR_UPDATABLE 表示可更新
            stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
            
            // 查询数据
            String sql = "SELECT id, name, age, major FROM students";
            rs = stmt.executeQuery(sql);
            
            System.out.println("===== 更新前 =====");
            while (rs.next()) {
                System.out.printf("ID: %d, 姓名: %s, 年龄: %d, 专业: %s%n",
                        rs.getInt("id"), rs.getString("name"), rs.getInt("age"), rs.getString("major"));
            }
            
            // 更新数据
            System.out.println("\n===== 执行更新 =====");
            
            // 更新特定行
            rs.absolute(2); // 移动到第二行
            rs.updateInt("age", 23); // 更新年龄
            rs.updateRow(); // 提交更新
            
            // 插入新行
            rs.moveToInsertRow(); // 移动到插入行
            rs.updateString("name", "吴九");
            rs.updateInt("age", 20);
            rs.updateString("gender", "男");
            rs.updateString("major", "电子信息工程");
            rs.insertRow(); // 插入新行
            rs.moveToCurrentRow(); // 移动回原来的位置
            
            // 删除行
            rs.last(); // 移动到最后一行
            int deleteId = rs.getInt("id");
            rs.deleteRow(); // 删除当前行
            System.out.println("删除了ID为 " + deleteId + " 的学生");
            
            // 显示更新后的数据
            System.out.println("\n===== 更新后 =====");
            rs.beforeFirst(); // 移动到结果集开头
            while (rs.next()) {
                System.out.printf("ID: %d, 姓名: %s, 年龄: %d, 专业: %s%n",
                        rs.getInt("id"), rs.getString("name"), rs.getInt("age"), rs.getString("major"));
            }
            
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            try { if (rs != null) rs.close(); } catch (SQLException e) { e.printStackTrace(); }
            try { if (stmt != null) stmt.close(); } catch (SQLException e) { e.printStackTrace(); }
            try { if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); }
        }
    }
}

使用可更新的ResultSet修改数据的方法:

  • 更新现有行:
    1. 移动到要更新的行
    2. 使用updateXxx()方法修改列值
    3. 调用updateRow()提交更新
  • 插入新行:
    1. 调用moveToInsertRow()移动到插入行
    2. 使用updateXxx()方法设置新行的列值
    3. 调用insertRow()插入新行
    4. 调用moveToCurrentRow()移动回原来的位置
  • 删除行:
    1. 移动到要删除的行
    2. 调用deleteRow()删除行

注意:不是所有的查询都可以生成可更新的ResultSet,通常需要满足以下条件:

  1. 查询的是单个表
  2. 查询中包含表的主键
  3. 没有使用SELECT *(虽然 MySQL 允许,但不推荐)

16.9 小结

本章主要介绍了 JDBC 数据库编程的相关知识,包括:

  1. 数据库系统基础知识,包括关系数据库和 SQL 语言
  2. MySQL 数据库的安装、配置和基本操作
  3. JDBC 体系结构和核心 API
  4. 使用 JDBC 访问数据库的基本步骤:加载驱动、建立连接、创建语句、执行 SQL、处理结果、关闭资源
  5. PreparedStatement的使用,它比Statement更安全、高效
  6. DAO 设计模式,用于分离数据访问逻辑和业务逻辑
  7. 可滚动和可更新的ResultSet的使用

掌握 JDBC 数据库编程是 Java 开发的重要技能,它使 Java 应用程序能够与各种关系型数据库进行交互,实现数据的存储、查询、更新和删除等操作。

编程练习

  1. 练习 1:学生成绩管理系统 设计一个学生成绩管理系统,实现以下功能:
    • 添加学生信息(学号、姓名、性别、年龄)
    • 添加学生成绩(学号、课程名、成绩)
    • 查询指定学生的所有成绩
    • 查询指定课程的所有学生成绩
    • 更新学生成绩
    • 删除学生信息及其成绩

    要求:使用 DAO 设计模式,使用PreparedStatement防止 SQL 注入。

  2. 练习 2:图书管理系统 设计一个简单的图书管理系统,实现以下功能:
    • 添加图书(图书编号、书名、作者、出版社、出版日期、库存数量)
    • 借阅图书(记录借阅人、图书编号、借阅日期、应还日期)
    • 归还图书(更新归还日期和库存)
    • 查询图书信息
    • 查询图书借阅记录

    要求:使用可更新的ResultSet实现部分功能,使用事务保证数据一致性。

以上练习的参考答案可以通过实际开发来完成,建议按照本章介绍的 DAO 模式进行设计,将数据访问逻辑与业务逻辑分离,提高代码的可维护性和可扩展性。

希望本章的内容能帮助你掌握 JDBC 数据库编程的核心技能,为后续的 Java 开发打下坚实的基础。如果有任何疑问或建议,欢迎在评论区留言讨论!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-01-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 思维导图
  • 16.1 数据库系统简介
    • 16.1.1 关系数据库简述
    • 16.1.2 数据库语言 SQL
  • 16.2 MySQL 数据库
    • 16.2.1 MySQL 的下载与安装
    • 16.2.2 使用 MySQL 命令行工具
    • 16.2.3 使用 Navicat 操作数据库
  • 16.3 JDBC 体系结构
    • 16.3.1 JDBC 访问数据库
    • 16.3.2 JDBC API 介绍
  • 16.4 数据库访问步骤
    • 16.4.1 加载驱动程序
    • 16.4.2 建立连接对象
    • 16.4.3 创建语句对象
    • 16.4.4 ResultSet 对象
    • 16.4.5 关闭有关对象
  • 16.5 访问 MySQL 数据库
    • 16.5.1 创建数据库和表
    • 16.5.2 访问 MySQL 数据库
  • 16.6 使用 PreparedStatement 对象
    • 16.6.1 创建 PreparedStatement 对象
    • 16.6.2 带参数的 SQL 语句
  • 16.7 DAO 设计模式
  • 16.8 可滚动和可更新的 ResultSet
    • 16.8.1 可滚动的 ResultSet
    • 16.8.2 可更新的 ResultSet
  • 16.9 小结
  • 编程练习
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档