This post explains how to customize a java.sql.Connection by overriding default properties. I demonstrate the topic by changing the isolation level of Connection object obtained from WebSphere server. By default the connection obtained from WebSphere 5.1 JNDI has a transaction isolation level of "Repeatable Read". If you ever wanted to change this level today's post will show you how. When you obtain the connection within a transaction boundary, you are not allowed to change the isolation level (the behavior is implementation-defined). Most likely an exception will be thrown. This is especially a problem if you are running distributed transactions. You dont have a lot of control over the Connection object. So the only place you could changethe isolation level is before the transaction starts but how would you do this? You'll have to intercept the connection object before it can be used in the transaction boundary. Normally, to obtain a connection you would do this:
public static Connection getConnection(String jndiName) throws DataAccessException {
	Connection connection = null;
	try {
		Context context = new InitialContext();
		DataSource ds = (DataSource) context.lookup(jndiName);
		connection = ds.getConnection();
	} catch (ClassCastException e) {
		logger.debug(e);
		throw new RuntimeException(e);
	} catch (NamingException e) {
		logger.debug(e);
		throw new RuntimeException(e);
	} catch (Exception e) {
		logger.debug(e);
		throw new DataAccessException(e);
	}

	return connection;
}
To customize the connection you'll need to understand a few WebSphere classes. The DataSource you obtain is an instance of com.ibm.websphere.rsadapter.WSDataSource. This class has an overloaded getConnection() method that takes a com.ibm.websphere.rsadapter.JDBCConnectionSpec. It is this class that controls the isolation level. The following code demonstrates this logic:
import com.ibm.websphere.rsadapter.*;
// ...

JDBCConnectionSpec connSpec = WSRRAFactory.createJDBCConnectionSpec();
connSpec.setTransactionIsolation(CONNECTION.TRANSACTION_REPEATABLE_READ);
// connSpec.setXXX() set other properties you want

Connection connection = ((WSDataSource)datasource).getConnection(connSpec);
Note that this solution is not portable across containers. But using reflection you can make the solution portable. I'll use an abstract class called DataSourceCustomizer and a helper class ConnectionProvider to obtain the connection after customizing it.
/**
 * @(#)WebSphereDataSourceCustomizer.java	May 22, 2007
 */

import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.HashMap;
import java.util.Map;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

/**
 * Adds/overrides properties of a WSDataSource class. This is to
 * be used with WebSphere 5.1 version. This class uses reflection and WebSphere
 * APIs to set isolation level.
 *
 * @author $Author: vijaykandy $
 * @version $Revision: 1.1 $
 */
public class WebSphereDataSourceCustomizer extends DataSourceCustomizer {

	protected Class WSDataSourceClass;

	protected Class JDBCConnectionSpecClass;

	protected Class WSRRAFactoryClass;

	protected Method createJDBCConnectionSpecMethod;

	protected Method getConnectionMethod;

	protected Method setTransactionIsolationMethod;

	public WebSphereDataSourceCustomizer() {
		init();
	}

	public WebSphereDataSourceCustomizer(Map map) {
		super(map);
		init();
	}

	public WebSphereDataSourceCustomizer(HashMap map) {
		super(map);
		init();
	}

	private void init() {
		ClassLoader classLoader = ReflectionUtil.getClassLoader();
		try {
			WSDataSourceClass = classLoader.loadClass("com.ibm.websphere.rsadapter.WSDataSource");
			JDBCConnectionSpecClass = classLoader.loadClass("com.ibm.websphere.rsadapter.JDBCConnectionSpec");
			WSRRAFactoryClass = classLoader.loadClass("com.ibm.websphere.rsadapter.WSRRAFactory");
			createJDBCConnectionSpecMethod = WSRRAFactoryClass.getMethod("createJDBCConnectionSpec", null);
			getConnectionMethod = WSDataSourceClass.getMethod("getConnection", new Class[] { JDBCConnectionSpecClass });

			Class args[] = new Class[] { int.class };
			setTransactionIsolationMethod = JDBCConnectionSpecClass.getMethod("setTransactionIsolation", args);
		} catch (SecurityException e) {
			throw new RuntimeException(e);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException(e);
		} catch (NoSuchMethodException e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * The following logic is executed using reflection:
	 *
	 * 
    * int level = ... a level defined by the client ...
    * JDBCConnectionSpec jdbcConnectionSpec = WSRRAFactory.createJDBCConnectionSpec();
    * jdbcConnectionSpec.setTransactionIsolation(level);
    * Connection connection = ((WSDataSource) datasource).getConnection(jdbcConnectionSpec);
    * 
	 *
	 * @throws NamingException
	 */
	public Connection getConnection(String jndiName) throws NamingException {

		// Lookup the DataSource located at the given location
		Context context = new InitialContext();
		DataSource ds = (DataSource) context.lookup(jndiName);

		// createJDBCConnectionSpec() is a static method
		Object jdbcConnectionSpec = ReflectionUtil.invokeMethod(createJDBCConnectionSpecMethod, null, null);

		// Set the isolation level
		if (map.containsKey(ISOLATION_LEVEL)) {
			Integer isolationLevel = (Integer) map.get(ISOLATION_LEVEL);
			Object args[] = new Object[] { isolationLevel };
			ReflectionUtil.invokeMethod(setTransactionIsolationMethod, jdbcConnectionSpec, args);
		}

		Object args[] = new Object[] { jdbcConnectionSpec };
		Connection connection = (Connection) ReflectionUtil.invokeMethod(getConnectionMethod, ds, args);
		return connection;
	}

}
The class ConnectionProvider serves as a facade.
/**
 * Retrieves connection after applying properties passed as argument.
 *
 * @param jndiName
 * @param supplementClass
 * @return
 * @throws NamingException
 */
public static Connection getConnection(String jndiName, String supplementClass, Map map) throws NamingException {
	Object args[] = new Object[] { map };
	DataSourceCustomizer supplement = (DataSourceCustomizer) ReflectionUtil.newInstance(supplementClass, args);
	return supplement.getConnection(jndiName);
}
Now you can use these classes like so:
/**
 * @(#)Test.java	May 27, 2008
 */
import java.sql.Connection;
import java.util.HashMap;
import java.util.Map;

/**
 * Tests a connection. Preferred way would be to use a properties file that can
 * be read by Class.getInputStream() and dynamically set the
 * customizer class property.
 *
 * @author $Author: vijaykandy $
 * @version $Revision: 1.1 $
 */
public class Test {

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		String jndiName = "jdbc/MyDS";
		String customizerClass = "WebSphereDataSourceCustomizer";

		// Set isolation level to Connection.TRANSACTION_READ_COMMITTED
		Map properties = new HashMap();
		properties.put("ISOLATION_LEVEL", new Integer(2));

		Connection connection = ConnectionProvider.getConnection(jndiName, customizerClass, properties);
	}
}
All source code is attached here. No WebSphere classes are need to compile (that was the whole point anyway!).

Resources

[1] IBM Example 1 [2] IBM Example 2

Complete Source Code

customize-java-sql-connection.zip