Tuesday, February 7, 2012

The Entity, DAO, Service, and Controller design patterns

As mentioned previously, we will implement interfaces to inject our beans to.  There are several files - we'll show a few of them, but I leave the rest for you to download from :  to be posted later.

Entities Dao Service Controller
BasicDao.java BasicService.java MainSampleViewBean.java
BasicDaoImp.java BasicServiceImp.java  AjaxBean.java
OrderEntity.javaOrderDao.javaOrderService.javaTestBean.java
OrderDaoImp.java OrderServiceImp.java TreeBean.java
OrderPartsEntity.javaOrderPartsDao.javaOrderPartsService.javaOrderPartsBean.java
OrderPartsDaoImp.javaOrderPartsServiceImp.java
PartEntity.javaPartDao.javaPartService.java
PartDaoImp.javaPartServiceImp.java

As previously mentioned, the view will access the controller, which will access the service layer, which will access the dao, which will access the entity. The single responsibility principle states: "that every object should have a single responsibility."  Using these design patterns, we can adhere to that principle.  The entity represents our data object.  The DAO defines the crud operations to use on that data object.  The service defines the transactions to be used with the DAO.  And the controller will handle the request response activities from the view.

To review the entities, see Entities - JPA 2.0.

Let us take a "simple" example. The AjaxBean uses the OrderService to verify that a name entered into the view is unique, and returns whether or not it is.  To implement the AjaxBean, we need the following files: AjaxBean.java, OrderService.java, OrderServiceImp.java, BasicService.java, BasicServiceImp.java, OrderDao.java, OrderDaoImp.java, BasicDao.java, BasicDaoImp.java, OrderEntity.java, applicationContext.xml (previously discussed) and ServiceAndDaoBeans.xml (discussed in Spring Beans).

In this senario, we have used both xml file configuration and annotations.  Because we extend JpaDaoSupport in the Dao, we needed a way to inject the entityManagerFactory, and it was easiest to do in the xml file.  So we create the Dao and Service Beans in the xml file.  Now we are free to inject them wherever needed via either xml or annotations.

A few things to note:  These are stateless beans, so when we do our crud operations, we have to merge our entity into the persistence context since that entity is "detached".  You see that demonstrated with the delete method in the dao.

In the dao and service classes, I have commented out the annotations since we are using the xml file.  If we were using annotations, the dao would be a repository, and the serivce a service.

Beyond that, I will let the code speak for itself, as there is a bit much to walk through them line by line.  Remember, the AjaxBean is interacting with the view.  That is why you will see a validator, faces context, etc.  I did not post the ajax jsf page; that was posted in Validation.  To view the bean creation, see Project 2 - Spring Beans.  Note: the ServiceAndDaoBeans.xml is called from applicationContext.xml.

AjaxBean.java

package com.sample.beans;
import com.sample.entities.OrderEntity;
import com.sample.services.OrderService;
import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.validator.ValidatorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
/**
 * @author Thomas Dias
 */
@Controller
@Scope("request")
public class AjaxBean implements Serializable {
    @Autowired(required=true)
    OrderService orderService;
    private boolean nameValid;
    private String name;
    private String otherName;
    private int counter = 0;
    private String msg = "Starting Message";
    /** Creates a new instance of AjaxBean */
    public AjaxBean() {
    }
    @PostConstruct
    public void init() {
        nameValid = false;
    }
    public boolean isNameValid() {
        return nameValid;
    }
    public void setNameValid(boolean nameValid) {
        this.nameValid = nameValid;
    }
    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
        if (value == null || value.toString().isEmpty()) {
            FacesMessage message = new FacesMessage("A Unique Name Less Then 21 Characters Must Be Supplied");
            setNameValid(false);
            throw new ValidatorException(message);
        }
        OrderEntity orderEntity = orderService.findByName(value.toString());
        if (orderEntity != null) {
            FacesMessage message = new FacesMessage("Name is not unique.");
            setNameValid(false);
            throw new ValidatorException(message);
        }
    }
    public int getCounter() {
        return counter;
    }
    public void setCounter(int counter) {
        this.counter = counter;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public OrderService getOrderService() {
        return orderService;
    }
    public void setOrderService(OrderService orderService) {
        this.orderService = orderService;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public String getOtherName() {
        return otherName;
    }
    public void setOtherName(String otherName) {
        this.otherName = otherName;
    }
    public void listener(ActionEvent event) {
        msg = "Called Action Listener"; 
    }
    public String actionCall() {
        msg = "Called Action method";
        return null;
    }
    public String sendAlert() {
        return "showMessage('Executed a bean that returned a JavaScript Command');";
    }
}

OrderServiceImp.java

package com.sample.services;
import com.sample.dao.BasicDao;
import com.sample.dao.OrderDao;
import com.sample.dao.PartDao;
import com.sample.entities.OrderEntity;
import com.sample.entities.PartEntity;
import java.util.List;
import org.springframework.transaction.annotation.Transactional;
/**
 * @author Thomas Dias
 */
//@Service
public class OrderServiceImp extends BasicServiceImp<OrderEntity> implements OrderService {
//    @Autowired(required = true)
    private OrderDao dao;
    private PartDao partDao;
    @Override
    public BasicDao getDao() {
        return dao;
    }
    @Override
    public void setDao(BasicDao dao) {
        this.dao = (OrderDao) dao;
    }
    @Override
    @Transactional(readOnly = true)
    public OrderEntity getOrderEntity(OrderEntity orderEntity) {
        return ((OrderDao) dao).getOrderEntity(orderEntity);
    }
    @Override
    @Transactional(readOnly = true)
    public OrderEntity findByName(String name) {
        return ((OrderDao) dao).findByName(name);
    }
    @Override
    public PartDao getPartDao() {
        return partDao;
    }
    @Override
    public void setPartDao(PartDao partDao) {
        this.partDao = partDao;
    }
    @Override
    public List<PartEntity> getAvailableParts(Long id) {
        return partDao.getAvailableParts(id);
    }
}

OrderService.java

package com.sample.services;
import com.sample.dao.OrderDao;
import com.sample.dao.PartDao;
import com.sample.entities.OrderEntity;
import com.sample.entities.PartEntity;
import java.util.List;
/**
 * @author Thomas Dias
 */
public interface OrderService extends BasicService<OrderEntity>, OrderDao {
    public PartDao getPartDao();
    public void setPartDao(PartDao partDao);
    public List<PartEntity> getAvailableParts(Long id);
}

BasicServiceImp.java

package com.sample.services;
import java.util.List;
import org.springframework.orm.jpa.JpaTemplate;
import org.springframework.transaction.annotation.Transactional;
/**
 * @author Thomas Dias
 */
public abstract class BasicServiceImp<T> implements BasicService<T> {
    @Override
    @Transactional(readOnly = true)
    public int count() {
        return getDao().count();
    }
    @Override
    @Transactional(readOnly = true)
    public List<T> findAll() {
        return getDao().findAll();
    }
    @Override
    @Transactional(readOnly = true)
    public T find(Long id) {
        return (T) getDao().find(id);
    }
    @Override
    @Transactional
    public void create(T entity) {
        getDao().create(entity);
    }
    @Override
    @Transactional
    public void delete(T entity) {
        getDao().delete(entity);
    }
    @Override
    @Transactional
    public void edit(T entity) {
        getDao().edit(entity);
    }
    @Override
    @Transactional
    public void remove(Long id) {
        getDao().remove(id);
    }
    @Override
    public JpaTemplate getJpaTemplate() {
        return getDao().getJpaTemplate();
    }
}

BasicService.java

package com.sample.services;
import com.sample.dao.BasicDao;
/**
 * @author Thomas Dias
 */
public interface BasicService<T> extends BasicDao<T> {
    public BasicDao getDao();
    public void setDao(BasicDao dao);
}


OrderDaoImp.java

package com.sample.dao;
import com.sample.entities.OrderEntity;
import com.sample.entities.OrderPartsEntity;
import com.sample.entities.PartEntity;
import java.util.ArrayList;
import java.util.List;
//@Repository
public class OrderDaoImp extends BasicDaoImp<OrderEntity> implements OrderDao {
    public OrderDaoImp() {
        super(OrderEntity.class);
    }
    private List<OrderEntity> createSampleData() {
        List<OrderEntity> list = null;
        Long numberParts = (Long) getJpaTemplate().find("select count(*) from PartEntity t").get(0);
        if (numberParts == 0) {
            PartEntity tower = new PartEntity("Tower", null);
            PartEntity monitor = new PartEntity("Monitor", tower);
            PartEntity printer = new PartEntity("Printer", tower);
            PartEntity paper = new PartEntity("Paper", printer);
            PartEntity entertain = new PartEntity("Entertainment Subsystem", tower);
            PartEntity speakers = new PartEntity("Speakers", entertain);
            PartEntity glasses3d = new PartEntity("3D Glasses", entertain);
            PartEntity pad = new PartEntity("Game Pad", entertain);
            PartEntity building = new PartEntity("Building", null);
            PartEntity security = new PartEntity("Security", building);
            PartEntity guards = new PartEntity("Guards", security);
            PartEntity cameras = new PartEntity("Cameras", security);
            PartEntity alarm = new PartEntity("Alarm", security);
            PartEntity carpet = new PartEntity("Carpet", building);
            getJpaTemplate().persist(tower);
            getJpaTemplate().persist(building);
            OrderEntity sampleOrder = new OrderEntity();
            sampleOrder.setName("Sample Order1");
            OrderPartsEntity orderPartsEntity = new OrderPartsEntity(sampleOrder, tower, null);
            list = new ArrayList<OrderEntity>();
            list.add(sampleOrder);
            getJpaTemplate().persist(sampleOrder);
        }
        return list;
    }
    @Override
    public List<OrderEntity> findAll() {
        List<OrderEntity> list = super.findAll();
        if (list.isEmpty()) {
            list = createSampleData();
        }
        return list;
    }
    private void loadParts(OrderPartsEntity part) {
        for (OrderPartsEntity subPart : part.getSubOrderParts()) {
            loadParts(subPart);
        }
    }
    @Override
    public OrderEntity getOrderEntity(OrderEntity orderEntity) {
        OrderEntity fullyLoadedOrderEntity = getJpaTemplate().merge(orderEntity);
        for (OrderPartsEntity part : fullyLoadedOrderEntity.getOrderedParts()) {
            loadParts(part);
        }
        return fullyLoadedOrderEntity;
    }
    /**
     * retrieves an order entity with name = supplied name
     * @param name of the entity to be retrieved
     * @return null if no entity has name, or the first entity whose name is name, order being undefined
     */
    @Override
    public OrderEntity findByName(String name) {
        List<OrderEntity> results = getJpaTemplate().find("select q from OrderEntity q where name=?1", name);
        if (results.isEmpty()) {
            return null;
        }
        return results.get(0);
    }
}

OrderDao.java

package com.sample.dao;
import com.sample.entities.OrderEntity;
/**
 * @author Thomas Dias
 */
public interface OrderDao extends BasicDao<OrderEntity> {
    public OrderEntity getOrderEntity(OrderEntity orderEntity) ;
    public OrderEntity findByName(String name);
}

BasicDaoImp.java

package com.sample.dao;
import java.io.IOException;
import java.util.List;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import org.springframework.orm.jpa.support.JpaDaoSupport;
/**
 * @author Thomas Dias
 */
public abstract class BasicDaoImp<T> extends JpaDaoSupport implements BasicDao<T> {
    private static final Logger logger = Logger.getLogger("AbstractFacade");
    private Class<T> entityClass;
    static {
        try {
            final FileHandler fh = new FileHandler("JSFDemoAPP.log");
            fh.setFormatter(new SimpleFormatter());
            logger.addHandler(fh);
        } catch (IOException e) {
        } catch (SecurityException e) {
        }
    }
    public BasicDaoImp(Class<T> entityClass) {
        this.entityClass = entityClass;
    }
    @Override
    public void create(T entity) {
        getJpaTemplate().persist(entity);
    }
    @Override
    public void edit(T entity) {
        logger.log(Level.FINE, "Saving {0}, {1} ", new Object[]{entity.getClass(), entity});
        getJpaTemplate().merge(entity);
    }
    @Override
    public void delete(T entity) {
        logger.log(Level.FINE, "Delete {0}, {1} ", new Object[]{entity.getClass(), entity});
        getJpaTemplate().remove(getJpaTemplate().merge(entity));
    }
    @Override
    public void remove(Long id) {
        if (id != null) {
            T toRemove = find(id);
            if (toRemove != null) {
                getJpaTemplate().remove(toRemove);
            }
        }
    }
    @Override
    public T find(Long id) {
        if (logger.isLoggable(Level.WARNING)) {
            logger.log(Level.WARNING, "my warning : finding {0}", id.toString());
        }
        logger.log(Level.INFO, "my info : finding {0}", id);
        logger.severe("my severe : finding ");
        return getJpaTemplate().find(entityClass, id);
    }
    @Override
    public List<T> findAll() {
        EntityManager em = getJpaTemplate().getEntityManagerFactory().createEntityManager();
        javax.persistence.criteria.CriteriaQuery cq = em.getCriteriaBuilder().createQuery();
        cq.select(cq.from(entityClass));
        return em.createQuery(cq).getResultList();
    }
    @Override
    public int count() {
        CriteriaBuilder cb = getJpaTemplate().getEntityManagerFactory().createEntityManager().getCriteriaBuilder();
        javax.persistence.criteria.CriteriaQuery cq = cb.createQuery();
        javax.persistence.criteria.Root<T> rt = cq.from(entityClass);
        cq.select(cb.count(rt));
        Query q = getJpaTemplate().getEntityManagerFactory().createEntityManager().createQuery(cq);
        return ((Long) q.getSingleResult()).intValue();
    }
}

BasicDao.java

package com.sample.dao;
import java.io.Serializable;
import java.util.List;
import org.springframework.orm.jpa.JpaTemplate;
/**
 * @author Thomas Dias
 */
public interface BasicDao<T> extends Serializable {
    public void create(T entity);
    public void edit(T entity);
    public void delete(T entity);
    public void remove(Long id);
    public T find(Long id);
    public List<T> findAll();
    public int count();
    public JpaTemplate getJpaTemplate();
}


No comments:

Post a Comment