0 Replies Latest reply on Oct 1, 2009 7:28 PM by Enrico Goosen

    Extending EntityQuery (Semi DAO Pattern)

    Enrico Goosen Newbie

      For anyone who wants to implement the DAO pattern in their Seam application, here's a strategy which aims to centralize data querying code to implement the DRY (don't repeat yourself) principle.


      In the Generic DAO Pattern all data access and CRUD (Create-Read-Update-Delete) operations are provided by the DAO, but since Seam already provides the EntityHome object for CRUD, it makes sense to extend EntityQuery to handle all queries in your application.


      So here's the code:


      package com.mydomain.demo.query;
      
      import java.io.Serializable;
      import java.lang.reflect.ParameterizedType;
      import java.lang.reflect.Type;
      import java.util.Collection;
      import java.util.Iterator;
      import java.util.Map;
      
      import javax.persistence.Query;
      
      import org.jboss.seam.framework.EntityQuery;
      import org.jboss.seam.persistence.PersistenceProvider;
      
      @SuppressWarnings("serial")
      public class BaseQuery<E extends Serializable,PK extends Serializable> extends EntityQuery<E> {
      
           private Class<E> entityClass = null;
      
           protected E instance;
      
           public BaseQuery() {
                String simpleEntityName = getSimpleEntityName().toLowerCase();
                setEjbql("SELECT "+simpleEntityName+" from " + getEntityName() + " " + simpleEntityName);
           }
      
           /**
            * Get the class of the entity being managed.
            * <br />
            * If not explicitly specified, the generic type of implementation is used.
            */
           @SuppressWarnings("unchecked")
           public Class<E> getEntityClass() {
                if (entityClass == null)
                {
                     Type type = getClass().getGenericSuperclass();
      
                     if (type instanceof ParameterizedType)
                     {
                          ParameterizedType paramType = (ParameterizedType) type;
                          entityClass = (Class<E>) paramType.getActualTypeArguments()[0];
                     }
                     else
                     {
                          type = getClass().getSuperclass().getGenericSuperclass();
                          if (type instanceof ParameterizedType)
                          {
                               ParameterizedType paramType = (ParameterizedType) type;
                               entityClass = (Class<E>) paramType.getActualTypeArguments()[0];
                          }
                          else
                          {
                               throw new IllegalArgumentException("Could not guess entity class by reflection");
                          }
                     }
                }
                return entityClass;
           }
      
           public E getInstance() {
                if (instance==null){
                     setInstance( createInstance() );
                }
                return instance;
           }
      
      
           public void setInstance(E instance) {
                this.instance = instance;
           }
      
           /**
            * Create a new instance of the entity.
            * <br />
            * Utility method called by {@link #getInstance()} to create a new instance
            * of the entity.
            */
           protected E createInstance() {
                if (getEntityClass()!=null) {
                     try {
                          return getEntityClass().newInstance();
                     }
                     catch (Exception e) {
                          throw new RuntimeException(e);
                     }
                } else {
                     return null;
                }
           }
      
           /**
            * The simple name of the entity
            */
           protected String getSimpleEntityName()
           {
                String name = getEntityName();
                if (name != null)
                {
                     return name.lastIndexOf(".") > 0 && name.lastIndexOf(".") < name.length()  ? name.substring(name.lastIndexOf(".") + 1, name.length()) : name;
                }
                else
                {
                     return null;
                }
           }
      
           protected String getEntityName()
           {
                try
                {
                     return PersistenceProvider.instance().getName(getInstance(), getEntityManager());
                }
                catch (IllegalArgumentException e)
                {
                     // Handle that the passed object may not be an entity
                     return null;
                }
           }
      
           public E getObjectByID(PK id) {
                return (E) getEntityManager().find(getEntityClass(), id);
           }
      
           /**
            * Get records by a named query. Can also optionally get all records starting
            * at a start position and the count of records to return
            * @param name
            * @param parameters
            * @param rowStartIndexAndCount
            * @return
            */
           @SuppressWarnings("unchecked")
           public Collection<E> findRecordsByName(final String name, final Map parameters, final int... rowStartIndexAndCount) {
                Query query = getEntityManager().createNamedQuery(name);
                if (parameters != null) {
                     for (Iterator<Map.Entry<String, Object>> it = parameters.entrySet().iterator(); it.hasNext();) {
                          Map.Entry<String, Object> entry = it.next();
                          query.setParameter(entry.getKey(), entry.getValue());
                     }
                }
                setupQueryForStartIndexAndCount(query, rowStartIndexAndCount);
                return query.getResultList();
           }
      
           /**
            * Find a single record by a named query
            * @param name
            * @param parameters
            * @return
            */
           @SuppressWarnings("unchecked")
           public E findRecordByName(String name, Map parameters) {
                Query query = getEntityManager().createNamedQuery(name);
                if (parameters != null) {
                     for (Iterator<Map.Entry<String, Object>> it = parameters.entrySet().iterator(); it.hasNext();) {
                          Map.Entry<String, Object> entry = it.next();
                          query.setParameter(entry.getKey(), entry.getValue());
                     }
                }
                return (E)query.getSingleResult();
      
           }
      
           /**
            * Helper method to setup a query with a start position and the count of records to return.
            * @param query
            * @param rowStartIndexAndCount
            */
           private void setupQueryForStartIndexAndCount(Query query, final int... rowStartIndexAndCount) {
                if (rowStartIndexAndCount != null && rowStartIndexAndCount.length > 0) {
                     int rowStartIdx = Math.max(0, rowStartIndexAndCount[0]);
                     if (rowStartIdx > 0) {
                          query.setFirstResult(rowStartIdx);
                     }
                     if (rowStartIndexAndCount.length > 1) {
                          int rowCount = Math.max(0, rowStartIndexAndCount[1]);
                          if (rowCount > 0) {
                               query.setMaxResults(rowCount);
                          }
                     }
                }
           }
      
      }
      




      package com.mydomain.demo.query;
      
      import java.util.Arrays;
      import java.util.Collection;
      import java.util.HashMap;
      import java.util.Map;
      
      import org.jboss.seam.annotations.Name;
      
      import com.mydomain.demo.model.Contact;
      
      @Name("contactList")
      public class ContactQuery extends BaseQuery<Contact,Long> {
      
           private static final long serialVersionUID = 3779715533354550167L;
      
           private static final String[] RESTRICTIONS = {
                     "lower(contact.address) like lower(concat(#{contactList.instance.address},'%'))",
                     "lower(contact.businessPhone) like lower(concat(#{contactList.instance.businessPhone},'%'))",
                     "lower(contact.cellPhone) like lower(concat(#{contactList.instance.cellPhone},'%'))",
                     "lower(contact.city) like lower(concat(#{contactList.instance.city},'%'))",
                     "lower(contact.firstName) like lower(concat(#{contactList.instance.firstName},'%'))",
                     "lower(contact.homePhone) like lower(concat(#{contactList.instance.homePhone},'%'))",
                     "lower(contact.lastName) like lower(concat(#{contactList.instance.lastName},'%'))",
                     "lower(contact.state) like lower(concat(#{contactList.instance.state},'%'))",
                     "lower(contact.zip) like lower(concat(#{contactList.instance.zip},'%'))",};
      
      
           public ContactQuery() {
                super();
                setRestrictionExpressionStrings(Arrays.asList(RESTRICTIONS));
                setMaxResults(25);
           }
      
           /*
            * @NamedQuery implementation
            * @See com.mydomain.demo.model.Contact
            */
           public Collection<Contact> findContactsByFirstNameLike(String firstName, int... rowStartIndexAndCount){
                Map<String,String> parameters = new HashMap<String,String>();
                parameters.put("firstName",firstName);
                return findRecordsByName("findContactsByFirstNameLike", parameters, rowStartIndexAndCount);
           }
      
           /*
            * @NamedQuery implementation
            * @See com.mydomain.demo.model.Contact
            */
           public Collection<Contact> findContactsByLastNameLike(String lastName, int... rowStartIndexAndCount){
                Map<String,String> parameters = new HashMap<String,String>();
                parameters.put("lastName",lastName);
                return findRecordsByName("findContactsByLastNameLike", parameters, rowStartIndexAndCount);
           }
      }
      
      



      package com.mydomain.demo.model;
      
      import javax.persistence.Entity;
      import javax.persistence.FetchType;
      import javax.persistence.GeneratedValue;
      import javax.persistence.Id;
      import javax.persistence.JoinColumn;
      import javax.persistence.JoinColumns;
      import javax.persistence.ManyToOne;
      import javax.persistence.NamedQueries;
      import javax.persistence.NamedQuery;
      import javax.persistence.Version;
      
      import org.hibernate.validator.Length;
      
      @NamedQueries({
      
           @NamedQuery(name = "findContactsByFirstNameLike",
           query = "SELECT contact from Contact contact where LOWER(contact.firstName) LIKE :firstName"),
      
           @NamedQuery(name = "findContactsByLastNameLike",
           query = "SELECT contact from Contact contact where LOWER(contact.lastName) LIKE :lastName")
      
      })
      
      @Entity
      public class Contact extends BaseEntity {
      
           @Id @GeneratedValue
           private Long id;
      
           @Length(max=50)
           private String firstName;
           @Length(max=50)
           private String lastName;
           @Length(max=250)
           private String address;
           @Length(max=50)
           private String city;
           @Length(max=50)
           private String state;
           @Length(max=6)
           private String zip;
           @Length(max=20)
           private String homePhone;
           @Length(max=20)
           private String businessPhone;
           @Length(max=20)
           private String cellPhone;
      
           @Version
           private int version;
      
           @ManyToOne(fetch = FetchType.LAZY)
           @JoinColumns({@JoinColumn(name = "country_id",referencedColumnName="id")})
           private Country country;
      
           public String getAddress()
           {
                return address;
           }
           public void setAddress(String address)
           {
                this.address = address;
           }
           public String getBusinessPhone()
           {
                return businessPhone;
           }
           public void setBusinessPhone(String businessPhone)
           {
                this.businessPhone = businessPhone;
           }
           public String getCellPhone()
           {
                return cellPhone;
           }
           public void setCellPhone(String cellPhone)
           {
                this.cellPhone = cellPhone;
           }
           public String getCity()
           {
                return city;
           }
           public void setCity(String city)
           {
                this.city = city;
           }
           public String getFirstName()
           {
                return firstName;
           }
           public void setFirstName(String firstName)
           {
                this.firstName = firstName;
           }
           public String getHomePhone()
           {
                return homePhone;
           }
           public void setHomePhone(String homePhone)
           {
                this.homePhone = homePhone;
           }
           public String getLastName()
           {
                return lastName;
           }
           public void setLastName(String lastName)
           {
                this.lastName = lastName;
           }
           public String getState()
           {
                return state;
           }
           public void setState(String state)
           {
                this.state = state;
           }
           public String getZip()
           {
                return zip;
           }
           public void setZip(String zip)
           {
                this.zip = zip;
           }
           public Long getId()
           {
                return id;
           }
           public void setId(Long id)
           {
                this.id = id;
           }
           public Country getCountry() {
                return country;
           }
           public void setCountry(Country country) {
                this.country = country;
           }
      
           @Override
           public void setDefaults(boolean relatedDefaults) {
                if (relatedDefaults){
                     Country country = new Country();
                     setCountry(country);
                }
           }
      
      }
      



      package com.mydomain.demo.model;
      
      import java.io.Serializable;
      
      @SuppressWarnings("serial")
      public abstract class BaseEntity implements Serializable {
      
           abstract public void setDefaults(boolean relatedDefaults);
      
           public void setDefaults() {
                setDefaults(false);
           }
      }