如何在自定义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
相关产品推荐
相关产品推荐

