杰瑞科技汇

Java JDBC增删改查如何实现?

JDBC (Java Database Connectivity) 是 Java 语言用于规范客户端程序如何访问数据库的应用程序接口,它提供了诸如查询和更新数据库中数据的方法。

本文将以 MySQL 数据库为例,因为它是目前最流行的开源关系型数据库之一,但核心概念和代码结构同样适用于其他数据库(如 Oracle, PostgreSQL, SQL Server 等),只需更换相应的驱动类和连接字符串即可。


第一步:准备工作

在开始编写代码之前,请确保你已经完成了以下准备工作:

  1. 安装 MySQL 数据库:并确保服务正在运行。

  2. 创建一个测试数据库:创建一个名为 test_db 的数据库。

  3. 创建一张测试表:在 test_db 中创建一张 users 表。

    -- 进入 test_db 数据库
    USE test_db;
    -- 创建 users 表
    CREATE TABLE users (
        id INT PRIMARY KEY AUTO_INCREMENT,
        name VARCHAR(50) NOT NULL,
        email VARCHAR(100) UNIQUE NOT NULL,
        age INT
    );
  4. 添加 JDBC 驱动:你需要将 MySQL 的 JDBC 驱动(JAR 文件)添加到你的项目中。

    • Maven 项目:在 pom.xml 中添加依赖。
      <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>8.0.33</version> <!-- 使用与你的MySQL版本匹配的最新版本 -->
      </dependency>
    • 普通 Java 项目:从 Maven 中央仓库 下载 JAR 文件,并将其添加到项目的类路径(Classpath)中。

第二步:建立数据库连接

所有 JDBC 操作都始于一个 Connection 对象,这个对象代表与数据库的物理连接,为了管理资源(如连接、语句、结果集),我们强烈推荐使用 try-with-resources 语句,它能自动关闭实现了 AutoCloseable 接口的对象。

数据库连接信息

  • URL: jdbc:mysql://localhost:3306/test_db
  • 用户名: root
  • 密码: your_password (替换成你自己的密码)
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DBUtil {
    // 数据库URL, 格式: jdbc:mysql://主机名:端口号/数据库名
    private static final String URL = "jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC";
    // 用户名
    private static final String USER = "root";
    // 密码
    private static final String PASSWORD = "your_password";
    /**
     * 获取数据库连接
     * @return Connection 对象
     * @throws SQLException 如果连接失败
     */
    public static Connection getConnection() throws SQLException {
        try {
            // 1. 加载JDBC驱动 (对于较新版本的JDBC驱动,这步通常是可选的)
            Class.forName("com.mysql.cj.jdbc.Driver");
            // 2. 获取连接
            return DriverManager.getConnection(URL, USER, PASSWORD);
        } catch (ClassNotFoundException e) {
            throw new SQLException("MySQL JDBC Driver not found.", e);
        }
    }
}

注意?useSSL=false&serverTimezone=UTC 是为了避免 SSL 警告和时区问题,在生产环境中请根据实际情况配置。


第三步:核心 CRUD 操作

我们将分别实现增、删、改、查。

查询 - SELECT

查询操作通常涉及 Connection, PreparedStatement, 和 ResultSet

  • Connection: 建立连接。
  • PreparedStatement: 预编译SQL语句,可以防止SQL注入,并提高性能。
  • ResultSet: 查询结果集,它像一个指向数据行的游标。

示例:查询所有用户

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 SelectExample {
    public static List<User> getAllUsers() {
        String sql = "SELECT id, name, email, age FROM users";
        List<User> userList = new ArrayList<>();
        // 使用 try-with-resources 自动关闭资源
        try (Connection conn = DBUtil.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql);
             ResultSet rs = pstmt.executeQuery()) {
            // 遍历结果集
            while (rs.next()) {
                // 通过列名获取数据,更健壮
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String email = rs.getString("email");
                int age = rs.getInt("age");
                User user = new User(id, name, email, age);
                userList.add(user);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return userList;
    }
    // 用于存储用户数据的简单POJO类
    static class User {
        private int id;
        private String name;
        private String email;
        private int age;
        public User(int id, String name, String email, int age) {
            this.id = id;
            this.name = name;
            this.email = email;
            this.age = age;
        }
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", email='" + email + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    public static void main(String[] args) {
        List<User> users = getAllUsers();
        for (User user : users) {
            System.out.println(user);
        }
    }
}

增加 - INSERT

增加操作使用 executeUpdate() 方法,它会返回一个整数,表示受影响的行数。

示例:添加一个新用户

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class InsertExample {
    public static boolean addUser(String name, String email, int age) {
        String sql = "INSERT INTO users (name, email, age) VALUES (?, ?, ?)";
        try (Connection conn = DBUtil.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            // 使用 setXxx() 方法为占位符赋值
            pstmt.setString(1, name);
            pstmt.setString(2, email);
            pstmt.setInt(3, age);
            // executeUpdate() 返回受影响的行数
            int affectedRows = pstmt.executeUpdate();
            return affectedRows > 0;
        } catch (SQLException e) {
            e.printStackTrace();
            return false;
        }
    }
    public static void main(String[] args) {
        // 添加一个新用户
        boolean success = addUser("张三", "zhangsan@example.com", 30);
        if (success) {
            System.out.println("用户添加成功!");
        } else {
            System.out.println("用户添加失败!");
        }
    }
}

修改 - UPDATE

修改操作与增加操作非常相似,同样使用 executeUpdate() 方法,只是SQL语句不同。

示例:根据ID更新用户信息

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class UpdateExample {
    public static boolean updateUser(int id, String newEmail, int newAge) {
        String sql = "UPDATE users SET email = ?, age = ? WHERE id = ?";
        try (Connection conn = DBUtil.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setString(1, newEmail);
            pstmt.setInt(2, newAge);
            pstmt.setInt(3, id);
            int affectedRows = pstmt.executeUpdate();
            return affectedRows > 0;
        } catch (SQLException e) {
            e.printStackTrace();
            return false;
        }
    }
    public static void main(String[] args) {
        // 假设要更新ID为1的用户
        boolean success = updateUser(1, "zhangsan_new@example.com", 31);
        if (success) {
            System.out.println("用户信息更新成功!");
        } else {
            System.out.println("用户信息更新失败!(可能用户不存在)");
        }
    }
}

删除 - DELETE

删除操作也使用 executeUpdate() 方法。

示例:根据ID删除用户

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class DeleteExample {
    public static boolean deleteUser(int id) {
        String sql = "DELETE FROM users WHERE id = ?";
        try (Connection conn = DBUtil.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setInt(1, id);
            int affectedRows = pstmt.executeUpdate();
            return affectedRows > 0;
        } catch (SQLException e) {
            e.printStackTrace();
            return false;
        }
    }
    public static void main(String[] args) {
        // 假设要删除ID为1的用户
        boolean success = deleteUser(1);
        if (success) {
            System.out.println("用户删除成功!");
        } else {
            System.out.println("用户删除失败!(可能用户不存在)");
        }
    }
}

第四步:事务管理

事务是一组操作的集合,它们要么全部成功,要么全部失败,这在保证数据一致性方面至关重要。

ACID 特性

  • 原子性:事务中的所有操作,要么全部完成,要么全部不完成。
  • 一致性:事务必须使数据库从一个一致性状态变换到另一个一致性状态。
  • 隔离性:一个事务的执行不能被其他事务干扰。
  • 持久性:一个事务一旦提交,它对数据库中数据的改变就是永久性的。

示例:转账操作(需要从两个账户中扣款和存款)

import java.sql.Connection;
import java.sql.SQLException;
public class TransactionExample {
    public static void transferMoney(int fromUserId, int toUserId, double amount) {
        String withdrawSql = "UPDATE users SET balance = balance - ? WHERE id = ?";
        String depositSql = "UPDATE users SET balance = balance + ? WHERE id = ?";
        Connection conn = null;
        try {
            // 1. 获取连接
            conn = DBUtil.getConnection();
            // 2. 开启事务 (关闭自动提交)
            conn.setAutoCommit(false);
            // 3. 执行第一个操作:扣款
            try (PreparedStatement pstmt1 = conn.prepareStatement(withdrawSql)) {
                pstmt1.setDouble(1, amount);
                pstmt1.setInt(2, fromUserId);
                pstmt1.executeUpdate();
            }
            // 模拟一个可能发生的错误
            // if (true) {
            //     throw new RuntimeException("模拟一个系统错误!");
            // }
            // 4. 执行第二个操作:存款
            try (PreparedStatement pstmt2 = conn.prepareStatement(depositSql)) {
                pstmt2.setDouble(1, amount);
                pstmt2.setInt(2, toUserId);
                pstmt2.executeUpdate();
            }
            // 5. 如果所有操作都成功,则提交事务
            conn.commit();
            System.out.println("转账成功!");
        } catch (SQLException | RuntimeException e) {
            // 6. 如果发生任何异常,则回滚事务
            if (conn != null) {
                try {
                    conn.rollback();
                    System.out.println("转账失败,已回滚!");
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            e.printStackTrace();
        } finally {
            // 7. 恢复自动提交,并关闭连接
            if (conn != null) {
                try {
                    conn.setAutoCommit(true); // 重要!
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) {
        // 假设 users 表中有 balance 列
        transferMoney(1, 2, 100.0);
    }
}

总结与最佳实践

  1. 使用 try-with-resources:这是管理 JDBC 资源(Connection, Statement, ResultSet)的最佳方式,能有效防止资源泄漏。
  2. 优先使用 PreparedStatement
    • 防止 SQL 注入:它对输入参数进行转义,是安全编程的基石。
    • 提高性能:对于需要多次执行的 SQL,数据库可以预编译它,后续执行更快。
  3. 使用列名访问 ResultSetrs.getString("name")rs.getString(2) 更清晰、更健壮,尤其是在 SQL 查询顺序改变时。
  4. 处理异常SQLException 是受检异常,必须处理,不要简单地 printStackTrace(),在实际应用中应该记录日志。
  5. 管理事务:对于涉及多个步骤的业务逻辑(如转账、下单),务必使用事务来保证数据的一致性。
  6. 连接池:在高并发应用中,频繁地创建和销毁连接是非常消耗资源的,应该使用数据库连接池(如 HikariCP, Druid, C3P0)来管理和复用连接,现代框架(如 Spring Boot)通常会自动配置连接池。

掌握了以上这些内容,你就可以熟练地使用 Java JDBC 进行大部分数据库操作了。

分享:
扫描分享到社交APP
上一篇
下一篇