JDBC 详解:Java 数据库连接的核心操作
JDBC(Java Database Connectivity)是 Java 访问数据库的标准接口,它定义了一套独立于特定数据库管理系统(DBMS)的 API,使 Java 程序能够通过统一的方式操作各种关系型数据库(如 MySQL、Oracle、SQL Server 等)。本文将详细讲解 JDBC 的核心概念、连接步骤及常用操作,帮助你掌握 Java 与数据库交互的基础。
JDBC 核心组件
JDBC 主要通过以下接口和类实现数据库交互:
| 组件 |
作用描述 |
Driver |
数据库驱动接口,由各数据库厂商实现(如 MySQL 的 com.mysql.jdbc.Driver)。 |
DriverManager |
驱动管理类,负责注册驱动、创建数据库连接。 |
Connection |
数据库连接接口,代表与数据库的会话,用于创建执行 SQL 的对象。 |
Statement |
执行静态 SQL 语句的接口,存在 SQL 注入风险。 |
PreparedStatement |
预编译 SQL 语句的接口,可防止 SQL 注入,性能更优。 |
ResultSet |
结果集接口,存储查询返回的数据,提供遍历和获取数据的方法。 |
JDBC 连接数据库的步骤
使用 JDBC 操作数据库的核心步骤可概括为:加载驱动 → 建立连接 → 执行 SQL → 处理结果 → 释放资源。
准备工作
加载驱动与建立连接
(1)核心参数定义
1 2 3 4 5 6 7 8
| private static final String DRIVER = "com.mysql.jdbc.Driver";
private static final String URL = "jdbc:mysql://localhost:3306/test?useSSL=false";
private static final String USER = "root";
private static final String PASSWORD = "123456";
|
(2)加载驱动
通过 Class.forName(DRIVER) 加载驱动类,其原理是触发驱动类中的静态代码块,向 DriverManager 注册驱动实例:
1 2 3 4 5 6 7 8
| static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException e) { throw new RuntimeException("驱动注册失败"); } }
|
(3)建立连接
通过 DriverManager.getConnection(URL, USER, PASSWORD) 获取 Connection 对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
public static Connection getConnection() { Connection conn = null; try { Class.forName(DRIVER); conn = DriverManager.getConnection(URL, USER, PASSWORD); System.out.println("数据库连接成功"); } catch (ClassNotFoundException e) { throw new RuntimeException("驱动加载失败:" + e.getMessage()); } catch (SQLException e) { throw new RuntimeException("连接建立失败:" + e.getMessage()); } return conn; }
|
注意:MySQL 5.1 之后的驱动可省略 Class.forName(DRIVER),因为 JDBC 4.0 引入了自动加载机制(通过 META-INF/services/java.sql.Driver 文件)。
释放资源
数据库连接、Statement、ResultSet 都是稀缺资源,使用后必须关闭,建议在 finally 块中处理:
1 2 3 4 5 6 7 8 9 10 11 12
|
public static void close(Connection conn, PreparedStatement pstmt, ResultSet rs) { try { if (rs != null) rs.close(); if (pstmt != null) pstmt.close(); if (conn != null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } }
|
执行 SQL 语句:Statement 与 PreparedStatement
Statement:执行静态 SQL(不推荐)
Statement 用于执行静态 SQL 语句(SQL 字符串固定),但存在 SQL 注入风险,且每次执行都需重新编译 SQL,性能较差。
示例:插入数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
public static void insertWithStatement(String name) { Connection conn = null; Statement stmt = null; try { conn = getConnection(); stmt = conn.createStatement(); String sql = "INSERT INTO user (name) VALUES ('" + name + "')"; int rows = stmt.executeUpdate(sql); System.out.println("插入成功,影响行数:" + rows); } catch (SQLException e) { e.printStackTrace(); } finally { close(conn, stmt, null); } }
|
PreparedStatement:预编译 SQL(推荐)
PreparedStatement 是 Statement 的子接口,支持 带占位符(?)的 SQL,执行前会预编译 SQL 模板,后续只需填充参数,可:
- 防止 SQL 注入(参数自动转义);
- 重复执行时无需重新编译,性能更优。
示例:插入、更新、删除数据
(1)插入数据(含自增主键获取)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
public static int insertWithPreparedStatement(String name) { Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; int generatedId = -1; try { conn = getConnection(); String sql = "INSERT INTO user (name) VALUES (?)"; pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); pstmt.setString(1, name); int rows = pstmt.executeUpdate(); System.out.println("插入成功,影响行数:" + rows); rs = pstmt.getGeneratedKeys(); if (rs.next()) { generatedId = rs.getInt(1); } } catch (SQLException e) { e.printStackTrace(); } finally { close(conn, pstmt, rs); } return generatedId; }
|
(2)更新数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
public static void updateWithPreparedStatement(int id, String newName) { Connection conn = null; PreparedStatement pstmt = null; try { conn = getConnection(); String sql = "UPDATE user SET name = ? WHERE id = ?"; pstmt = conn.prepareStatement(sql); pstmt.setString(1, newName); pstmt.setInt(2, id); int rows = pstmt.executeUpdate(); System.out.println("更新成功,影响行数:" + rows); } catch (SQLException e) { e.printStackTrace(); } finally { close(conn, pstmt, null); } }
|
(3)删除数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
public static void deleteWithPreparedStatement(int id) { Connection conn = null; PreparedStatement pstmt = null; try { conn = getConnection(); String sql = "DELETE FROM user WHERE id = ?"; pstmt = conn.prepareStatement(sql); pstmt.setInt(1, id); int rows = pstmt.executeUpdate(); System.out.println("删除成功,影响行数:" + rows); } catch (SQLException e) { e.printStackTrace(); } finally { close(conn, pstmt, null); } }
|
查询数据:处理 ResultSet
executeQuery() 用于执行 SELECT 语句,返回 ResultSet 结果集,需通过 next() 方法遍历数据行,通过列名或索引获取字段值。
示例:查询所有用户
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
public static void queryAllUsers() { Connection conn = null; PreparedStatement pstmt = null; ResultSet rs = null; try { conn = getConnection(); String sql = "SELECT id, name FROM user"; pstmt = conn.prepareStatement(sql); rs = pstmt.executeQuery(); while (rs.next()) { int id = rs.getInt("id"); String name = rs.getString(2); System.out.println("ID: " + id + ", Name: " + name); } } catch (SQLException e) { e.printStackTrace(); } finally { close(conn, pstmt, rs); } }
|
批量操作:高效处理大量数据
当需要插入、更新多条记录时,使用批量操作可大幅提升性能(减少网络交互次数)。
示例:批量插入数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
|
public static void batchInsertUsers(List<String> names) { Connection conn = null; PreparedStatement pstmt = null; try { conn = getConnection(); conn.setAutoCommit(false); String sql = "INSERT INTO user (name) VALUES (?)"; pstmt = conn.prepareStatement(sql); for (String name : names) { pstmt.setString(1, name); pstmt.addBatch(); } int[] rows = pstmt.executeBatch(); System.out.println("批量插入完成,总影响行数:" + Arrays.stream(rows).sum()); conn.commit(); } catch (SQLException e) { if (conn != null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } e.printStackTrace(); } finally { if (conn != null) { try { conn.setAutoCommit(true); } catch (SQLException e) { e.printStackTrace(); } } close(conn, pstmt, null); } }
|
优化点:
- 关闭自动提交(
conn.setAutoCommit(false)),批量执行后手动提交,减少事务开销。
- 批量大小适中(如每次 1000 条),避免内存溢出。
JDBC 最佳实践
- 优先使用 PreparedStatement:防止 SQL 注入,提升重复执行性能。
- 及时释放资源:在
finally 块中关闭 Connection、PreparedStatement、ResultSet,避免资源泄露。
- 使用连接池:生产环境中,通过连接池(如 HikariCP、C3P0)管理连接,避免频繁创建和关闭连接的开销。
- 处理事务:对批量操作或多步 SQL,使用事务(
commit()/rollback())保证数据一致性。
- 避免硬编码:数据库参数(URL、用户名、密码)应配置在外部文件(如
properties)中,便于维护