Posts tagged: java

Using a custom Oracle collection type with iBatis

By Rob, September 17, 2009 8:28 pm

Some months ago I came across a problem with the more complex custom Oracle types in combination with iBatis. I thought that it would be nice to share it with you. In my case I had to link a list of objects in Java to an array of structs in SQL (Oracle).

The biggest part of the trick is inside a custom type handler, which is actually a helper class with two important methods, the setParameter and the getResult. setParameter if for mapping parameters before firing the query and the getResult is for processing the result (if you use a custom type as an OUT parameter). For the example I used a Quantity class which has two properties, id and quantity. My Oracle types are a struct (Quantity) and a table of that struct (QuantityList)

The code will show you the rest, because once you know how to deal with this, other cases are quite easy.

package nl.tigrou.test;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;

import java.util.List;
import oracle.jdbc.driver.OracleTypes;
import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;
import oracle.sql.STRUCT;
import oracle.sql.StructDescriptor;

import org.springframework.jdbc.support.nativejdbc.C3P0NativeJdbcExtractor;
import org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor;

import com.ibatis.sqlmap.client.extensions.ParameterSetter;
import com.ibatis.sqlmap.client.extensions.ResultGetter;
import com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;
import com.ibatis.sqlmap.engine.type.JdbcTypeRegistry;
import com.mchange.v2.c3p0.impl.NewProxyConnection;

public class QuantityListTypeHandler implements TypeHandlerCallback
{

  private static final String QUANTITY = "QUANTITY";
  private static final String QUANTITYLIST = "QUANTITYLIST";

  static
  {
    JdbcTypeRegistry.setType(QUANTITY, OracleTypes.STRUCT);
    JdbcTypeRegistry.setType(QUANTITYLIST, OracleTypes.ARRAY);
  };  

  public void setParameter(ParameterSetter setter, Object parameter) throws SQLException
  {
    try
    {
      List<Quantity> quantities = (List<Quantity>) parameter;

      Connection conn = setter. getPreparedStatement(). getConnection();
      if(conn instanceof NewProxyConnection)
      conn = new C3P0NativeJdbcExtractorImpl().getNativeConnection(conn);

      StructDescriptor quantityStruct = StructDescriptor.createDescriptor(QUANTITY, conn);
      ArrayDescriptor quantityList = ArrayDescriptor.createDescriptor(QUANTITYLIST, conn);

      STRUCT[] elements = new STRUCT[quantities == null ? 0 : quantities.size()];

      for (int count = 0; count < elements.length; count++)
      {
        Quantity quantity = quantities.get(count);

        elements[count] = new STRUCT(quantityStruct, conn, new Object[] { quantity.getId(), quantity.getQuantity() });
      }

      ARRAY array = new ARRAY(quantityList, conn, elements);

      setter.setArray(array);
    } catch (SQLException sqle)
    {
      throw sqle;
    }
  }

  private class C3P0NativeJdbcExtractorImpl extends C3P0NativeJdbcExtractor
  {
    public Connection getNativeConnection(Connection con) throws SQLException
    {
      return doGetNativeConnection(con);
    }
  }

  public Object getResult(ResultGetter getter) throws SQLException
  {
    ARRAY array = (oracle.sql.ARRAY) getter.getArray();
    ResultSet rs = array.getResultSet();
    List<Quantity> quantities = new ArrayList<Quantity>();
    while (rs != null &amp;amp;amp;&amp;amp;amp; rs.next())
    {
     STRUCT struct = (STRUCT) rs.getObject(2);
     Object[] attribs = struct.getAttributes();
     Quantity quantity = new Quantity();
     quantity.setId(((java.math.BigDecimal) attribs[0]).longValue());
     quantity.setQuantity(((java.math.BigDecimal) attribs[1]).intValue());
     quantities.add(quantity);
    }
    return quantities;
  }

  public Object valueOf(String value)
  {
    if (value == null)
      return new ArrayList<Quantity>();
    return value;
  }
}

And the corresponding query in one of your xml mapping files. The magic is all inside the inline parameter.

SELECT * FROM sometable x
LEFT JOIN TABLE (
  CAST
  (
    #quantities,handler=quantityListTypeHandler,jdbcType=STRUCT,javaType=Quantity# AS QuantityList
  )
) s ON (s.identifier = x.id)

BTW, if you haven’t noticed it yet, iBatis 3 is about to release. Check out the release candidates because they did an awesome job!

AOP Advices and Aspect combinations

By Rob, June 14, 2009 8:37 pm

It’s about a month ago that I was completely stucked with a Spring AOP configuration. While working on a better transaction management (see older post), I kept struggling in a circle of either a non functioning transaction system or broken AOP aspects. It really drove me nuts so I decided to avoid that pain for somebody else. No examples and code this time, just a simple pointer to the Spring documentation (which clearly took too much time to find :) ).

Let’s start with the note in the documentation:

Advising aspects

In Spring AOP, it is not possible to have aspects themselves be the target of advice from other aspects. The @Aspect annotation on a class marks it as an aspect, and hence excludes it from auto-proxying.

(Source – Spring reference docs)

After searching a bit more information, since the text above not really explains why they’re excluded, I found the following 2 “rules”:

  • Classes with annotations @AspectJ and classes that implement or extend any other AOP component are excluded from the autoproxy. This is because they aren’t target classes, and they perform tasks in Spring AOP infrastructure.
  • Proxy is not applied to beans that implement the interfaces BeanPostProcessor or BeanFactoryPostProcessor. The class AnnotationAwareAspectJAutoProxyCreator implements the interface BeanPostProcessor, which allows the class to modify the life cycle of beans on which a proxy must be created and applied.

The first point here explains why Aspects are not proxied. The second point names another condition.

So, conclusion:

Never use Aspects (@Apects) and proxy based transaction management on the same bean!

It’s simple to avoid by creating a separate Aspect which advices other beans. Something like this:

@Aspect
public class TestAspect
{

  @Before("execution(* someThing) ")
  public void doSomethingElse()
  {

  }
}

That’s it! At the moment I’m working on the website of my local athletics club, with CodeIgniter I’m finally enjoying PHP programming again.

Spring’s ThreadLocalTargetSource

By Rob, May 9, 2009 10:50 am

ThreadLocalTargetSource, as the name already explains, is a proxied ThreadLocal helper class. Spring provides this class together with other pooling mechanisms as a better version of the Sun’s ThreadLocal class. One of the benefits is the already created wrapper class around the threadlocal and much more important, it destroys automatically the old thread references when the BeanFactory is destructed.

But actually these kind of features always sound to good to be true to me. I really wanted to figure out how safe it is and how far you can go. A simple testapplication showed me that it’s absolutely powerful!

So far I didn’t have much experience with ThreadLocal’s, they sound a bit evil to but on the other hand very useful. And I was afraid for the memory usage, keeping objects in memory per thread must have its cost. But Spring insures me that the objects will be automatically removed as soon as possible, although that still means that objects can live quite some time. Especially the automatically destroy functionality was for me a good reason to use the ThreadLocalTargetSource and not a custom wrapper for a ThreadLocal. If you really would like to use the ThreadLocal, be sure that you don’t forget to unset the object for every requestcycle. Otherwise it’s a just a matter of time before your application will start throwing Java heap space errors :) .

Now it’s time for my example, to start with the bean config:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

<bean id="testBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetSource" ref="testThreadLocalTargetSource" />
</bean>
<bean id="testBeanUtil" class="nl.tigrou.threadlocal.TestBeanUtil">
<property name="testBean" ref="testBean" />
</bean>
<bean id="testThreadLocalTargetSource" class="org.springframework.aop.target.ThreadLocalTargetSource"
destroy-method="destroy">
<property name="targetBeanName" value="testTarget" />
</bean>
<bean id="testTarget" class="nl.tigrou.threadlocal.TestBeanImpl" scope="prototype">
<property name="myValue" value="boe!" />
</bean>
</beans>

A simple Pojo:


public class TestBeanImpl implements TestBean {

private String myValue;

public void setMyValue(String myValue) {
this.myValue = myValue;
}

public String getMyValue() {
return myValue;
}

}

A util class to test the proxy’ing even better:


public class TestBeanUtil {

private static TestBean testBean;

public void setTestBean(TestBean testBean) {
TestBeanUtil.testBean = testBean;
}

public static TestBean getTestBean() {
return testBean;
}
}

And a simple testclient with 2 testcases:


public class ThreadLocalTest {

private ClassPathXmlApplicationContext context;

public static void main(String[] args) {
new ThreadLocalTest();
}

public ThreadLocalTest() {

context = new ClassPathXmlApplicationContext("applicationContext.xml");

for(int i=0; i < 10; i++)
{
ThreadTest test = new ThreadTest(i);
test.setName("Thread1-"+i);
test.start();
}

for(int i=0; i < 10; i++)
{
Thread2Test test2 = new Thread2Test(i);
test2.setName("Thread2-"+i);
test2.start();
}
}

private class ThreadTest extends Thread
{
private final int number;

public ThreadTest(int number) {
this.number = number;

}

public void run() {
TestBean testBean = (TestBean) context.getBean("testBean");

testBean.setMyValue(String.valueOf(number));
System.out.println(Thread.currentThread().getName() + ", bean value: " + testBean.getMyValue());

}
}

private class Thread2Test extends Thread
{
private final int number;

public Thread2Test(int number) {
this.number = number;

}

public void run() {

TestBeanUtil.getTestBean().setMyValue(String.valueOf(number));
System.out.println(Thread.currentThread().getName() + ", bean value: " + TestBeanUtil.getTestBean().getMyValue());

}
}

}

The above testclient starts 10 x 2 threads where both different threadclasses read and set the value in the threadlocal. The result in the console is:

Thread1-0, bean value: 0
Thread1-2, bean value: 2
Thread1-3, bean value: 3
Thread2-4, bean value: 4
Thread1-4, bean value: 4
Thread2-5, bean value: 5
Thread1-9, bean value: 9
Thread2-6, bean value: 6
Thread2-2, bean value: 2
Thread1-7, bean value: 7
Thread2-0, bean value: 0
Thread2-1, bean value: 1
Thread2-8, bean value: 8
Thread1-5, bean value: 5
Thread1-8, bean value: 8
Thread1-6, bean value: 6
Thread2-3, bean value: 3
Thread2-7, bean value: 7
Thread2-9, bean value: 9
Thread1-1, bean value: 1

Totally random order of execution as you can expect with threads but never a duplicate combination of threadnumber and value! Very simple but very powerful. You can access the beanvalues from everywhere withing the same thread, even with a static reference in a util class.

Next subject, my AOP headache issue with a combination of Advices and Aspects.

Nested transactions and their db connections

By Rob, April 28, 2009 10:21 pm

Last week I was researching the AOP config from an big application because I wasn’t really happy with the way it was implemented right now. The idea was to improve the performance by reducing the number of calls initiated by the AOP proxy. But there was a thing I wasn’t really sure about. How does Spring handle transactions when there are nested service class calls from one service to the other?

When I don’t how something exactly works, I create a small sample application. Nothing shocking, just a sample app which gives me the possibility to look to the different transaction propagations. And who knows if it could help other people as well, so I added the idea to my blog.

Let’s start with the Spring application context:


<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.0.xsd

http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop-2.5.xsd

http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

<aop:aspectj-autoproxy/>

<tx:advice id="txAdvice" transaction-manager="txManager">

<tx:attributes>
<!-- normally you would exclude read actions from required transactions but I only wrote a get method for testing purpose -->
<!-- tx:method name="get*" propagation="SUPPORTS" read-only="true" /-->
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>

<aop:config>
<aop:pointcut id="serviceOperation" expression="bean(*Service)"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation"/>
</aop:config>

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost/world" />
<property name="user" value="" />
<property name="password" value="" />
</bean>

<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation" value="WEB-INF/sqlmapconfig.xml" />
<property name="dataSource" ref="dataSource" />
</bean>

<bean id="countryService" class="nl.tigrou.test.service.CountryServiceImpl">
<property name="countryDao" ref="countryDao" />
<property name="cityService" ref="cityService" />
</bean>

<bean id="countryDao" class="nl.tigrou.test.persistence.CountryDaoImpl">
<property name="sqlMapClient" ref="sqlMapClient" />
</bean>

<bean id="cityService" class="nl.tigrou.test.service.CityServiceImpl">
<property name="cityDao" ref="cityDao" />
</bean>

<bean id="cityDao" class="nl.tigrou.test.persistence.CityDaoImpl">
<property name="sqlMapClient" ref="sqlMapClient" />
</bean>

</beans>

The only interesting thing in here is the tx:advice element defining the right propagation settings.

Now the pieces of Java code:


// grab countries
List<Country> countries = countryService.getCountries();

// this is the getCountries method
// which is btw a good example of how you shouldn't do this with iBatis
// always try to avoid N+1 selects!
List<Country> countries = countryDao.getCountries();
for(Country country: countries){
List<City> cities = cityService.getCities(country.getCode());
country.setCities(cities);
}

That’s all! Now enable the debug logging on for example the org.springframework.jdbc.datasource package and you’ll see something like this:

2009-04-28 22:07:47,591 [http-8080-1] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Creating new transaction with name [nl.tigrou.test.service.CountryService.getCountries]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2009-04-28 22:07:48,684 [http-8080-1] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Acquired Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@46d0cc] for JDBC transaction
2009-04-28 22:07:48,821 [http-8080-1] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Participating in existing transaction
2009-04-28 22:07:48,829 [http-8080-1] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Participating in existing transaction
2009-04-28 22:07:48,832 [http-8080-1] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Participating in existing transaction
2009-04-28 22:07:48,834 [http-8080-1] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Participating in existing transaction
2009-04-28 22:07:48,837 [http-8080-1] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Participating in existing transaction
2009-04-28 22:07:48,839 [http-8080-1] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Participating in existing transaction
2009-04-28 22:07:48,840 [http-8080-1] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Participating in existing transaction
2009-04-28 22:07:48,842 [http-8080-1] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Participating in existing transaction
2009-04-28 22:07:49,376 [http-8080-1] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Initiating transaction commit
2009-04-28 22:07:49,376 [http-8080-1] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Committing JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@46d0cc]
2009-04-28 22:07:49,376 [http-8080-1] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Releasing JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@46d0cc] after transaction
2009-04-28 22:07:49,378 [http-8080-1] DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource

As you see, Spring initializes a transaction and when another transaction is about to start, Spring runs these other db calls inside the existing transaction. Of course you can heavily influence this by changing the transaction propagation.

Conclusion: Spring did the job exactly as expected and I’m pretty happy with the solution as well.

btw, next subject is probably the use of Spring’s ThreadLocalTargetSource.

Keep an eye on your iBatis cachemodels

By Rob, April 7, 2009 10:38 pm

As you might have read already, I’m using iBatis for more then a year now and so far I really like it. Ok, it has some drawbacks and not comparable to something like Hibernate. IBatis is just an OR Mapper. The current 2.3.x release is a bit outdated in my opinion but they’re working hard on the new and shiny 3.0 version (read here about the plans) but that’s still far away, a total rewrite takes time.

One of the things clear things in iBatis is the caching mechanism, it’s plain and simple and easy customizable. By default, it’s using a LRU algorithm but you can change that easily to something different, or maybe even cooler, you’re own algorithm. But that’s for another post in the future. Last weekend I was thinking about how to monitor the succes of your iBatis query caches. You could do that (probably, never tried) with JMX but I was looking for a simple and clean solution. I already did the same in a Wicket application but I didn’t want too many extra dependencies. So I wrote a simple Servlet which accesses the Spring context and prints the cachemodels with their hitratio. Actually pretty simple, it took a lot more time to create a db schema with data and writing some code which uses a cache :) .

Simple screenshot:

picture-2

The actual code

My apologees for the horrible code layout, I’m searching for another code preview thingy for WordPress….

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>ibatiscachemodel</display-name>

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
  </context-param>
  <servlet>
    <servlet-name>myhttprequesthandler</servlet-name>
    <servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>myhttprequesthandler</servlet-name>
    <url-pattern>/CacheServlet</url-pattern>
  </servlet-mapping>
</web-app>

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://localhost/world" />
		<property name="username" value="rob" />
		<property name="password" value="rob" />
	</bean>

	<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
		<property name="configLocation" value="WEB-INF/sqlmapconfig.xml" />
		<property name="dataSource" ref="dataSource" />
	</bean>

	<bean id="myhttprequesthandler" class="nl.tigrou.test.MyHttpRequestHandler">
		<property name="countryService" ref="countryService" />
		<property name="helper" ref="helperDao" />
	</bean>

	<bean id="countryService" class="nl.tigrou.test.service.CountryServiceImpl">
		<property name="countryDao" ref="countryDao" />
	</bean>

	<bean id="countryDao" class="nl.tigrou.test.persistence.CountryDaoImpl">
		<property name="sqlMapClient" ref="sqlMapClient" />
	</bean>

	<bean id="helperDao" class="nl.tigrou.test.persistence.HelperDaoImpl">
		<property name="sqlMapClient" ref="sqlMapClient" />
	</bean>
</beans>

HelperClassDaoImpl (exposing the cachemodels from the SqlMapClient)

package nl.tigrou.test.persistence;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient;
import com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate;

public class HelperDaoImpl extends SqlMapClientDaoSupport implements HelperDao {

	public List getCacheModels() {
		final ExtendedSqlMapClient ext = (ExtendedSqlMapClient) getSqlMapClient();

		SqlMapExecutorDelegate dlgt = ext.getDelegate();
		Iterator iter = dlgt.getCacheModelNames();
		List models = new ArrayList();

		while (iter.hasNext())
			models.add(iter.next());
		return models;
	}

	 public SqlMapExecutorDelegate getSqlMapExecutorDelegate()
	  {
	    final ExtendedSqlMapClient ext = (ExtendedSqlMapClient) getSqlMapClient();
	    return ext.getDelegate();
	  }

}

MyHttpRequestHandler (inheriting from Spring’s HttpRequestHandler to access the Spring context)

package nl.tigrou.test;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import nl.tigrou.test.persistence.HelperDao;
import nl.tigrou.test.service.CountryService;

import org.springframework.web.HttpRequestHandler;

public class MyHttpRequestHandler implements HttpRequestHandler {

	private CountryService countryService;
	private HelperDao helper;

	public void handleRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		response.setContentType("text/html");
		PrintWriter pw = new PrintWriter (response.getOutputStream());

		pw.println("<html>");
	    pw.println("<head><title>Cachemodels</title>");
	    pw.println("<body>");
	    pw.println("<h1>Cachemodels</h1>");

	    countryService.getCountries();

	    pw.println("<table>");

	    Double ratio;
	    for (String name: helper.getCacheModels()) {
	    	ratio = helper.getSqlMapExecutorDelegate().getCacheModel(name).getHitRatio();
			pw.println("<tr><td>" + name + "</td><td>" + ((ratio.isNaN())? 0 : ratio) + "</td></tr>");
		}
	    pw.println("</table>");

	    pw.println("</body></html>");
	    pw.flush();
	}

	public void setCountryService(CountryService countryService) {
		this.countryService = countryService;
	}

	public CountryService getCountryService() {
		return countryService;
	}

	public void setHelper(HelperDao helper) {
		this.helper = helper;
	}

	public HelperDao getHelper() {
		return helper;
	}
}

Of course there are several other files (classes and iBatis files) but those are not really interesting. I’ve zipped my eclipse project and uploaded it here. Download

Panorama Theme by Themocracy