I think I found it. The $Proxy97 class is a java.lang.reflect.Proxy instance and the problem lies in the class com.ibatis.common.jdbc.logging.PreparedStatementLogProxy. When the equals method is invoked control is being passed to this class. The invoke method is being called and control falls down to line 80, where the method is being invoked, passing in the statement and the params. The passed in statement is an instance of SybPreparedStatement but params[0] is an instance of $Proxy97 so of course they are not equal, and this is why the PreparedStatement is never found within the session.

From: Christopher.Mathrusse@sybase.com [mailto:Christopher.Mathrusse@sybase.com]
Sent: Wednesday, September 27, 2006 2:09 PM
To: user-java@ibatis.apache.org
Subject: Odd behavior in SqlExecutor

I'm using:
the latest version of iBatis. (Compiled it from svn last week)
Spring 2.0 rc4
Sybase ASE 12.5
I'm seeing some rather odd behavior that I can't explain. The only reason I found this is due to the fact that I've turned on JDBC trace in the JDBC driver. On occasion I see in the trace file SQL Exceptions occurring due to the Object already being closed. I traced this down to the fact that the PreparedStatement was being closed twice. As I traced this down I found the code in Spring where the exception was actually being consumed as the exception is occurring on a call to PreparedStatement.close(). (Of course this is negligible)  So I decided to investigate a bit more to see why it was being called twice.
I found within the class com.ibatis.sqlmap.engine.execution.SqlExecutor where the problem stems from. The method on line 481

private static PreparedStatement prepareStatement(SessionScope session, Connection conn, String sql) throws SQLException

checks to see if a PreparedStatement already exists in the session and if not it creates the PreparedStatement and places it into the session, so it can be found later. (That make sense) The session simply places it into a HashMap called preparedStatements. I can see the newly created PreparedStatement in the session. (Object Id's match) The statement is then returned to the executeQuery method and it's business as usual. The statement is executed, the results are processed, everything is normal. Then in the finally block the resultSet is closed and then the method closeStatement is executed, passing in the session and the PreparedStatement.
Within this method, line 501, a test is made to see if the session contains the PreparedStatement.

if (!session.hasPreparedStatement(ps))

If the session does not contain the statement, then the statement is closed, and this is the behavior I'm seeing. The statement is being closed. The problem is though that the session does contain the statement. The object Id's match up, the passed in PreparedStatement and the object residing in the session's preparedStatements HashMap.

If I execute session.hasPreparedStatementFor(String) it returns true, it finds the entry, but if I execute session.hasPreparedStatement(ps) it returns false. And because it returns false the PreparedStatement is being closed. Interestingly enough, when I execute session.getPreparedStatement(String).equals(ps) the return value is false, which simply confuses me.

Looking at the PreparedStatement I can see that the object is of datatype $Proxy97. I believe that this is because the PreparedStatement is being wrapped in a similar fashion to the Connection itself. My guess at this point would be that the Proxy is not implementing the equals() method correctly and that is why I am seeing this behavior. (Does this sound plausible?)

So I'm guessing that I need to figure out who is wrapping the underlying PreparedStatement. (Spring? The Transaction Manager?)

Any insight or wisdom into this issue will be greatly appreciated.




Chris Mathrusse
(925) 236-5553