Java JDBC 编程:连接 MySQL 数据库的基础实践

JDBC(Java Database Connectivity)是 Java 操作数据库的标准 API,通过统一接口实现对不同数据库的访问。本文基于 MySQL 数据库,从 JDBC 核心流程入手,详解数据库连接、SQL 执行、结果处理及安全注意事项,帮助读者掌握 Java 操作数据库的基础方法。

一、JDBC 核心概念与依赖

1. 核心作用

JDBC 作为 Java 与数据库的 “桥梁”,屏蔽了不同数据库的底层差异,开发者通过统一 API 即可完成数据库连接、SQL 执行、结果获取等操作,无需关注数据库具体实现。

2. 核心依赖

操作 MySQL 需引入 MySQL JDBC 驱动(如mysql-connector-java), Maven 项目可通过依赖配置引入:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version> <!-- 对应MySQL 5.x版本,MySQL 8.x需用8.x驱动 -->
</dependency>

二、JDBC 操作的核心流程

JDBC 操作数据库遵循固定流程,无论执行增删改查,核心步骤均包含加载驱动、获取连接、创建执行对象、执行 SQL、处理结果、关闭资源,以下结合实例详解各步骤。

1. 执行插入操作(Demo1:新增用户)

适用于INSERT类 SQL,通过executeUpdate()执行,返回受影响行数。

步骤拆解:
public class Demo1 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1. 加载MySQL驱动(MySQL 5.x必需,8.x可省略,驱动会自动注册)
        Class.forName("com.mysql.jdbc.Driver"); 

        // 2. 获取数据库连接(URL、用户名、密码)
        // URL格式:jdbc:mysql://主机IP:端口/数据库名?参数(useSSL=false关闭SSL验证,避免警告)
        String url = "jdbc:mysql://localhost:3306/java2504?useSSL=false";
        String username = "root"; // 数据库用户名
        String password = "123456"; // 数据库密码
        Connection con = DriverManager.getConnection(url, username, password);

        // 3. 创建SQL执行对象(Statement:用于执行静态SQL)
        Statement statement = con.createStatement();

        // 4. 编写SQL语句(插入一条用户数据)
        String sql = "insert into user values(4,'老刘',3)"; // user表需提前创建(u_id, u_name, r_id字段)

        // 5. 执行SQL(executeUpdate()用于增删改,返回受影响行数)
        int count = statement.executeUpdate(sql);

        // 6. 处理结果(根据受影响行数判断操作是否成功)
        if (count > 0) {
            System.out.println("添加成功");
        } else {
            System.out.println("添加失败");
        }

        // 7. 关闭资源(反向关闭:先关执行对象,再关连接,避免资源泄漏)
        statement.close();
        con.close();
    }
}

2. 执行查询操作(Demo2:查询所有用户)

适用于SELECT类 SQL,通过executeQuery()执行,返回ResultSet结果集,需遍历获取数据。

步骤拆解:
public class Demo2 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1. 加载驱动
        Class.forName("com.mysql.jdbc.Driver");

        // 2. 获取连接(同Demo1)
        String url = "jdbc:mysql://localhost:3306/java2504?useSSL=false";
        Connection con = DriverManager.getConnection(url, "root", "123456");

        // 3. 创建SQL执行对象
        Statement statement = con.createStatement();

        // 4. 编写查询SQL(查询user表所有数据)
        String sql = "select * from user";

        // 5. 执行查询(executeQuery()用于查询,返回ResultSet结果集)
        ResultSet resultSet = statement.executeQuery(sql);

        // 6. 处理结果集(遍历resultSet,通过列名/索引获取数据)
        while (resultSet.next()) { // next():移动到下一行,有数据返回true
            // 通过列名获取数据(推荐,避免列顺序变化导致错误)
            int id = resultSet.getInt("u_id"); // 对应表中u_id字段
            String name = resultSet.getString("u_name"); // 对应u_name字段
            int rId = resultSet.getInt("r_id"); // 对应r_id字段
            System.out.println(id + "--" + name + "--" + rId);
        }

        // 7. 关闭资源(新增ResultSet,需先关结果集)
        resultSet.close();
        statement.close();
        con.close();
    }
}

3. 封装查询逻辑(Demo3:根据姓名查询用户)

将 JDBC 操作封装为方法,结合实体类(User)返回数据,同时通过try-finally确保资源关闭,避免异常导致资源泄漏。

关键优化点:
  • 资源安全关闭:无论是否发生异常,均通过finally关闭ResultSetStatementConnection
  • 实体类映射:将查询结果封装为User对象,符合面向对象编程思想;
  • 用户交互:通过Scanner获取用户输入,动态查询。
代码实现:
// 1. User实体类(存储用户数据,使用lombok的@Data简化getter/setter,需引入lombok依赖)
@Data // lombok注解:自动生成getter、setter、toString等方法
public class User {
    private int uId;     // 对应u_id字段
    private String uName;// 对应u_name字段
    private String rId;  // 对应r_id字段(此处示例用String,实际应与表字段类型一致)

    // 构造方法:用于接收查询结果
    public User(int uId, String uName, String rId) {
        this.uId = uId;
        this.uName = uName;
        this.rId = rId;
    }
}

// 2. 封装查询方法的Demo3
public class Demo3 {
    public static void main(String[] args) throws SQLException {
        // 接收用户输入(查询条件:用户名)
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入姓名");
        String name = sc.next();
        sc.close();

        // 调用查询方法,获取用户对象
        User user = login(name);
        System.out.println(user); // 打印用户信息(依赖@Data生成的toString())
    }

    // 封装:根据用户名查询用户
    public static User login(String name) throws SQLException {
        // 声明资源对象(初始化为null,便于finally关闭)
        Connection con = null;
        Statement s = null;
        ResultSet rs = null;
        User user = null;

        try {
            // 1. 加载驱动
            Class.forName("com.mysql.jdbc.Driver");

            // 2. 获取连接
            String url = "jdbc:mysql://localhost:3306/java2504?useSSL=false";
            con = DriverManager.getConnection(url, "root", "123456");

            // 3. 创建执行对象
            s = con.createStatement();

            // 4. 编写SQL(注意:此处直接拼接字符串,存在SQL注入风险!后续需用PreparedStatement优化)
            String sql = "select * from user where u_name='" + name + "'";

            // 5. 执行查询
            rs = s.executeQuery(sql);

            // 6. 处理结果:若查询到数据,封装为User对象
            if (rs.next()) {
                user = new User(
                    rs.getInt("u_id"),
                    rs.getString("u_name"),
                    rs.getString("r_id")
                );
            }
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace(); // 实际开发需更规范的异常处理(如抛自定义异常)
        } finally {
            // 7. 关闭资源:先关rs,再关s,最后关con;每个关闭都需try-catch,避免前一个关闭失败导致后续不执行
            try {
                if (rs != null) rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if (s != null) s.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if (con != null) con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return user;
    }
}

三、JDBC 的关键问题与优化

1. SQL 注入风险(Statement 的缺陷)

Demo3 中直接拼接用户输入的字符串(如"select * from user where u_name='" + name + "'"),若用户输入' or '1'='1,SQL 会变为select * from user where u_name='' or '1'='1',导致查询所有用户,存在严重安全风险。

解决方案:使用PreparedStatement(预编译 SQL)替代Statement,通过占位符?接收参数,自动过滤特殊字符:

// 优化后的SQL:用?占位,不直接拼接字符串
String sql = "select * from user where u_name=?";
PreparedStatement pstmt = con.prepareStatement(sql);

// 给占位符赋值(索引从1开始,而非0)
pstmt.setString(1, name); // 第1个?赋值为用户输入的name

// 执行查询(无需再传SQL)
ResultSet rs = pstmt.executeQuery();

2. 资源泄漏问题

若不妥善关闭ResultSetStatementConnection,会导致数据库连接资源无法释放,长期运行会耗尽连接池,导致系统故障。

解决方案

  • 始终在finally中关闭资源,且按 “ResultSetStatementConnection” 的顺序关闭;
  • 每个资源的关闭单独用try-catch包裹,避免前一个关闭异常导致后续资源未关闭;
  • 生产环境推荐使用数据库连接池(如 Druid、HikariCP),统一管理连接,避免手动关闭的繁琐与风险。

3. 驱动版本适配

  • MySQL 5.x:驱动类为com.mysql.jdbc.Driver,依赖版本如5.1.47
  • MySQL 8.x:驱动类为com.mysql.cj.jdbc.Driver,依赖版本如8.0.32,且 URL 需添加时区参数(serverTimezone=UTC),否则会报错:
    String url = "jdbc:mysql://localhost:3306/java2504?useSSL=false&serverTimezone=UTC";

四、总结

JDBC 是 Java 操作数据库的基础,核心流程固定(加载驱动→获取连接→执行 SQL→处理结果→关闭资源),但需注意安全与资源管理问题:

  1. PreparedStatement替代Statement,避免 SQL 注入;
  2. 通过try-finally或连接池管理资源,防止泄漏;
  3. 根据 MySQL 版本适配驱动类与 URL 参数;
  4. 复杂项目推荐使用 ORM 框架(如 MyBatis、JPA),简化 JDBC 的繁琐代码,提升开发效率。

掌握 JDBC 基础是后续学习数据库连接池、ORM 框架的前提,也是理解 Java 数据持久层原理的关键。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值