Brian's Waste of Time

Tue, 31 Jan 2006

Lightweight Spring

Now that CrazyBob started the Spring backlash it seems like a good idea to talk about how useful Spring is... even when you don't use it as a container =) Maybe I should leave out the even in that considering how many people are piling on Bob's bandwagon. Ah well, I still like Spring, even if I quite dislike it's XML. So, Lightweight Spring -- getting the goodness without the thirty second startup time and pages of XML.

People get all excited about transactions and the hibernate (or OJB or JDO or JDBI, I just use hibernate as an example here as people get all gushy-eyed about the Spring hibernate integration) integration, well, you can do all that without the XML pretty easily:

public void testSomething() throws Exception
{
  SessionFactory sessionFactory = getSessionFactory();

  final String name = "Brian";

  PlatformTransactionManager transactionManager = 
    new HibernateTransactionManager(sessionFactory);
  final HibernateTemplate hibernate = new HibernateTemplate(sessionFactory);
  TransactionTemplate transactions = new TransactionTemplate(transactionManager);

  Something s = (Something) transactions.execute(new TransactionCallback() 
  {
    public Object doInTransaction(TransactionStatus transactionStatus) 
    {
      return hibernate.execute(new HibernateCallback() 
      {
        public Object doInHibernate(Session session) throws HibernateException
        {
          return session.createQuery("select s from Something s where s.name = :name")
                        .setString("name", name)
                        .uniqueResult();
        }
      });
    }
  });
}

Basiclaly here we use its nice transaction management stuff completely programmatically. Not bad. You can do the same thing with the JdbcTemplate:

PlatformTransactionManager ptm = new DataSourceTransactionManager(getDataSource());
TransactionTemplate transactions = new TransactionTemplate(ptm);
final JdbcTemplate jdbc = new JdbcTemplate(getDataSource());
		
Long count = (Long) transactions.execute(new TransactionCallback()
{
    public Object doInTransaction(TransactionStatus transactionStatus)
    {
      return jdbc.queryForLong("select count(id) from something");
    }
});

Now, why would you do this? It gives you resource cleanup, transaction rollback on exceptions, flexibility in terms of what kind of transaction you use (the hibernate platform works for a data source as long as the session factory uses the same data source, the JTA one can be plugge din pretty transparently, etc), you can nest transactions inside these callbacks and the expected behavior (joining transactions, sub-transactions, etc). It is pretty nice to work with.

Now, a couple catches. First, you need to make sur ethe transaction callback is the outermost as Spring binds resources to the transaction, not the thread. This is kind of subtle as the transaction is bound to the thread, but if you had the jdbc callback around the transaction callback, the connection wouldn't be bound to the transaction, despite visually looking like it would be. You also lose control over which connection you have inside a given transaction, which usually doesn;t matter, but it might if you want to do a lot of prepared statement reuse for one connection across a lot of transactions. So it isn't a solution to everything, but few things are.

The other very useful thing I have found myself doing frequently with Spring-As-LIbrary instead of Spring-As-Container is using is using the ServletRequestDataBinder to bind request params to beans in other servlet contexts:

Something s = new Somthing();
ServletRequestDataBinder binder = new ServletRequestDataBinder(s, "");
binder.bind(servletRequest);

Such a simple thing, but so very common if the framework you are using at the moment doesn;t quite line up with what you need to do. The empty string, by the way, is a prefix, so name would map to Something#setName(String) in the above example, but if we has used new ServletRequestDataBinder(s, "s") then s.name would be bound to Something#setName(String). Commons-BeanUtils comes close to doing this, but I couldn'f find anything in there that played as nicely with the servlet request. It must be there, but it was hidden, or didn't work as I needed it, cannot recall now =(

0 writebacks [/src/java] permanent link