Oracle JDBC driver 11.2.x:
Should I rely on the implicit statement cache or should I invoke setPoolable(true) on each created Statement?
What are the differences, advantages and disadvantages of both methods?
Statement caching improves performance by caching executable statements that are used repeatedly, such as in a loop or in a method that is called repeatedly.
When you enable implicit Statement caching, JDBC automatically caches the prepared or callable statement when you call the close method of this statement object.
Invoking setPoolable(true) on each created statement caches the statement.
I'd say that you're better off relying on the implicit Statement cache.
Here's Oracle's documentation on Statement and Result Set Caching for Oracle 11.2.
Related
I am currently facing issue on performance of Spring JDBC. I am performing inserts in serie and this is increasing the latency when load is introduced. I am using prepared statements and Hikari CP connection pool.
I have narrowed down the problem which function call in Jdbctemplate class is taking more time.
This is the line - https://github.com/spring-projects/spring-framework/blob/06e352822abc1eb0f5858ca9e79e38f8958e3c63/spring-jdbc/src/main/java/org/springframework/jdbc/core/JdbcTemplate.java#L546
It is stmt.executeUpdate in UpdateStatementCallback. What can be reason for this?
I have using connection pooling, prepared statement cache & batching as well. I have checked, the underlying Mysql (using AWS RDS) is not a Problem as well.
is there anything I am missing?
spring.datasource.hikari.data-source-properties.cachePrepStmts=true
spring.datasource.hikari.data-source-properties.prepStmtCacheSize=250
spring.datasource.hikari.data-source-properties.prepStmtCacheSqlLimit=2048
spring.datasource.hikari.data-source-properties.useServerPrepStmts=true
spring.datasource.hikari.data-source-properties.useLocalSessionState=true
spring.datasource.hikari.autoCommit=false
spring.jpa.properties.hibernate.jdbc.batchSize=200
I know that, when used the first time, jdbc keeps somewhere the compiled prepared statement so that next time it will be accessed in a more efficient way.
Now, suppose I have this situation:
public class MyDao{
public void doQuery(){
try(PreparedStatement stmt = connection.prepareStatement(MY_STMT)){
}
}
}
Both the following snippets will keep the compiled prepared statement in memory?
Snippet 1:
MyDao dao = new MyDao();
dao.doQuery(); //first one, expensive
dao.doQuery(); //second one, less expensive as it has been already compiled
Snippet 2:
MyDao dao = new MyDao();
dao.doQuery(); //first one, expensive
MyDao dao2 = new MyDao();
dao2.doQuery(); //will it be expensive or less expensive?
I am afraid that, by creating a new dao object, the jvm will see that prepared statement as a new one and so it will not compile it.
And, if it's not the case, is there any situation in which the jvm will "forget" the compiled statement and will compile it again?
Thanks
The most basic scenario for prepared statement reuse is that your code keeps the PreparedStatement open and reuses that prepared statement. Your example code does not fit this criteria because you close the prepared statement. On the other hand trying to keep a prepared statement open for multiple method invocations is usually not a good plan because of potential concurrency problems (eg if multiple threads use the same DAO, you could be executing weird combinations of values from multiple threads, etc).
Some JDBC drivers have an (optional) cache (pool) of prepared statements internally for reuse, but that reuse will only happen if an attempt is made to prepare the same statement text again on the same physical connection. Check the documentation of your driver.
On a separate level, it is possible that the database system will cache the execution plan for a prepared statement, and it can (will) reuse that if the same statement text is prepared again (even for different connections).
You're correct it will be compiled again. PreparedStatements will only be reused if you actually use the statement itself multiple times (ie, you call executeQuery on it multiple times).
However, I wouldn't worry too much about the cost of compiling the statement. If your query takes more than a few milliseconds, the cost of compiling will be insignificant. The overhead of compiling statements only becomes apparent when doing 1000's of operations per second.
Do a benchmark. It is the best way to get some certainty about the performance difference. It is not necessarily the case that the statement is always recompiled at server side. Depending on your RDBMS, it may cache the statements previously compiled. In order to maximize the cache hit probability, submit always exactly the same parameterized SQL text and do it over the same connection.
I'm using c3p0. I set up a pooled as follows,
cpds = new ComboPooledDataSource();
cpds.setJdbcUrl(...);
/* connection setup */
spds.setMaxStatements(200);
I have an object that prepares several prepared statements on initialization. In order to do that, I grab a connection (con = getConnection()) from the PooledDataSource and then prepare a statement (e.g., PreparedStatement stmt = con.preparedStatemet(/*sql*/)). The prepared statements are stored as private variables in the object and the current connection is closed at the end of initialization (con.close()). The prepared statements are used in methods of the object.
For prepared statements that update the database, this works just fine. However, when I call a method that uses a prepared statement (stmt.executeQuery()) to query the database, I get the following SQLException
java.sql.SQLException: You can't operate on a closed Statement!!!
at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:118)
at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:77)
at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeQuery(NewProxyPreparedStatement.java:127)
Did I get something wrong concerning the usage of c3p0?
Many thanks in advance!
Edit: Obviously, my question is partly based on my lack of understanding. As was pointed out in the definite answer, a PreparedStatement belongs to a connection and whenever the connection is closed, the associated statements should be closed, as well. But if that is the case, I don't understand what the use of c3p0's statement cache is.
you should get the same Exception calling executeUpdate(). JDBC Connection and Statement pooling is designed to be transparent: the same API that works for unpooled DataSources should be used for pooled versions too. There will be a dramatic difference in performance, but the code should be semantically interchangeable.
in an unpooled environment, it should be obvious why your approach fails: a Statement, prepared or otherwise, is a child of a Connection, without which it can't function. you are hoping that in the pooled environment, even though the Connection has been "closed", it should still exist in the pool, so hey, those Statements might be good. but that's a very bad idea (and if your attempts to do updates really are succeeding after the parent Connection has been close()ed, again, that'd be a bug, a bad one.) once a Connection has been "closed" it goes back in the pool, but not forever. other clients will check it out, and start performing transaction work that shouldn't be interrupted by your stale Statements. eventually Connections will be expired out of the pool. what should happened to your retained PreparedStatements then?
c3p0 pools Statements transparently, meaning you should use exactly the same API you would have used with no pooling. Call prepareStatement(...) on your Connection, every time. if you've enabled Statement pooling in c3p0 (as you have), then internally c3p0 will check to see whether the Statement has already been prepared, and if so it will quietly use the cached version rather than forwarding the request to the dbms.
i hope this helps!
I have made a java class which runs in it's own thread that works through a queue of mysql queries so that it doesn't block the main application thread. I want to use prepared statements, however if I keep reusing the same prepared statement then if there's two or more of that same prepared statement in the queue (I use the same preparedstatement object for each query of that type), it will have the wrong params. If I make a new preparedstatement each time will it recompile the prepared statement each time it is run or will it detect that it's already been compiled and just execute?
I think you really wont be able to utilize Prepared Statement Pooling feature available in Java connection pooling implementations like Apache DBCP. This is because you are storing prepared statement objects in the queue. If you could store your SQL and paramenters in a custom class instead, and create / execute PreparedStatements in execution thread you can get the benefit of pooling by using something like DBCP
See DBCP configurations docs on how to enable statement pooling (poolPreparedStatements)
will it recompile the prepared statement each time it is run or will
it detect that it's already been compiled and just execute?
It's up to the DBMS, not Java, but the general idea is that it can detect a reuse of an existing compiled statement and pools them under the hood, either in the driver or at the server. See the JDBC 4.0 Specification, #11.6, "Reuse of Statements by Pooled
Connections".
From the java tutorial
If you want to execute a Statement object many times, it usually reduces execution time to use a PreparedStatement object instead.
The main feature of a PreparedStatement object is that, unlike a Statement object, it is given a SQL statement when it is created. The advantage to this is that in most cases, this SQL statement is sent to the DBMS right away, where it is compiled. As a result, the PreparedStatement object contains not just a SQL statement, but a SQL statement that has been precompiled. This means that when the PreparedStatement is executed, the DBMS can just run the PreparedStatement SQL statement without having to compile it first.
http://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html#supply_values_ps
In the tutorial "Using Prepared Statements" it states that they should always be closed. Suppose I have a function
getPrice() {
}
that I expect to be called multiple times per second. Should this method be opening and closing the PreparedStatement with every single method call? This seems like a lot of overhead.
First of all, PreparedStatement are never opened. It's just a prepared Statement that is executed. The statement is sent to the RDBMS that executes the SQL statement compiled by the PreparedStatement. The connection to the SQL statement should be opened during the duration of the SQL querying and closed when no other RDMS calls is needed.
You can send many Statement/PreparedStatement as you require provided that you finally close its ResultSet and PreparedStatement once you're completed with them and then close the RDBMS connection.
Should this method be opening and closing the PreparedStatement with every single method call?
If you are creating the PreparedStatement object within the method, then you must close it, once you are done with it. You may reuse the PreparedStatement object for multiple executions, but once you are done with it, you must close it.
This is because, although all Statement objects (including PreparedStatements) are supposed to be closed on invoking Connection.close(), it is rarely the case. In certain JDBC drivers, especially that of Oracle, the driver will be unable to close the connection if the connection has unclosed ResultSet and Statement objects. This would mean that, on these drivers:
You should never lose a reference to a PreparedStatement object. If you do, then the connection will not be closed, until garbage collection occurs. If you are reusing PreparedStatement instances for different SQL statements, it is easy to forget this.
You should close the PreparedStatement once you no longer need it. Only then can the Connection.close() actually tear down the physical connection.
As the example in the tutorial shows you should close it after all your queries have been performed.
Once the statement is closed the RDMS may release all resources associated with your statement. Thus to use it further you'd have to re-prepare the very same statement.
I think that, after every database interaction, every component like statement, resultset must be closed, except for connection, if u tend to perform more operation.
And there is no need to worry, if you are creting the prepared statement again and again, because as you will be using the same statement again and again, there wont be any performannce issue.
Yes..No issues are there if you are creating the prepared statement n number of times, because as you will be using the same statement at all the places. No need to have any observation here regarding performance
Thanks