Search code examples
javajpaeclipselinkdaoopenejb

How to make EntityManager works properly in DAO Factories?


I want to implement DAO pattern described here with EclipseLink and OpenEJB. The first try with Figure 9.1 listed in link works fine. The code is like:

// CustomerDAO
@Local
public interface CustomerDAO {
    Customer findCustomer(String id);
}

// OracleCustomerDAO
@Stateless
@Local(CustomerDAO.class)
@TransactionManagement(TransactionManagementType.CONTAINER)
public class OracleCustomerDAO implements CustomerDAO {

    @PersistenceContext(unitName = "MY_EJB")
    private EntityManager em;

    public Customer findCustomer(String id) {
        return em.find(Customer.class, id);
    }
}

And in my service:

public class CustomerService implements CustomerServiceLocal {
    @EJB
    CustomerDAO customerDAO;

    public Customer findCustomer(String id) {
        return customerDAO.findCustomer(id);
    }
}

However, when I want to adapt Factory pattern, I can't figure out what's the correct way to do it. I wrote the code as the tutorial in the link and add two classes like this:

// DAOFactory
public abstract class DAOFactory {
    public abstract CustomerDAO getCustomerDAO();

    public enum Factory {
        ORACLE;
    }

    public static DAOFactory getDaoFactory(Factory whichFactory) {
        switch (whichFactory) {
        case ORACLE:
            return new OracleDAOFactory();
            break;
        default:
            break;
        }
    }
}

// OracleDAOFactory
public class OracleDAOFactory extends DAOFactory {

    @Override
    public CustomerDAO getCustomerDAO() {
        return new OracleCustomerDAO();
    }

}

And I modify my service to this:

public class CustomerService implements CustomerServiceLocal {

    public Customer findCustomer(String id) {

        CustomerDAO customerDAO = DAOFactory.getDaoFactory(Factory.ORACLE).getCustomerDAO();

        return customerDAO.findCustomer(id);
    }
}

And this gives me a NullPointerException. When I trace the code with debugger, I found that EntityManager in OracleCustomerDAO is null. I think it's because I didn't do any @EJB injection in my new service, but I have no idea where can I put that @EJB injection.

So what's the proper way to inject an EntityManager with DAO Factories Pattern?


Solution

  • In order for EJB to behave like EJB, you should no create them. The EJB container is responsible to create them an make sure every decoration (transaction, injection) is correctly apply.

    The EJB container is already implementing the factory pattern for you. You should use its functionality to get a particular implementation (I have not experience with stand alone openEJB but in a full Java EE environment CDI and JNDI are providing the needed functionality).

    It seems you should be able to use JNDI to retrieve the expected EJB.

    This code should work if the name of the EJB is by default the class name:

    CustomerDAO friend = (CustomerDAO) new InitialContext().lookup("java:comp/env/OracleCustomerDAO");
    

    (full doc)

    For the CDI way, you can use its alternative mechanism (TommEE doc).

    A little word about patterns. They should be use to fix a pain point. The J2EE patterns have been documented to overcome the limitation of J2EE. In future release of Java EE, they introduce element that implements the different pattern in a event more usable way. JAP is part of it.

    JPA is an implementation of the Domain Store pattern. In the Domain Store diagram you can clearly see that it already includes DAO. So what is the point to create DAO on top of a abstraction that already include DAO?