当前位置: 首页 > news >正文

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

前言

        在当今的软件开发中,几乎所有应用程序都需要与数据库进行交互。无论是小型应用还是大型企业系统,数据的存储、检索和管理都是核心功能。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 语句示例:

-- 创建表
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

    • 访问 MySQL 官方网站:https://dev.mysql.com/downloads/mysql/
    • 根据操作系统选择合适的版本下载,推荐下载 MySQL Community Server(社区版)
  2. 安装 MySQL

    • Windows 系统:运行下载的安装文件,按照向导进行安装,可以选择 "Developer Default" 安装类型
    • macOS 系统:可以使用 DMG 文件安装,或者通过 Homebrew 安装
    • Linux 系统:可以使用包管理器安装,如apt-get install mysql-server(Ubuntu)
  3. 配置 MySQL

    • 安装过程中需要设置 root 用户密码
    • 可以选择是否允许远程访问
    • 配置端口号(默认 3306)

16.2.2 使用 MySQL 命令行工具

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

  1. 登录 MySQL

    mysql -u root -p
    
     

    输入密码后即可登录

  2. 常用命令

    -- 显示所有数据库
    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,可以通过以下方式加载:

// 加载MySQL驱动
Class.forName("com.mysql.cj.jdbc.Driver");

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

16.4.2 建立连接对象

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

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

// 数据库连接信息
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对象
// 创建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 类型数据
// 执行查询
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
// 关闭资源
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 创建数据库和表

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

-- 创建数据库
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 数据库,并执行查询、插入、更新和删除操作:

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

<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对象:

String sql = "SELECT * FROM students WHERE age > ?";
PreparedStatement pstmt = conn.prepareStatement(sql);

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

16.6.2 带参数的 SQL 语句

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

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. 学生实体类
/*** 学生实体类*/
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;}@Overridepublic String toString() {return "Student{id=" + id + ", name='" + name + "', age=" + age + ", gender='" + gender + "', major='" + major + "'}";}
}

   2.学生 DAO 接口

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 实现类

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;}@Overridepublic 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();}}@Overridepublic 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();}}@Overridepublic 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();}}@Overridepublic 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();}}@Overridepublic 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 工厂类

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

    5.测试类

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_INSENSITIVE 或 ResultSet.TYPE_SCROLL_SENSITIVE
  • 结果集并发性:ResultSet.CONCUR_READ_ONLY(默认,只读)
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
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 开发打下坚实的基础。如果有任何疑问或建议,欢迎在评论区留言讨论!

http://www.dtcms.com/a/308865.html

相关文章:

  • SpringBoot实战:高效Web开发
  • SpringBoot中异常的全局处理
  • 学习曲线之TS
  • SQL Server DATEADD()函数详解:时间计算的终极指南与实战案例
  • 可计算存储(Computational Storage)与DPU(Data Processing Unit)的技术特点对比及实际应用场景分析
  • 免费语音识别(ASR)服务深度指南​
  • 动态配置实现过程
  • 《黑马笔记》 --- C++ 提高编程
  • Winform C# 热力图制作要点
  • HOOPS Exchange技术架构全解析:打造高效CAD/BIM数据导入与导出引擎
  • 【go】格式化的输入和输出
  • 计算机网络知识【推荐!!!】按照OSI七层模型梳理
  • BGP高级特性之GTSM实验案例
  • 蓝牙数据包解析
  • mapper.xml中的<include>是什么
  • 【React】状态管理
  • Spring 面试点(八股)
  • review| advance
  • wxPython 实践(五)高级控件
  • 企业对于DDOS攻击有哪几种安全防护对策?
  • 选择跨网文件交换系统的核心因素有哪些?
  • Kafka Streams性能优化实践指南:实时流处理与状态管理
  • 脚手架搭建React项目
  • LCGL基本使用
  • 智慧园区通行效率↑68%!陌讯多模态融合算法的实战解析
  • 【C++】1·入门基础
  • C语言基础第18天:动态内存分配
  • 什么是 MySQL 的索引?常见的索引类型有哪些?
  • 【动态规划】数位dp
  • 【AD】域管理员登录错误