当前位置:首页 > 学海无涯 > 正文内容

Java 链接数据库与基础增删改查操作详解

清羽天2周前 (11-27)学海无涯13


在 Java 开发中,数据库交互是绝大多数应用的核心功能之一。无论是用户信息存储、业务数据统计还是日志记录,都需要通过 Java 程序与数据库建立连接并执行数据操作。本文将以 MySQL 数据库(最常用的关系型数据库之一)为例,从环境准备、数据库连接、基础增删改查(CRUD)操作到代码优化,一步步带大家掌握 Java 操作数据库的核心技能。

一、环境准备

在开始编码前,我们需要先完成 “工具 + 依赖” 的双重准备,确保后续操作能顺利进行。

1.1 核心工具与软件

  • JDK:推荐 JDK 8 及以上版本(本文使用 JDK 11),确保 Java 开发环境正常。

  • MySQL 数据库:推荐 MySQL 8.0 版本,需提前创建测试数据库和表(下文会提供建表语句)。

  • 开发工具:IntelliJ IDEA(或 Eclipse),用于编写和运行 Java 代码。

  • 数据库连接依赖:MySQL 官方提供的 JDBC 驱动(mysql-connector-java),用于 Java 程序与 MySQL 通信。

1.2 依赖配置(Maven 方式)

如果使用 Maven 管理项目,直接在 pom.xml 中添加 JDBC 驱动依赖(无需手动下载 JAR 包):
xml
<!-- MySQL JDBC 驱动(MySQL 8.0+ 对应此依赖) --><dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.32</version> <!-- 版本可根据 MySQL 版本调整 -->
    <scope>runtime</scope></dependency>
如果是普通 Java 项目,需手动下载 MySQL JDBC 驱动 JAR 包,并在 IDE 中添加到项目的 “库” 中。

1.3 测试数据库与表创建

打开 MySQL 客户端(如 Navicat、SQLyog 或命令行),执行以下 SQL 语句,创建测试用的数据库 java_db 和用户表 user
sql
-- 1. 创建数据库(若不存在)CREATE DATABASE IF NOT EXISTS java_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;-- 2. 使用数据库USE java_db;-- 3. 创建用户表(存储用户ID、姓名、年龄、邮箱)CREATE TABLE IF NOT EXISTS user (
    id INT PRIMARY KEY AUTO_INCREMENT, -- 主键,自增
    name VARCHAR(50) NOT NULL,         -- 姓名,非空
    age INT,                           -- 年龄
    email VARCHAR(100) UNIQUE          -- 邮箱,唯一) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

二、Java 链接数据库的核心:JDBC

JDBC(Java Database Connectivity)是 Java 提供的一套用于操作数据库的标准 API,它定义了 Java 程序与数据库交互的规范,而具体的实现由各数据库厂商提供(如 MySQL 的 mysql-connector-java)。

2.1 JDBC 连接数据库的 5 个核心步骤

  1. 加载 JDBC 驱动:MySQL 8.0+ 无需显式加载(驱动会自动注册),但需确保依赖已引入。

  2. 定义数据库连接 URL:格式为 jdbc:mysql://主机地址:端口号/数据库名?参数1&参数2

    • useSSL=false:禁用 SSL(开发环境常用,生产环境需根据需求调整)。

    • serverTimezone=UTC:设置时区(MySQL 8.0+ 必须指定,否则会报时区错误)。

    • 示例(本地 MySQL):jdbc:mysql://localhost:3306/java_db?useSSL=false&serverTimezone=UTC

    • 关键参数说明:

  3. 创建数据库连接:通过 DriverManager.getConnection(url, username, password) 获取 Connection 对象(数据库连接的核心对象)。

  4. 执行 SQL 语句:通过 Connection 创建 Statement 或 PreparedStatement 对象,执行 SQL 并处理结果。

  5. 关闭资源:依次关闭 ResultSet(结果集)、Statement(SQL 执行对象)、Connection(数据库连接),避免资源泄漏。

2.2 基础连接示例(含工具类封装)

直接在业务代码中写连接逻辑会导致冗余,因此我们先封装一个 JDBC 工具类,统一处理连接的创建和关闭:
java
import java.sql.*;/**
 * JDBC 工具类:封装数据库连接与资源关闭
 */public class JDBCUtils {
    // 数据库连接参数(建议放在配置文件中,此处为演示简化)
    private static final String URL = "jdbc:mysql://localhost:3306/java_db?useSSL=false&serverTimezone=UTC";
    private static final String USERNAME = "root"; // 你的 MySQL 用户名
    private static final String PASSWORD = "123456"; // 你的 MySQL 密码

    /**
     * 获取数据库连接
     * @return Connection 对象(若连接失败,抛出 SQLException)
     */
    public static Connection getConnection() throws SQLException {
        Connection conn = null;
        try {
            // MySQL 8.0+ 无需显式加载驱动(Driver 类会自动注册)
            // Class.forName("com.mysql.cj.jdbc.Driver"); // 旧版本需添加此句
            conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            System.out.println("数据库连接成功!");
        } catch (SQLException e) {
            System.out.println("数据库连接失败!");
            throw e; // 抛出异常,由调用者处理
        }
        return conn;
    }

    /**
     * 关闭资源(ResultSet + Statement + Connection)
     * @param rs 结果集(可null)
     * @param stmt SQL执行对象(可null)
     * @param conn 数据库连接(可null)
     */
    public static void closeResources(ResultSet rs, Statement stmt, Connection conn) {
        // 关闭顺序:先ResultSet,再Statement,最后Connection(避免资源泄漏)
        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.isClosed()) conn.close();
            System.out.println("数据库连接已关闭!");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }}
注意:实际开发中,URLUSERNAMEPASSWORD 应放在配置文件(如 jdbc.properties)中,而非硬编码,方便后期维护。

三、基础增删改查(CRUD)操作实现

有了 JDBC 工具类后,我们就可以基于它实现用户表 user 的增删改查操作。本文使用 PreparedStatement(而非 Statement),因为它能防止 SQL 注入,且性能更优(支持 SQL 预编译)。

3.1 1. 新增操作(Create)

功能:向 user 表插入一条新用户数据。
java
import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.SQLException;public class JDBCCreateDemo {
    public static void main(String[] args) {
        // 1. 声明需要关闭的资源(Connection、PreparedStatement)
        Connection conn = null;
        PreparedStatement pstmt = null;

        try {
            // 2. 获取数据库连接
            conn = JDBCUtils.getConnection();

            // 3. 定义SQL(? 为占位符,避免SQL注入)
            String sql = "INSERT INTO user (name, age, email) VALUES (?, ?, ?)";

            // 4. 创建PreparedStatement对象(预编译SQL)
            pstmt = conn.prepareStatement(sql);

            // 5. 为占位符赋值(参数索引从1开始)
            pstmt.setString(1, "张三");  // 第一个?:name
            pstmt.setInt(2, 25);        // 第二个?:age
            pstmt.setString(3, "zhangsan@example.com"); // 第三个?:email

            // 6. 执行SQL(新增/修改/删除用executeUpdate(),返回受影响的行数)
            int affectedRows = pstmt.executeUpdate();
            System.out.println("新增成功,受影响行数:" + affectedRows);

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 7. 关闭资源(调用工具类方法)
            JDBCUtils.closeResources(null, pstmt, conn);
        }
    }}

3.2 2. 查询操作(Read)

功能:根据用户 ID 查询用户信息,或查询所有用户信息。

3.2.1 按 ID 查询单个用户

java
import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;public class JDBCReadOneDemo {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null; // 查询操作需要ResultSet存储结果

        try {
            conn = JDBCUtils.getConnection();

            // 定义SQL:根据id查询用户
            String sql = "SELECT id, name, age, email FROM user WHERE id = ?";
            pstmt = conn.prepareStatement(sql);
            pstmt.setInt(1, 1); // 查询id=1的用户

            // 执行查询(用executeQuery(),返回ResultSet)
            rs = pstmt.executeQuery();

            // 处理结果集(rs.next()判断是否有下一条数据)
            if (rs.next()) {
                // 通过列名或列索引获取数据(推荐列名,避免索引变动导致错误)
                int id = rs.getInt("id");
                String name = rs.getString("name");
                int age = rs.getInt("age");
                String email = rs.getString("email");

                // 输出用户信息
                System.out.println("查询到的用户:");
                System.out.println("ID:" + id + ",姓名:" + name + ",年龄:" + age + ",邮箱:" + email);
            } else {
                System.out.println("未查询到该用户!");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源(需传入ResultSet)
            JDBCUtils.closeResources(rs, pstmt, conn);
        }
    }}

3.2.2 查询所有用户

只需修改 SQL 语句和结果集处理逻辑即可:
java
// 替换SQL语句String sql = "SELECT id, name, age, email FROM user";// 处理结果集(循环获取所有数据)while (rs.next()) {
    int id = rs.getInt("id");
    String name = rs.getString("name");
    int age = rs.getInt("age");
    String email = rs.getString("email");
    System.out.println("ID:" + id + ",姓名:" + name + ",年龄:" + age + ",邮箱:" + email);}

3.3 3. 修改操作(Update)

功能:根据用户 ID 修改用户的年龄或邮箱。
java
import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.SQLException;public class JDBCUpdateDemo {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement pstmt = null;

        try {
            conn = JDBCUtils.getConnection();

            // 定义SQL:修改id=1的用户年龄和邮箱
            String sql = "UPDATE user SET age = ?, email = ? WHERE id = ?";
            pstmt = conn.prepareStatement(sql);

            // 为占位符赋值
            pstmt.setInt(1, 26); // 新年龄
            pstmt.setString(2, "zhangsan_new@example.com"); // 新邮箱
            pstmt.setInt(3, 1); // 目标用户ID

            // 执行修改(executeUpdate()返回受影响行数)
            int affectedRows = pstmt.executeUpdate();
            if (affectedRows > 0) {
                System.out.println("修改成功!");
            } else {
                System.out.println("修改失败,未找到该用户!");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResources(null, pstmt, conn);
        }
    }}

3.4 4. 删除操作(Delete)

功能:根据用户 ID 删除用户数据。
java
import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.SQLException;public class JDBCDeleteDemo {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement pstmt = null;

        try {
            conn = JDBCUtils.getConnection();

            // 定义SQL:删除id=1的用户
            String sql = "DELETE FROM user WHERE id = ?";
            pstmt = conn.prepareStatement(sql);
            pstmt.setInt(1, 1);

            // 执行删除
            int affectedRows = pstmt.executeUpdate();
            if (affectedRows > 0) {
                System.out.println("删除成功!");
            } else {
                System.out.println("删除失败,未找到该用户!");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.closeResources(null, pstmt, conn);
        }
    }}

四、常见问题与优化建议

4.1 常见问题排查

  1. “时区错误”:MySQL 8.0+ 必须在 URL 中添加 serverTimezone=UTC(或 Asia/Shanghai)。

  2. “连接被拒绝”:检查 MySQL 是否启动、主机地址 / 端口号是否正确、用户是否有权限访问数据库。

  3. “SQL 注入风险”:严禁使用 Statement,必须用 PreparedStatement 并通过占位符赋值。

  4. “资源泄漏”:确保在 finally 中关闭所有资源(即使发生异常),或使用 Java 7+ 的 try-with-resources 语法自动关闭资源。

4.2 代码优化建议

  1. 使用配置文件存储连接参数:将 URLUSERNAMEPASSWORD 放在 jdbc.properties 中,通过 Properties 类读取,避免硬编码。
    properties
    # jdbc.propertiesjdbc.url=jdbc:mysql://localhost:3306/java_db?useSSL=false&serverTimezone=UTCjdbc.username=rootjdbc.password=123456
    读取方式:
    java
    public static Connection getConnection() throws SQLException, IOException {
        Properties props = new Properties();
        // 读取配置文件(放在src/main/resources目录下)
        props.load(JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties"));
        String url = props.getProperty("jdbc.url");
        String username = props.getProperty("jdbc.username");
        String password = props.getProperty("jdbc.password");
        return DriverManager.getConnection(url, username, password);}
  2. 使用 try-with-resources 自动关闭资源:Java 7+ 提供的语法,无需手动在 finally 中关闭资源(资源需实现 AutoCloseable 接口,JDBC 相关类均已实现)。
    java
    // 示例:查询操作(自动关闭conn、pstmt、rs)try (Connection conn = JDBCUtils.getConnection();
         PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM user WHERE id = ?");
         ResultSet rs = pstmt.executeQuery()) {
         // 业务逻辑...} catch (SQLException e) {
        e.printStackTrace();}
  3. 使用数据库连接池:频繁创建 / 关闭 Connection 会消耗大量资源,实际开发中需使用连接池(如 HikariCP、C3P0),复用连接对象,提升性能。

五、总结

本文从环境准备到代码实现,详细讲解了 Java 基于 JDBC 操作 MySQL 数据库的核心流程:
  1. 掌握 JDBC 连接数据库的 5 个步骤,理解 ConnectionPreparedStatementResultSet 的作用。

  2. 实现基础增删改查操作,重点关注 PreparedStatement 的占位符用法(防 SQL 注入)。

  3. 学会封装工具类、处理资源关闭,避免冗余代码和资源泄漏。

JDBC 是 Java 操作数据库的基础,后续学习的 MyBatis、Spring Data JPA 等框架,本质上都是对 JDBC 的封装。掌握本文内容后,再学习框架会更加轻松。建议大家多动手实践,尝试修改 SQL 语句、处理异常场景,加深对 JDBC 的理解。


分享给朋友:

“Java 链接数据库与基础增删改查操作详解” 的相关文章

Spring Boot 过滤器入门:从概念到实战配置

在 Web 开发中,过滤器(Filter)是处理 HTTP 请求和响应的重要组件,它能在请求到达控制器前、响应返回客户端前进行拦截和处理。比如日志记录、权限验证、字符编码转换等场景,都离不开过滤器的身影。本文将带大家从零开始,掌握 Spring Boot 中过滤器的入门知识和完整设置流程。一、过滤器...

Python 链接数据库与基础增删改查(CRUD)操作详解

在 Python 开发中,数据库交互是后端开发、数据分析、自动化脚本等场景的核心能力 —— 无论是存储用户数据、处理业务逻辑,还是批量分析数据,都需要 Python 与数据库建立连接并执行操作。本文以 MySQL 数据库(Python 生态最常用的关系型数据库)为例,从环境准备、数据库连接...

PHP 链接数据库与基础增删改查(CRUD)操作详解

在 Web 开发中,PHP 与数据库的交互是动态网站的核心能力 —— 无论是用户登录注册、数据展示还是业务逻辑处理,都离不开 PHP 对数据库的增删改查操作。本文将以 MySQL 数据库(PHP 生态最常用的关系型数据库)为例,从环境准备、数据库连接、核心 CRUD 实现到安全优化,一步步...

Unity 场景转换功能实现全指南:从基础到进阶

场景转换是几乎所有 Unity 项目都必备的核心功能,无论是简单的场景切换还是带有加载动画的复杂过渡,都直接影响着玩家的体验。本文将从基础原理出发,逐步讲解如何在 Unity 中实现各种场景转换效果,帮助开发者打造流畅自然的场景过渡体验。一、场景转换的基本原理在 Unity 中,场景转换本质上是卸载...

Unity 开发实战:在游戏中嵌入拼图玩法系统

拼图游戏作为一种经典的益智玩法,非常适合嵌入各类游戏中作为休闲模块、解谜环节或奖励机制。本文将详细介绍如何在 Unity 中设计并实现一个可复用的拼图游戏系统,包括核心逻辑、UI 交互和扩展功能。一、拼图游戏核心需求分析一个灵活的拼图系统应具备以下功能:支持不同尺寸的拼图(如 3x3、4x4、5x5...

Unity 开发实战:实现逼真的作物生长系统

作物生长系统是农场类、生存类游戏的核心玩法之一,一个设计精良的作物生长系统能极大提升游戏的沉浸感。本文将详细介绍如何在 Unity 中构建一个完整的作物生长系统,包括生长周期、环境影响、交互逻辑和可视化表现。一、作物生长系统核心需求分析一个真实的作物生长系统应包含以下核心要素:多阶段生长周期(种子→...