JDBC 资源关闭

本文探讨了JDBC中Connection、Statement、PreparedStatement及ResultSet对象的正确管理和关闭方式,解释了为何需要关闭这些对象,并介绍了使用连接池时资源管理的变化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述

我们获取到的 Connection 对象 / Statement 对象 / PreparedStatement 对象 / ResultSet 对象 都需要关闭, 但是Connection对象是数据库的连接, 需要关闭的话还可以理解, 为什么其他的对象需要关闭呢?

ResultSet 的 API 这样描述:

A ResultSet object is automatically closed when the Statement object that generated it is closed, re-executed, or used to retrieve the next result from a sequence of multiple results.

当生成 ResultSet 的 Statement 对象关闭,重新执行或者从 ResultSet 序列中重新获取一个 ResultSet 的时候, 当前的 ResultSet 对象会自动关闭

Statement 的 API 这样描述:

By default, only one ResultSet object per Statement object can be open at the same time. Therefore, if the reading of one ResultSet object is interleaved with the reading of another, each must have been generated by different Statement objects. All execution methods in the Statement interface implicitly close a current ResultSet object of the statement if an open one exists.

默认情况下, 同一时刻只有一个 ResultSet 可以被打开, 因此, 如果两个 ResultSet 的读取发生了交叉, 那么产生这两个 ResultSet 的 Statement 必须是不同的. 另外, Statement 接口中所有的 execute 方法都会隐式的关闭当前打开的 ResultSet

上边的 API 描述了 Statement 和 ResultSet 的自动关闭, 但是为什么要关闭还是不知道

另外, 如果使用了连接池之后, 调用 close 方法就不是关闭连接, 而是归还 Connection 对象给 Connection Pool(连接池),

首先分析不使用连接池的 close

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

import org.junit.Test;

public class DQLTest {

    @Test
    public void testSelect() throws Exception {
        String sql = "SELECT COUNT(id) count FROM t_student_day05";
        //1. 加载注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2. 获取连接对象
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo","root","admin");
        //3. 获取语句对象
        Statement statement = connection.createStatement();
        //4. 执行语句
        ResultSet resultSet = statement.executeQuery(sql);
        //5. 打印获取到的
        if (resultSet.next()){
            System.out.println(resultSet.getLong("count"));
        }
        //6. 释放资源
        resultSet.close();
        statement.close();
        connection.close();
    }
}

通过 Debug 发现, Connection 的 close 方法是执行的 com.mysql.jdbc.ConnectionImpl 中的 close 方法, 那么 JVM 是怎么知道应该调用的是 Connection 的那个实现类呢?

使用连接池之后

import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.junit.Test;

public class DbcpTest {
    @Test
    public void testGetConnProperties() throws Exception {
        //加载配置文件
        Properties info = new Properties();
        InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("day07_db.properties");
        info.load(in);

        //获取Connection
        DataSource dataSource = BasicDataSourceFactory.createDataSource(info);
        Connection connection = dataSource.getConnection();
        String sql = "SELECT COUNT(id) count FROM t_day07_student WHERE name = ?";
        PreparedStatement pst = connection.prepareStatement(sql);
        pst.setString(1, "DJ");
        ResultSet resultSet = pst.executeQuery();
        if (resultSet.next()) {
            System.out.println(resultSet.getLong("count"));
        }
        resultSet.close();
        pst.close();
        connection.close();
    }
}

通过 Debug 调试发现, 这里的 Connection 调用的是 org.apache.commons.dbcp.PoolingDataSource 中的 close 方法, 那么它又是怎么知道是找这个实现类中的 close 呢?

另外, 这些资源的关闭一定要有先后顺序吗?

还有资源的关闭好像可以使用 Java7 的新特性, 尝试使用新特性

@Test
public void testCatchExceptionNew(){
    String sql = "CREATE TABLE t_student_day05(id INT(10), name VARCHAR(10), age INT(10))";
    try(
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbcdemo", "root", "admin");
        Statement statement = connection.createStatement();
    ){
    // * 1. 加载注册驱动
    Class.forName("com.mysql.jdbc.Driver");
    // * 4. 执行语句
    statement.executeUpdate(sql);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

发现虽然可以使用新特性,但是加载注册驱动就出现在对象创建的后边了,也就是说先创建对象才加载驱动,因为 jdbc4.0 / java6 之后会自动加载驱动,又因为新特性是7出现的,所以在SE中可以这样使用,,这样说对吗?

问题解决

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值