You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在自定义Iterator中正确关闭PreparedStatement与ResultSet

正确实现带资源自动关闭的JDBC Sequence迭代器

要解决PreparedStatement和ResultSet的资源关闭问题,核心是把Sequence和资源的生命周期绑定,利用AutoCloseable接口结合Kotlin的use函数自动管理资源,确保遍历过程中或异常场景下都能正确释放资源。

实现方案

1. 自定义Sequence类,绑定资源并实现AutoCloseable

创建同时实现Sequence<Item>和AutoCloseable的类,内部统一管理PreparedStatement和ResultSet的创建与关闭逻辑:

import java.sql.Connection
import java.sql.PreparedStatement
import java.sql.ResultSet
import java.sql.SQLException
import java.util.NoSuchElementException

class ItemSequence(
    private val connection: Connection,
    private val sql: String
) : Sequence<Item>, AutoCloseable {
    private var stmt: PreparedStatement? = null
    private var resultSet: ResultSet? = null

    override fun iterator(): Iterator<Item> {
        // 仅首次获取迭代器时初始化资源
        if (stmt == null) {
            try {
                stmt = connection.prepareStatement(sql)
                resultSet = stmt!!.executeQuery()
            } catch (e: SQLException) {
                // 初始化失败时,提前关闭已创建的Statement
                stmt?.close()
                throw e
            }
        }

        return object : Iterator<Item> {
            private var hasNextChecked = false
            private var hasNextValue = false

            override fun hasNext(): Boolean {
                if (!hasNextChecked) {
                    hasNextValue = resultSet!!.next()
                    hasNextChecked = true
                    // 遍历到末尾时主动关闭资源
                    if (!hasNextValue) {
                        close()
                    }
                }
                return hasNextValue
            }

            override fun next(): Item {
                if (!hasNext()) {
                    throw NoSuchElementException("没有更多元素")
                }
                hasNextChecked = false
                // 从ResultSet映射为Item对象
                return Item(
                    id = resultSet!!.getLong("id"),
                    name = resultSet!!.getString("name")
                    // 根据实际表结构补充其他字段
                )
            }
        }
    }

    override fun close() {
        // 显式关闭ResultSet(部分JDBC驱动需手动触发)
        resultSet?.run {
            try { close() } catch (_: SQLException) {}
        }
        // 关闭PreparedStatement,JDBC规范中会自动关闭关联的ResultSet
        stmt?.run {
            try { close() } catch (_: SQLException) {}
        }
        // 置空引用帮助GC回收
        resultSet = null
        stmt = null
    }
}

// 示例数据类
data class Item(val id: Long, val name: String)

2. 使用Sequence时用use块自动管理资源

通过Kotlin标准库的use函数,确保无论遍历是否完成、是否抛出异常,都会自动调用close()释放资源:

import java.io.File
import java.sql.Connection

fun selectItems(connection: Connection): Sequence<Item> {
    val sql = File("sql/select.sql").readText()
    return ItemSequence(connection, sql)
}

// 调用示例
fun main() {
    // 假设sqliteConnection是已初始化的JDBC连接
    val sqliteConnection: Connection = TODO("初始化你的SQLite连接")
    
    selectItems(sqliteConnection).use { itemSequence ->
        itemSequence.forEach { item ->
            println("处理Item: $item")
            // 即使中途中断遍历(如break、return),use块仍会执行close()
            if (item.id == 10) return@forEach
        }
    }
}

关键要点说明

  • 资源与Sequence生命周期绑定:将PreparedStatement和ResultSet作为Sequence的成员变量,确保资源的创建、使用、销毁全流程可控。
  • 自动关闭保障:借助AutoCloseable接口和use函数,无需手动调用close(),Kotlin会自动处理异常场景下的资源释放。
  • 提前释放资源:在迭代器的hasNext()中检测到遍历结束时,主动调用close(),避免资源长时间闲置。
  • 异常安全初始化:初始化资源时用try-catch包裹,确保executeQuery失败时,已创建的Statement能被及时关闭。

内容的提问来源于stack exchange,提问作者t3chb0t

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 04:17:15