Way2Java

JDBC Performance Tuning


1.0  Introduction
        1.1  Connection Pooling
2.0  Auto-Commit
3.0  Closing JDBC objects
4.0  Optimization with Statement
	4.1 Choose right Statement interface
        4.2  Do batch update
        4.3  Do batch retrieval using Statement
        4.4  Close Statement when finished
5.0  Optimization with ResultSet
	5.1  Do batch retrieval using ResultSet
        5.2  Setup proper direction of processing rows
        5.3  Use proper getxxx() methods
        5.4  Close ResultSet when finished
6.0  Optimization with SQL Query 

1.0 Introduction

Performance is usually considered an issue at the end of a development cycle when it should really be considered from the start. Often, a task called "performance tuning" is done after the coding is complete, and the end user of a program complains about how long it takes the program to complete a particular task.

Java Database Connectivity (JDBC) is used extensively by many Java applications. Getting database connection is very expensive. If your application requires database connections that are repeatedly opened and closed, this can become a significant performance issue. Now let us see how best we can optimize by clearing the bottlenecks and increase the performance.

When the application server accesses the database, it is also one of the bottlenecks to be considered in the performance.

I/O operations (keyboard input) on the system will slower the connection as I/O threads have maximum priority.

Whenever all the database records are to be returned, better way is put all the records in a Vector and return.

The following example method illustrates standard database interaction by accessing all the columns from every row of a specified database table and storing the data from each row into a String[] array and finally putting all the rows in a Vector.

public static Vector getATable(String tablename, Connection Connection) throws SQLException
  {
    String sqlQuery = "SELECT * FROM " + tablename; 
    Statement statement = Connection.createStatement();
    ResultSet resultSet = statement.executeQuery(sqlQuery);
    int numColumns = resultSet.getMetaData().getColumnCount();
    String[] aRow = new String[numColumns];
    Vector allRows = new Vector();
    while(resultSet.next())
    {                     //ResultSet access is 1-based, arrays are 0-based
      aRow[i] = resultSet.getString(i+1);
    } 
    allRows.addElement(aRow); 
    return allRows;
Now start reading JDBC Performance Tuning and pass your comments at the end of this tutorial.

Adding to an array is more efficient than adding each element to a vector.

1.1 Connection Pooling

When WebLogic Application Server (or any Web server like Tomcat) starts, connections from the connection pools are opened and are available to all clients. When a client closes a connection from a connection pool, the connection is returned to the pool and becomes available for other clients; the connection itself is not closed. There is little cost to opening and closing pool connections.

How many connections should you create in the pool? A connection pool can grow and shrink according to configured parameters, between a minimum and a maximum number of connections. The best JDBC Performance Tuning will always be when the connection pool has as many connections as there are concurrent users.

2.0 Auto-Commit

If auto-commit feature is on, every SQL statement is committed as it is executed. If more than one SQL statement is executed by your program, then a small performance increase can be achieved by turning off auto-commit and by committing all transactions at a time.

try
{
  connection.setAutoCommit(false); 
  PreparedStatement ps = connection.preareStatement( "UPDATE employee SET Address=? WHERE name=?");
  ps.setString(1,"Austin");
  ps.setString(2,"RR");
  ps.executeUpdate();
  PreparedStatement ps1 = connection.prepareStatement( "UPDATE account SET salary=? WHERE name=?");
  ps1.setDouble(1, 5000.00);
  ps1.setString(2,"RR");
  ps1.executeUpdate();
  connection.commit();
  connection.setAutoCommit(true);
}
catch(SQLException e)
{ 
  connection.rollback();
}
finally
{
  if(ps != null)  { ps.close(); }
  if(ps1 != null) { ps1.close(); }
  if(connection != null) { connection.close(); }
}

This batch transaction gives good JDBC performance by reducing commit calls after each statement’s execution.

3.0 Explicitly closing JDBC objects

Oracle JDBC objects such as Connection, ResultSet, PreparedStatement and CallableStatement do not have finalizer methods. If you do not explicitly call the close() method for each of these objects as soon as you are done wit them, your programs are likely to run out of resources.

Close JDBC objects in finally block. In addition of closing, assign null value afterwards, so that it will be immediately eligible for garbage collection. Doing these things will keep your programs nice and tidy.


4.0 Optimization with Statement

Statement interface represents SQL query and execution and they provide number of methods and constants to work with queries. They also provide some methods to fine tune performance. Programmer may overlook these fine tuning methods that result in poor performance. The following are the tips to improve JDBC performance by using statement interfaces

1. Choose the right Statement interface
2. Do batch update
3. Do batch retrieval using Statement
4. Close Statement when finished

4.1 Choose right Statement interface

There are three types of Statement interfaces in JDBC to represent the SQL query and execute that query, they are Statement, PreparedStatement and CallableStatement.

Statement is used for static SQL statement with no input and output parameters, PreparedStatement is used for dynamic SQL statement with input parameters and CallableStatement is used for dynamic SQL satement with both input and output parameters, but PreparedStatement and CallableStatement can be used for static SQL statements as well. CallableStatement is mainly meant for executing stored procedures.

PreparedStatement gives better performance when compared to Statement because it is pre-parsed and pre-compiled by the database once for the first time and then onwards it reuses the parsed and compiled statement. Because of this feature, it significantly improves performance when a statement executes repeatedly, It reduces the overload incurred by parsing and compiling.

CallableStatement gives better performance when compared to PreparedStatement and Statement when there is a requirement for single request to process multiple complex statements. It parses and stores the stored procedures in the database and does all the work at database itself that in turn improves JDBC performance. But we loose java portability and we have to depend up on database specific stored procedures.

4.2 Do batch update

You can send multiple queries to the database at a time using batch update feature of statement objects this reduces the number of JDBC calls and improves performance. Here is an example of how you can do batch update.

statement.addBatch(“sql query1”);
statement.addBatch(“sql query2”);
statement.addBatch(“sql query3”);
statement.executeBatch();

Example Program:

// to execute a batch of sql commands with single statement.

import java.sql.*;
public class BatchUpdates
{
  public static void main(String args[]) throws Exception
  {
    Class.forName("oracle.jdbc.driver.OracleDriver");
    Connection con = DriverManager.getConnection ("jdbc:oracle:thin:@localhost:1521:orcl","scott","tiger");
    Statement stmt=con.createStatement();

    con.setAutoCommit(false);

    stmt.addBatch("insert into Employee values(498,'SNRao',2000.50)");
    stmt.addBatch("delete from Employee where empid=100”);
    stmt.addBatch("update Employee set empsal=7777 where empid=200”);
    stmt.executeBatch();                          // executes all addBatch() statements
    System.out.println("Records inserted");
    stmt.close();
    con.close();
  }
}

All three types of statements have these methods to do batch update.

4.3 Do batch retrieval using Statement

You can get the default number of rows that is provided by the driver. You can improve performance by increasing number of rows to be fetched at a time from database using setFetchSize() method of the statement object.

Initially find the default size by using
Statement.getFetchSize(); and then set the size as per your requirement

     Statement.setFetchSize(30);

Here it retrieves 30 rows at a time for all result sets of this statement.

4.4 Close Statement when finished

Close Statement object as soon as you finish working with that; it explicitly gives a chance to garbage collector to recollect memory as early as possible which in turn effects JDBC performance as in the following.

Statement.close();

5.0 Optimization with ResultSet

ResultSet interface represents data that contains the results of executing a select SQL Query and it provides a number of methods and constants to work with this data. It also provides methods to fine tune retrieval of data to improve performance. The following are the fine tuning tips to improve performance by using ResultSet interface.

1. Do batch retrieval using ResultSet
2. Set up proper direction for processing the rows
3. Use proper get methods
4. Close ResultSet when finished

5.1 Do batch retrieval using ResultSet

This is discussed earlier; anyhow repeated again.ResultSet interface also provides batch retrieval facility like Statement as mentioned above. It overrides the Statement behaviour. Initially find the default size by using ResultSet.getFetchSize(); and then set the size as per requirement

      ResultSet.setFetchSize(50);

This feature significantly improves performance when you are dealing with retrieval of large number of rows like search functionality.

5.2 Setup proper direction of processing rows

ResultSet has the capability of setting the direction in which you want to process the results, it has three constants for this purpose, they are
FETCH_FORWARD, FETCH_REVERSE, FETCH_UNKNOWN
Initially find the direction by using
ResultSet.getFetchDirection(); and then set the direction accordingly

      ResultSet.setFetchDirection(FETCH_REVERSE);

5.3 Use proper getxxx() methods

ResultSet interface provides a lot of getxxx() methods to get and convert database data types to java data types and is flexible in converting non feasible data types. For example,
getString(String columnName) returns java String object. columnName is recommended to be a VARCHAR OR CHAR type of database but it can also be a NUMERIC, DATE etc.

If you give non recommended parameters, it needs to cast it to proper java data type that is expensive. For example consider that you select a product’s id from huge database which returns millions of records from search functionality, it needs to convert all these records that is very expensive. So always use proper getxxx() methods according to JDBC recommendations. That numeric field data should retrieved as getInt() and varchar is to be retrieved as getString() etc.

5.4 Close ResultSet when finished

Close ResultSet object as soon as you finish working with ResultSet object even though Statement object closes the ResultSet object implicitly when it closes, closing ResultSet explicitly gives chance to garbage collector to recollect memory as early as possible because ResultSet object may occupy lot of memory depending on query.

    ResultSet.close();

6.0 Optimization with SQL Query

This is one of the areas where programmers generally make a mistake If you give a query like:

	
	Statement stmt = connection.createStatement();
	ResultSet rs = stmt.executeQuery("select * from employee where name=RR");

The returned result set contains all the columns data. you may not need all the column data and want only salary for RR.
The better query is "select salary from employee where name=RR"

It returns the required data and reduces unnecessary data retrieval.

=============Other Posting on Performance Tuning==============

1. Core Java Tuning – 1
2. Core Java Tuning – 2
3. Servlets & JSP Tuning