JDBC核心接口
获得Connection
// 1. java.sql.DriverManager public static Connection getConnection(String url) throws SQLException {} public static Connection getConnection(String url, java.util.Properties info) throws SQLException {} public static Connection getConnection(String url, String user, String password) throws SQLException {} // 2. javax.sql.DataSource Connection getConnection() throws SQLException;
Statement家族
Statement
Statement createStatement() throws SQLException; Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException; Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException;
5组执行SQL的方法
// 1.1 执行SQL boolean execute(String sql) throws SQLException; // 1.2 获得1.1执行结果 ResultSet getResultSet() throws SQLException; int getUpdateCount() throws SQLException; // 2.1 执行SQL boolean execute(String sql, int autoGeneratedKeys) throws SQLException; boolean execute(String sql, int columnIndexes[]) throws SQLException; boolean execute(String sql, String columnNames[]) throws SQLException; // 2.2 获得2.1执行结果 ResultSet getResultSet() throws SQLException; int getUpdateCount() throws SQLException; // 2.2 获得DB自增键对应的值 ResultSet getGeneratedKeys() throws SQLException; // 3. 执行查询语句 ResultSet executeQuery(String sql) throws SQLException; // 4. 执行增删改语句 int executeUpdate(String sql) throws SQLException; // 5.1 执行增删改语句 int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException; int executeUpdate(String sql, int columnIndexes[]) throws SQLException; int executeUpdate(String sql, String columnNames[]) throws SQLException; // 5.2 获得DB自增键对应的值 ResultSet getGeneratedKeys() throws SQLException;
PreparedStatement
PreparedStatement prepareStatement(String sql) throws SQLException; PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException; PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException; PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException; PreparedStatement prepareStatement(String sql, int columnIndexes[]) throws SQLException; PreparedStatement prepareStatement(String sql, String columnNames[]) throws SQLException;
3组执行SQL方法
// 1.1 执行SQL boolean execute() throws SQLException; // 1.2 获得1.1执行结果 ResultSet getResultSet() throws SQLException; int getUpdateCount() throws SQLException; // 1.2 如果prepareStatement时设置了自增键信息 ResultSet getGeneratedKeys() throws SQLException; // 2. 执行查询语句 ResultSet executeQuery() throws SQLException; // 3.1 执行SQL int executeUpdate() throws SQLException; // 3.2 如果prepareStatement时设置了自增键信息 ResultSet getGeneratedKeys() throws SQLException;
CallableStatement
略,我没使用过,以后应该也不会使用
Statement参数
resultSetType、resultSetConcurrency和resultSetHoldability
这三个参数都是用来控制查询类SQL的结果集ResultSet的行为,只有要执行查询类Select语句时,设置这三个参数才有意义。
resultSetType(结果集类型)
控制结果集的游标移动方向和对数据变更的敏感度:
| 可选值 | 移动方向 | 数据变更敏感度 | 备注 |
| ResultSet.TYPE_FORWARD_ONLY | 结果集游标只能向前移动(从第一行到最后一行) | 不敏感(结果集是创建时的数据快照) | 性能最佳 |
| ResultSet.TYPE_SCROLL_INSENSITIVE | 结果集游标可双向滚动(向前、向后、绝对定位等) | 不敏感(结果集是创建时的数据快照) | |
|
ResultSet.TYPE_SCROLL_SENSITIVE |
结果集游标可双向滚动(向前、向后、绝对定位等) | 对底层数据的修改敏感(滚动时会反映最新数据) | 实际支持性依赖数据库驱动 |
resultSetConcurrency(结果集并发模式)
控制结果集是否允许更新数据库:
| 可选值 | 是否允许更新数据 | 备注 |
| ResultSet.CONCUR_READ_ONLY | 结果集为只读 | |
| ResultSet.CONCUR_UPDATABLE | 结果集支持直接更新数据库(通过 updateXXX() 方法) |
实际支持性依赖数据库驱动 |
resultSetHoldability(结果集游标可保持性)
控制事务提交(事务关闭自动提交的前提下)后结果集游标是否保持打开:
各个数据库对此特性的支持是不一样的,比如常用的MySQL就不支持
| 可选值 | 游标是否保持打开 | 备注 |
| ResultSet.HOLD_CURSORS_OVER_COMMIT | 事务提交后,结果集保持打开(可继续读取) | |
| ResultSet.CLOSE_CURSORS_AT_COMMIT | 事务提交后,自动关闭结果集(释放资源) |
|
结果集
常用模版
try (Connection conn = DriverManager.getConnection(url, user, password); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM users")) { while (rs.next()) { System.out.println(rs.getString("name")); } } // 此处自动关闭 ResultSet、Statement、Connection
根据resultSetType、resultSetConcurrency和resultSetHoldability的不同设置,ResultSet还支持其他更多的方法。
事务
默认情况下Connection是事务自动提交模式的,可以通过以下方法来修改,如果关闭了事务自动提交,需要手动commit进行提交事务,或者rollback进行回滚事务
void setAutoCommit(boolean autoCommit) throws SQLException; void commit() throws SQLException; void rollback() throws SQLException;
批处理
作用
JDBC批处理(Batch Processing)是一种将多个SQL语句批量发送到数据库执行的机制,旨在减少网络通信和数据库开销,显著提升大批量数据操作的性能。
机制
通过addBatch()/addBatch(String sql)方法将多个SQL语句(如INSERT/UPDATE/DELETE)打包,再通过executeBatch()一次性发送到数据库执行。
- addBatch(String sql)(仅Statement):添加静态SQL语句到批处理。
- addBatch()(仅PreparedStatement):添加预编译SQL的参数到批处理(需先设置参数)。
常用模版
String sql = "INSERT INTO users (name, email) VALUES (?, ?)"; try (Connection conn = DriverManager.getConnection(url, user, password); PreparedStatement pstmt = conn.prepareStatement(sql)) { conn.setAutoCommit(false); // 关闭自动提交以提高性能 // 添加多个批处理条目 for (User user : userList) { pstmt.setString(1, user.getName()); pstmt.setString(2, user.getEmail()); pstmt.addBatch(); } int[] counts = pstmt.executeBatch(); // 执行批处理 conn.commit(); // 手动提交事务 } catch (BatchUpdateException e) { conn.rollback(); // 失败时回滚 int[] partialCounts = e.getUpdateCounts(); // 获取部分成功的数据 }
MySQL默认需在JDBC URL添加rewriteBatchedStatements=true以优化批处理。
常见问题
1. 获得Connection时,有那么好用的DriverManager,为啥还有再弄个DataSource呢?
| 特性 | DriverManager | DataSource |
|---|---|---|
| 连接获取方式 | DriverManager.getConnection(url) |
dataSource.getConnection() |
| 连接管理 | 每次创建新连接,用完关闭 | 支持连接池,复用已有连接 |
| 性能 | 低(频繁创建连接消耗资源) | 高(连接池减少开销) |
| 扩展性 | 仅支持基础连接 | 支持连接池、分布式事务、JNDI 绑定等 |
| 适用场景 | 简单测试或单次操作 | 生产环境、高并发场景 |
DataSource 是 JDBC 连接管理的现代化解决方案,核心价值是提升性能(通过连接池)、增强安全性、支持高级特性(如分布式事务)。在实际开发中,应始终优先使用 DataSource 而非 DriverManager,尤其是在生产环境中。
2. Statement、PreparedStatement和CallableStatement三者有啥区别?
| 特性 | Statement | PreparedStatement | CallableStatement |
|---|---|---|---|
| 参数化支持 | ❌ 直接拼接字符串 | ✅ 使用 ? 占位符 |
✅ 支持 IN/OUT 参数 |
| 预编译 | ❌ 每次重新编译 | ✅ 预编译并缓存 | ✅ 预编译并缓存 |
| SQL 注入风险 | 高风险 | 低风险(参数化隔离数据) | 低风险 |
| 性能 | 低(无缓存) | 高(适合重复执行) | 高(适合复杂逻辑) |
| 核心用途 | 静态 SQL(DDL/DQL) | 动态参数查询 | 调用存储过程或函数 |
| 批量操作支持 | 可批量执行不同SQL语句(动态生成) | 必须为同一条预编译SQL(参数变化) | ❌ |
3. 如何确定当前厂商的驱动是否支持 resultSetType、resultSetConcurrency和resultSetHoldability的哪些可选值?
参数可选值的支持性,可以使用DatabaseMetaData的以下方法验证驱动支持情况:
java.sql.Connection DatabaseMetaData getMetaData() throws SQLException; java.sql.DatabaseMetaData boolean supportsResultSetType(int type) throws SQLException; boolean supportsResultSetConcurrency(int type, int concurrency) throws SQLException; boolean supportsResultSetHoldability(int holdability) throws SQLException;
4. 为什么Statement和PreparedStatement设置自增字段信息的时机不一样?
Statement 和 PreparedStatement 处理自增字段的方式不同,根本原因在于两者的执行机制不同。
| 特性 | Statement | PreparedStatement |
|---|---|---|
| SQL 类型 | 动态 SQL(运行时拼接) | 预编译 SQL(固定结构 + 参数占位符) |
| 执行效率 | 低(每次需解析 SQL) | 高(预编译后重复使用) |
| 生成键的元数据感知 | 执行时才能确定 SQL 结构 | 创建时即可确定 SQL 结构 |
由于 PreparedStatement 的预编译机制, 在创建时已明确 SQL 结构(如表名、字段名),JDBC 驱动可以提前解析自增字段的元数据,因此支持在prepareStatement时就指定需要返回的自增字段,并可以在后续重复使用预编译SQL执行多次SQL时也能重复使用自增字段的返回
5. MySQL设置rewriteBatchedStatements=true时,对批处理做了哪些优化?
看名称可知,最主要的优化就是重写SQL了
- 合并INSERT语句
将多个INSERT语句合并为单个多值(multi-value)的INSERT语句。
- 优化UPDATE/DELETE语句
对于批量UPDATE或DELETE,驱动会尝试合并为更高效的语法(如CASE-WHEN结构或利用临时表),但优化效果因语句复杂度而异。
参考
Java文档: https://docs.oracle.com/javase/8/docs/api/java/sql/Connection.html

浙公网安备 33010602011771号