Monday, May 22, 2006

proper implementation of lazy-loading in hibernate

traditionally, a database session is closed before the view is rendered. in Hibernate, this may result to a NullPointerException when you enable the framework's lazy-loading facility. lazy-loading enabled association attributes need a live session to be dynamically populated. in the case of web applications, a servlet filter can be used to close the session after the views have been processed and before the response is sent to the client. if there is a transaction, commit and rollback demarcation can be also added. however, starting a new session and transaction can be done in the action or anywhere in the application. below is a utility class that can maintain only one session and one transaction thoughout a single "request to response" process.




import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.Transaction;
import net.sf.hibernate.cfg.Configuration;
import org.apache.log4j.Logger;
import sincang.SystemException;

public class HibernateManager
{
private static final Logger logger = Logger.getLogger(HibernateManager.class);
private static final ThreadLocal threadSession = new ThreadLocal();
private static final ThreadLocal threadTransaction = new ThreadLocal();
private static final SessionFactory sessionFactory;

static
{
try
{
sessionFactory = new Configuration().configure().buildSessionFactory();
}
catch (Throwable e)
{
logger.fatal(e);
throw new ExceptionInInitializerError(e);
}
}

public static Session getSession() throws SystemException
{
Session session = (Session) threadSession.get();
try
{
if (session == null)
{
session = sessionFactory.openSession();
threadSession.set(session);
}
}
catch (HibernateException e)
{
logger.fatal(e);
throw new SystemException(e);
}
return session;
}

public static void closeSession() throws SystemException
{
try
{
Session session = (Session) threadSession.get();
threadSession.set(null);
if ((session != null) && session.isOpen())
{
session.close();
}
}
catch (HibernateException e)
{
logger.fatal(e);
throw new SystemException(e);
}
}

public static void beginTransaction() throws SystemException
{
Transaction transaction = (Transaction) threadTransaction.get();
try
{
if (transaction == null)
{
transaction = getSession().beginTransaction();
threadTransaction.set(transaction);
}
}
catch (HibernateException e)
{
logger.fatal(e);
throw new SystemException(e);
}
}

public static void commitTransaction() throws SystemException
{
Transaction transaction = (Transaction) threadTransaction.get();
try
{
if ((transaction != null) && !transaction.wasCommitted() && !transaction.wasRolledBack())
{
transaction.commit();
threadTransaction.set(null);
}
}
catch (HibernateException e)
{
rollbackTransaction();
logger.fatal(e);
throw new SystemException(e);
}
}

public static void rollbackTransaction() throws SystemException
{
Transaction transaction = (Transaction) threadTransaction.get();
try
{
threadTransaction.set(null);
if ((transaction != null) && !transaction.wasCommitted() && !transaction.wasRolledBack())
{
transaction.rollback();
}
}
catch (HibernateException e)
{
logger.fatal(e);
throw new SystemException(e);
}
finally
{
closeSession();
}
}
}


Tuesday, May 16, 2006

hash code as key

avoid using object's hash code as key in a map or any caching facility. two or more objects can have the same value for hash code at the same time. when the .equals returns false, it does not mean that there will be always 2 distinct hash code values.

bytecode generation

"bytecode generation" to follow tip # 11 DRY - don't repeat yourself

the project needs to have a set of custom classes extending the Struts EL Tags to integrate the in-house authentication framework's policy in HMTL components level. the implementaion will only need one authentication handling in doStartTag method and the same set of attributes with the corresponding setter-getter methods across classes. entending from a parent custom class is not possible; modifying the Struts Tag base class implementation is not pragmatic; and creating AOP crosscutting using the Spring Framework (already integrated in the project) will not work since only the application server has the control over the instances of the tags. thus, i choose class generation at runtime.

law of demeter

one way of decoupling components is through law of demeter. the concept states that a class should only call a method that belongs to itself(instance and class methods), method parameters, class and instance attributes and local references. there will be no call on the object's method that is being returned from a method call.

sample:

public void method(MyClass myClass)
{
myClass.getAnotherClass().thirdLevelMethod(); // violation
}

public void method(AnotherClass anotherClass)
{
anotherClass.thirdLevelMethod(); // correct
}