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呢?

特性DriverManagerDataSource
连接获取方式 DriverManager.getConnection(url) dataSource.getConnection()
连接管理 每次创建新连接,用完关闭 支持连接池,复用已有连接
性能 低(频繁创建连接消耗资源) 高(连接池减少开销)
扩展性 仅支持基础连接 支持连接池、分布式事务、JNDI 绑定等
适用场景 简单测试或单次操作 生产环境、高并发场景

DataSource 是 JDBC 连接管理的现代化解决方案,核心价值是提升性能(通过连接池)、增强安全性、支持高级特性(如分布式事务)。在实际开发中,应始终优先使用 DataSource 而非 DriverManager,尤其是在生产环境中。

 

2. Statement、PreparedStatement和CallableStatement三者有啥区别?

特性StatementPreparedStatementCallableStatement
参数化支持 ❌ 直接拼接字符串 ✅ 使用 ? 占位符 ✅ 支持 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 处理自增字段的方式不同,根本原因在于两者的执行机制不同。

特性StatementPreparedStatement
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

posted @ 2025-04-18 00:24  halu126  阅读(21)  评论(0)    收藏  举报