I have been looking at the forum posts and working on developing a DAO pattern that works for me. Here are the requirements I have for what is a good DAO implementation:
1) The Data Access Layer (DAOs) must completely hide the EIS being used. If there are multiple datasources, hide this, and hide the technology used to load the POJOs (castor / toplink / hiberntae / etc).
2) EJB Session Beans should use CMT.
3) The DAL implementation should support being used in a fast lane implementation for the web tier (by pass EJBs) and should support easy Junit tests for the DAL.
Here is what I came up with:
class DAOFactory{
//singleton
private DAOFactory(){}
//getaDAO
public static PersonDAO getPersonDAO() throws DataAccessException{
return new HibernatePersonDAO(getHibernateSession());
}
public static Session getHibernateSession(){
//jndi lookup
}
}
Note that I do not work with the UserTransaction and set any thread local objects. We will see how this is handled in a second.
public class HibernatePersonDAO extends BaseHibernateDAO implements PersonDAO{
public HibernatePersonDAO(Session session){
super(session);
}
public Person savePerson(Person p) throws DataUpdateException{
try{
super.save(p);
super.flush();
}catch(HibernateException he){
throw new DataUpdateException(he);
}
}
//called when someone is finished with this DAO
public boolean close(){
try{
session.close();
}...
}
}
The reason for the close on the DAO was that I had no good way that I would know when to close the session(s) when in an EJB method.
e.g.
abstract class SomeSessionFacadeBean extends BaseSessionBean{
...
public Person updatePerson(Person p){
PersonDAO pdao = null;
try{
pdao = DAOFactory.getPersonDAO();
}catch(DataAccessException dae){
}catch(DataUpdateException due){
} finally{
pdao.close();
}
}
Now, where this is especially good for me is I can bypass the DAOFactory all together when writing Junit Tests for my HibernateDAOs, or (using the "fast lane" in web apps for that matter)..
public void setup()throws Exception{
theSession = getConfiguration().getSessionFactory().getSesseion();
Transaction tx = theSession.begin();
pDAO = new HibernatePersonDAO(theSession);
}
...
public void tearDown() throws Exception{
tx.rollback();
}
This is starting to work very nicely for me. Since DAO methods always flush - and the Sessions are all in the same transaction - they can see uncommited data from other sessions in the same transaction. I hope that having the extra sessions in a transaction will not be that expensive. For the most part - this rarely exceeds two or three DAOs / sessions for most use cases anyway.
(Notice that an especially complex DAO method might be able to create nested DAOs with the same session).
If worst comes to worse - I will code my own HibernateJCA Session Factory and thinly wrap Sessions. This is the correct place to put Thread Local instances of the Session any way - not in a Base EJB as many around have been talking about. I think this is wrong because it does not seem to support remote UserTransactions - which I use when unit testing the SessionFacade classes.
Post your comments, ideas, evaluations.
|