Backend Development 8 min read

Understanding ResultSet Resource Release and Statement Concurrency in MySQL JDBC

This article examines how MySQL's JDBC driver manages ResultSet resource release, the internal close mechanisms, and the concurrency limitations of Statement objects, illustrated with code excerpts and a Groovy virtual‑thread demo that reveals runtime exceptions when ResultSets are accessed after implicit closure.

FunTester
FunTester
FunTester
Understanding ResultSet Resource Release and Statement Concurrency in MySQL JDBC

Reading source code helps verify and deepen knowledge; after exploring Caffeine, the author turned to the Java‑MySQL JDBC driver to answer two questions: what happens if a ResultSet is not explicitly closed, and whether Statement supports concurrent calls.

The close() method of ResultSet releases all resources associated with the result set, including the cursor and the underlying database connection. Once closed, the ResultSet cannot be used to fetch data.

public void close() throws SQLException { try { this.realClose(true); } catch (CJException var2) { throw SQLExceptionsMapping.translateException(var2, this.getExceptionInterceptor()); } }

The internal realClose() method performs two main actions. First, it obtains the current java.sql.Connection and synchronizes on its mutex to ensure thread‑safety. Second, it nullifies many internal fields, including this.connection = null , which clears the reference but does not close the underlying Connection object.

In com.mysql.cj.jdbc.StatementImpl , the driver calls closeAllOpenResults() which iterates over all open ResultSetInternalMethods and invokes element.realClose(false) for each, then clears the collection.

protected void closeAllOpenResults() throws SQLException { JdbcConnection locallyScopedConn = this.connection; if (locallyScopedConn != null) { synchronized (locallyScopedConn.getConnectionMutex()) { if (this.openResults != null) { Iterator var3 = this.openResults.iterator(); while (var3.hasNext()) { ResultSetInternalMethods element = (ResultSetInternalMethods) var3.next(); try { element.realClose(false); } catch (SQLException var7) { AssertionFailedException.shouldNotHappen(var7); } } this.openResults.clear(); } } } }

Consequently, each execution of a MySQL statement implicitly closes all previously opened ResultSet objects, so even without explicit close() calls resources are eventually released.

Regarding concurrency, the official JDBC documentation does not guarantee that Statement is thread‑safe. The author wrote a Groovy demo using JDK 21 virtual threads that runs ten concurrent queries on the same Statement . The program throws:

java.sql.SQLException: Operation not allowed after ResultSet closed

The exception originates from com.mysql.cj.jdbc.result.ResultSetImpl#checkClosed :

protected final JdbcConnection checkClosed() throws SQLException { JdbcConnection c = this.connection; if (c == null) { throw SQLError.createSQLException(Messages.getString("ResultSet.Operation_not_allowed_after_ResultSet_closed_144"), "S1000", this.getExceptionInterceptor()); } else { return c; } }

When ResultSet.close() runs, it sets the internal connection field to null . Subsequent concurrent accesses find the connection null and raise the above exception, confirming that Statement should not be used concurrently.

The article concludes by highlighting the broader benefits of reading source code: deeper architectural insight, learning best practices, faster debugging, and improved team collaboration.

JavaconcurrencyMySQLJDBCResultSetSTATEMENT
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.