Performance of Spring NamedParameterJdbcTemplate Query is very slow - java

I am working on a project that requires JDBC Calls to an Oracle Database. I have set up UCP pooling to work with SpringJDBC. I have a fairly simple query that I am executing like the following...
SELECT * FROM TABLE WHERE ID IN (:ids)
my java code to set this query up looks like the following...
NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(datasource);
Map<String,Object> paramMap = new HashMap<String,Object>();
paramMap.put("ids", Arrays.asList(idArray));
List<Result> results = template.query("SELECT * FROM TABLE WHERE ID IN (:ids)",
paramMap, new ResultRowMapper());
This all performs fine as long as there is only 1 id in the array. When I add a 2nd ID the query takes nearly 5 minutes to run. If I take the exact query and execute it in SQLDeveloper, it takes .093 seconds.
Something must be going terribly wrong with my code or configuration... Does anyone have any ideas?
EDIT:
I stripped out the usage of the Spring NamedParameterJdbcTemplate and went with just straight Jdbc and everything seems to perform great. What is it that NamedParameterJdbcTemplate is doing differently?

Well, I found the difference between my straight jdbc solution and my spring-jdbc solution in this situation... It appears that as #Annjawn below explained, it is a bind variable issue not a spring-jdbc issue. My spring-jdbc issue was trying to bind a variable to an index (that doesn't exist) thus doing a table scan...
My straight JDBC solution ends up just doing a string replacement and executing as is thus no table scan...
The below link explains the difference.
http://bytes.com/topic/oracle/answers/65559-jdbc-oracle-beware-bind-variables

Related

Same SQL Query returning 2 different results

I have a java application that does a SQL query against an Oracle database, that for some reason gives way less values when executed from the SQL Developer and from the application itself.
Now to the technicalities. The application produces a connection to the db using a wrapper library that employs c3p0. The c3p0 config has been checked, so we know that this things can't be:
-Pointing to wrong database/schema
-Restricted user
Then there's the query:
select to_char(AGEPINDW.TRANSACTION.TS_TRANSACTION,'yyyy-mm') as Time,result, count(*) as TOTAL, sum(face_value) as TOTAL_AMOUNT
from AGEPINDW.TRANSACTION
where (ts_transaction >= to_timestamp(to_char(add_months(sysdate,-1),'yyyy-mm'),'yyyy-mm')
and ts_transaction < to_timestamp(to_char(sysdate,'yyyy-mm'),'yyyy-mm')) and service_id in (2,23)
group by to_char(AGEPINDW.TRANSACTION.TS_TRANSACTION,'yyyy-mm'), result;
It doesn't have any parameter and is executed via your standard PreparedStatement. Yet the return from the app is wrong and I don't know what may be. Any suggestions?

MySQL query request from WorkBench client takes much less time than JDBC .executeQuery

I am using this code:
double timeBefore = System.currentTimeMillis();
ResultSet rs = st.executeQuery(sql);
double timeAfter = System.currentTimeMillis();
System.out.println(timeAfter - timeBefore);
The return is 9904.0
While when I do the exact same query from WorkBench MySQL client:
SELECT DISTINCT completeAddress FROM DB_M3_Medium.AvailableAddressesV2 where postNr = 2300 ORDER BY completeAddress ASC;
it takes 0.285s
How is that possible?
PS: I tried it with different payload sizes and it's always approx. 10s with Java JDBC
EDIT:
I tried PreparedStatement with the same query as above and it took the same time, approx. 1s less.
I have also tried pinging with following code:
String query = "/* ping */ SELECT 1";
double timeBefore = System.currentTimeMillis();
PreparedStatement preparedStatement = DBConnect.getInstance().con.prepareStatement(query);
ResultSet rs = preparedStatement.executeQuery(query);
double timeAfter = System.currentTimeMillis();
System.out.println(timeAfter - timeBefore);
And the response was: 1306.0 which is not perfect, but better.
But I am still not getting what is wrong with it.
EDIT2:
I have figured out that the time that it takes is related to the amount of data in the DB (not the payload that I am retrieving). It appears to me like indexing didn't work. But why would I then have the issue only when I go with JDBC but not with WorkBench.
while you code in java, you are creating connection,then passing the query. That query is compiled(as you are using Statement) in the sql server and then you will get the result. This whole process needs some time.But when you execute direclty in workbench you are neither creating connection nor compiling,you are simply running the sql.Hence the time taken is less
As #SpringLearner suggested in JDBC every time you execute a query and made a new connection and cost some time. You can use a Data Source to avoid this overhead and better performance.
One thing to bear in mind is that the JDBC driver is pure Java, so you are probably running into some early JIT compilation that would not apply with the MySQL workbench. After the JDBC driver code has been through the JIT, you will probably see comparable performance. The real test for you would be put that code few more times after that and see what happens.
You can also use a PreparedStatement and see if that helps since that should be the API most comparable to what the MySQL workbench is using to avoid unnecessary recompilation of the query.

Flyway Migration: NamedParameterJdbcTemplate

Is there anyway to create a flyway migration class utilizing the NamedParameterJdbcTemplate rather than the standard JdbcTemplate that comes across from the implementation of SpringJdbcMigration?
I have an upgrade I need to run where I need to convert a column type from text to integer (Replacing a string value with an internal id associated with that value.)
The way I'm doing this is temporarily storing the string values for a reverse lookup, deleting the column and re-adding it as the proper type, and then running an UPDATE call to add in the appropriate ID to the records. I have code similar to the following I want to execute as part of the migration:
String sql = "UPDATE my_table SET my_field = :my_field WHERE my_id IN (:my_ids)";
MapSqlParameterSource source = new MapSqlParameterSource();
source.addValue("my_field", someIntValue); // the internal id of the string I want to use.
source.addValue("my_ids", someListOfPKIds); // List of PK ids.
namedTemplate.update(sql,source); //namedTemplate is a NamedParameterJdbcTemplate
However, it seems as if I can't take advantage of the NamedParameterJdbcTemplate. Am I incorrect in this?
According to Flyway sources they create a new JdbcTemplate in SpringJdbcMigrationExecutor
However you can try creating a new NamedParameterJdbcTemplate in your migration given the classic JdbcTemplate. Check this constructor. E.g. new NamedParameterJdbcTemplate(jdbcTemplate)

Slow performance on Hibernate + Java but fast when I use TOAD with the same native Oracle query

I've detected a performance problem with hibernate and native queries on Oracle. When I execute a complex SQL query with several parameters on TOAD I get the result in miliseconds. However, when I execute the same query using Hibernate this time is incremented hugely (up to four seconds or even more).
My SQL query is rather complex, return an unique value (so, the problem is not related with the time necessary to instation classes) and it contains several parameters with the the format ':nameParameter'. This query is stored in a String. For example,
String myNamedNativeQuery = "select count(*) from tables "+
"where column1 = :nameParameter1 "+
"and column2 = :nameParameter2";
//actually my sentence is much more complex!!
When I execute the sentence on TOAD it is resolved in few miliseconds. But using this sentence with Hibernate
SQLQuery query = session.createSQLQuery("myNamedNativeQuery");
query.setParameter(nameParameter1, value1);
query.setParameter(nameParameter2, value2);
query.uniqueResult();
are necessary several seconds to get the same result.
I realized if I replaced the parameters directly on the native query and then I execute the sentence using Hibernate the time decreases drastically. It would be something like that:
String strQuery = session.getNamedQuery("myNamedNativeQuery").getQueryString();
myNamedNativeQuery = myNamedNativeQuery.replace("nameParameter1", value1);
myNamedNativeQuery = myNamedNativeQuery.replace("nameParameter2", value2);
SQLQuery query = session.createSQLQuery("myNamedNativeQuery");
query.uniqueResult();
Anybody knows what's happening??
Thanks in advance.
PS: The Oracle version is 9i and Hibernate 3.2
I think what's happening with this code :
SQLQuery query = session.createSQLQuery("myNamedNativeQuery");
query.setParameter(nameParameter1, value1);
query.setParameter(nameParameter2, value2);
query.uniqueResult();
is this:
at line 1 : a query plan is created based on some expected values for your named parameters.
at line 4 : the query is executed with value1 and value2, but those values are not "good values" for the query plan that was elaborate at line 1 and so, the database is executing a very inappropriate plan for the actual values and it takes a lot of time.
Why ?
Looking at the source code of HibernateSessionImpl.createSQLQuery(...) I found this line of code:
SQLQueryImpl query = new SQLQueryImpl(
sql,
this,
factory.getQueryPlanCache().getSQLParameterMetadata( sql )
);
which is calling getQueryPlanCache() with some parameterMetaData. I assume that this metadata is not good enough.
My answer to you is:
Remove all bind parameters and use StatelessSession instead of Session
Use SQLQuery instead of query with full SQL including parameter values
StatelessSession session = sessionFactory.openStatelessSession();
I had similar problem and till I get better solution,this is what I managed to make it work.
See Hibernate parameterized sql query slow and active oracle sessions
<property name = "hibernate.temp.use_jdbc_metadata_defaults">false</property>
Add this to your hibernate.cfg.xml or update your application properties file.

Simple hibernate query returning very slowly

I have the following hibernate query:
Query query = session.createQuery("from MyHibernateClass");
List<MyHibernateClass> result = query.list();// executes in 7000ms
When logging the sql being executed in MySQL I see
select
myhibernat0_.myFirstColumn as myfirstcolumn92_,
myhibernat0_.mySecondColumn as mysecondcolumn92_,
myhibernat0_.mythirdcolumn as mythirdcolumn92_,
myhibernat0_.myFourthColumn as myfourthcolumn92_
from MyHibernateClass myhibernat0_
where (1=1);
When measurering the java code in the jvm on a small dataset of 3500 rows in MyHibernateClass database table this takes about 7000ms.
If I on the otherhand uses direct jdbc as follows:
Statement statement = session.connection().createStatement();
ResultSet rs = statement.executeQuery("select * from MyHibernateClass");// 7ms
List<MyHibernateClass> result = convert(rs);// executes in 20ms
I see the same sql going into the database but now the time spend in the java code in the jvm is 7ms.
The MyHibernateClass is a simple java bean class with getters and setters, I use no special resulttransformers as can be seen in the example. I only need a read-only instance of the class, and it doesn't need to be attached to the hibernate session.
I would rather like to use the hibernate version but cannot accept the execution times.
Added information:
After adding hibernate logging I see
[2011-07-07 14:26:26,643]DEBUG [main] [logid: ] -
org.hibernate.jdbc.AbstractBatcher.logOpenResults(AbstractBatcher.java:426) -
about to open ResultSet (open ResultSets: 0, globally: 0)
followed by 3500 of the following log statements
[2011-07-07 14:26:26,649]DEBUG [main] [logid: ] -
org.hibernate.loader.Loader.getRow(Loader.java:1197) -
result row: EntityKey[com.mycom.MyHibernateClass#1]
followed by 3500 log statements like
[2011-07-07 14:27:06,789]DEBUG [main] [logid: ] -
org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:130) -
resolving associations for [com.mycom.MyHibernateClass#1]
[2011-07-07 14:27:06,792]DEBUG [main] [logid: ] -
org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:226) -
done materializing entity [com.mycom.MyHibernateClass#1]
What does this mean?
What is Hibernate doing in the first implementation, how can I find out?
Adding a constructor with all attributes of the class did the trick, now the execution times are 70ms for the hibernate query. Previously the class only had a default constructor without arguments and a constructor with the entity id argument.
Based on the new information I felt I should provide another answer. The difference looks like that you have a one-to-many association specified for a List or Set property in your bean.
You are probably specifying that lazy=false which will turn off lazy loading. With lazy loading turned off it will fetch every associated record for every MyHibernateClass entity and this is why it is taking so long to execute.
Try setting lazy=true and this will perform much faster and then only retrieve the associated entities when explicitly requesting them from the entity.
If you utilize Log4j in your application you can set a variety of different logging options specific to Hibernate to get a better picture of what is going on behind the scenes in Hibernate.
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/session-configuration.html#configuration-logging
My guess is that this is the typical initial load time that occurs when first calling an HQL query in an application. Subsequent HQL queries should be noticeably and considerably faster after this first one.
I know this thread is old, but to update I ran into the same problem but with SQL Server and it turns out to be that SQL being printed by Hibernate and SQL Sent using the driver is different. Using MSSQL Driver by default sends the queries as stored procedures as RPC calls it's because the driver tries to optimize the query plan for MSSQL Standards , so it sends the queries something like
Hibernate Query:
select c.col1,c.col2 from customer c where c.name like #param1 and c.country like #param2
Actual Driver Sent Query:
#param1=somevalue, #param2=somevalue
declar sp ....
select c.col1,c.col2 from customer c where c.name like #param1 and c.country like #param2
go
Note: This Query I got through SQL Profiler Tool directly listening on DB
It turns out to be that sp_exec optimizations on the MSSQL tend to produce good Query plans that's get cached, but this would result in 'parameter sniffing' to know more about this problem read here...
So to overcome this I had following options:
Change my HQL to native Queries and add OPTION RECOMPILE FOR SOME PARAM
Use Direct query values instead of prepared statements so there will be no translation for param values and queries will not be modified as Stored Procedures by the Driver
Change the driver settings to not send the stored procedures (this is still bad because now the query plans in MSSQL server will be specific to this query, this is same as Option:2 but outside the code)
I didn't want to use OPTION 1 & 2 since that eliminates the whole purpose of using ORM Frameworks and I end up using OPTION 3 for now
So I changed the JDBC URL to send option prepareStatement=false
After setting this I had one more problem the query being sent like
Select * from customer c where c.name like **N**'somename' and c.country=**N**'somevalue'
Here there is a prefix before the values which states that to convert the encoding scheme , so I disable the JDBC url to sendUnicode = false
This all I did in JTDS driver options.. As far as I am concerned now the application is up and running fast. I have also introduced second level caches to cache it for some time..
Hope this helps for someone, if you have any good suggestion please let me know.
I had an incident where my application was always using every row in the result set of a query. I found a 40-fold increase in speed by setting my fetch size using the setFetchSize method below. (The performance improvement includes the addition of the count query.)
Long count = getStoreCount(customerId);
Query query = session.getNamedQuery("hqlGetStoresByCustomerId")
.setString("i_customerid",customerId)
.setFetchSize(count.intValue());
Be careful while doing this; my data set had about 100 rows, and it was scoped to a the life of a web request. If you have larger data sets, you will be eating Java Heap for the duration of the existence of that data, prior to returning it to the Java Heap.
I know this is an old question but here is what fixed it for me...
In your hibernate.cfg.xml make sure you have the correct !DOCTYPE... it should be as follows:
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
Anyone else who is facing a similar issue with SQL Server can use sendStringParametersAsUnicode=false in the JDBC Query String as shown in this answer:
JPA (Hibernate) Native Query for Prepared Statement SLOW
If you're not using Unicode for your prepared statement parameters and want to utilize the index on the varchar field which you're using as a parameter for the prepared statement, this can help.
It took me 10 seconds to execute a simple select all query before I found out that DOCTYPE tag is written wrongly in hibernate.cfg.xml and *mapping object*.hbm.class
Make sure that hibernate.cfg.xml start with
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
And mapping xml.class with
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
Now it took me 1-2 seconds to execute any queries.

Categories

Resources