Spring Tip: Don't Explicitly Call Setter Methods

Posted on July 24, 2007 by Scott Leberknight

Spring has many setter methods in its classes. For example, take a look at the Hibernate support class HibernateTemplate and the Spring MVC base controller BaseCommandController. This tip is very simple:

Setter methods in Spring are for bean configuration only.

For example, say you are implementing a DAO and have extended the Spring support class HibernateDaoSupport which provides a HibernateTemplate for use in data access methods. You might have a method to search for blog entries like this:

public List findBlogEntriesByTitle(String title) {
    return getHibernateTemplate().findByNamedParam(
        "from BlogEntry e where e.title like :theTitle", "theTitle", "%" + title + "%");
}

Now let's say you have a really popular blog and want to limit the number of search results to 100. You check the HibernateTemplate JavaDocs and find there is a setMaxResults() method that looks promising. So you change your method to this:

public List findBlogEntriesByTitle(String title) {
    getHibernateTemplate().setMaxResults(100);  // Don't do this!
    return getHibernateTemplate().findByNamedParam(
        "from BlogEntry e where e.title like :theTitle", "theTitle", "%" + title + "%");
}

The above innocent-looking code has an insidious side-effect, which occurs because HibernateTemplate, like many Spring classes, is intended to be used in a thread-safe manner. In the example, calling setMaxResults sets the maximum number of results for all threads flowing through the HibernateTemplate after this method has been called, instead of just for the current Hibernate Session. While setting the maximum query results may not be a big deal, what if instead you did this:

public void updateAccountProfile(AccountProfile profile) {
    HibernateTemplate ht = getHibernateTemplate();
    int originalFlushMode = ht.getFlushMode();
    ht.setFlushMode(FlushMode.FLUSH_EAGER);  // Don't do this!
    ht.update(profile);
    ht.setFlushMode(originalFlushMode);
}

This is much worse as now the Hibernate flush mode is changed for every thread using the HibernateTemplate and causes non-deterministic flushing behavior. I have seen a project encounter this very bug and it was initially difficult to track down since most of the time everything worked just fine. As an aside, you should rarely if ever need to explicitly flush a Hibernate Session in application code.

In general, setter methods in Spring classes are intended for initial bean configuration, and should not be called at runtime as they are generally not thread-safe. So, if you want to call a setter method, your best bet is to look for an alternate, thread-safe solution. In the above example, the solution could be to use a HibernateCallback which passes you a Hibernate Session on which you can set the flush mode only for that particular Session. In any case, making sure your team understands this could save some headaches down the road, especially for developers who are new to Spring and who are used to using setter methods to change object properties at runtime, not just as an initial configuration mechanism.



Post a Comment:
Comments are closed for this entry.