Friday, July 23, 2010

DAO or not DAO? This is the question...

In a multi tiers architecture application, data access are managed by dedicated object, called: Data Access Object, aka DAO. They are responsible how to connect to data store and brings functionalities for making operation on data. For the following explanation, I will use JPA as ORM.

DAO approach

A general way to implement the DAO layer is a parametrized interface defining contract for CRUD operations:

public interface GenericDao<E, PK> {
E findByPrimaryKey(PK pk);

void save(E entity);

void update(E entity);

void delete(E entity);
}

And now, we define an abstract class implementing a JPA dedicated DAO. The default constructor is used to determine the type of the entity and the primary key type, that allows to implement CRUD operation:

public abstract class JpaDao<E, PK> implements GenericDao<E, PK> {

private EntityManager entityManager;

public JpaDao() {
ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
this.entityType = (Class<E>) genericSuperclass.getActualTypeArguments()[0];
}

protected EntityManager getEntityManager() {
return entityManager;
}

public E findByPrimaryKey(PK pk) throws DataAccessException {
return entityManager.find(entityType, pk);
}

public void save(E entity) throws DataAccessException {
entityManager.persist(entity);
}

public void update(E entity) throws DataAccessException {
entityManager.merge(entity);
entityManager.flush();
}

public void delete(E entity) throws DataAccessException {
entityManager.remove(entity);
}

public List<E> findAll() throws DataAccessException {
Query query = entityManager.createQuery("SELECT e FROM " + entityType.getName() + " AS e");
return query.getResultList();
}
}

This implementation forces to get one DAO per entity, even in case non additional operations are needed. And separate DAO and entities encourages AnemicDomainModel discussed by Martin Follower. In this case, entities are only by bag of getters and setters without any behavior.

Brings DAO functionalities to entities

My proposal is to implement a utility class to manage data access. A kind of generic DAO.

Utilities for DAO implementation

This class has to be a singleton that shares an EntityManager between every entities. It implements too methods to create and commit transaction. Generic functions are implemented fro CRUD operation and takes a entity class object as parameter to apply CRUD on entity class:

public final class EntityUtility {
private EntityManager entityManager;

private static EntityUtility instance = new EntityUtility();

public static EntityUtility instance() {
return instance;
}

public final EntityManager getEntityManager() {
return entityManager;
}

public final void setEntityManager(EntityManager em) {
entityManager = em;
}

public <T> List<T> find(Class<T> entityClass, String where, Object... values) {
EntityTransaction transaction = getEntityManager().getTransaction();
transaction.begin();
StringBuilder builder = new StringBuilder("SELECT e FROM ");
builder.append(entityClass.getName());
builder.append(" AS e WHERE ");
builder.append(where);
Query query = getEntityManager().createQuery(builder.toString());
for(int i = 0; i < values.length; i++) {
query.setParameter(i + 1, values[i]);
}

List<T> result = query.getResultList();
transaction.commit();
return result;
}

public <T> List<T> findAll(Class<T> entityClass) {
EntityTransaction transaction = getEntityManager().getTransaction();
transaction.begin();
StringBuilder builder = new StringBuilder("SELECT e FROM ");
builder.append(entityClass.getName());
builder.append(" AS e");
Query query = getEntityManager().createQuery(builder.toString());
List<T> result = query.getResultList();
transaction.commit();
return result;
}

public <T> void save(T entity) {
EntityTransaction transaction = getEntityManager().getTransaction();
transaction.begin();
getEntityManager().persist(entity);
transaction.commit();
}

public <T> void delete(T entity) {
EntityTransaction transaction = getEntityManager().getTransaction();
transaction.begin();
getEntityManager().remove(entity);
transaction.commit();
}

public <T> void refresh(T entity) {
EntityTransaction transaction = getEntityManager().getTransaction();
transaction.begin();
getEntityManager().refresh(entity);
transaction.commit();
}

public <T> void update(T entity) {
EntityTransaction transaction = getEntityManager().getTransaction();
transaction.begin();
getEntityManager().merge(entity);
transaction.commit();
}
}

Entity superclass

To bring data access capabilities to entities, I need a class to bring CRUD methods implementation to entity classes. This class should provide implementation for creation, deletion and update methods:


public class Model {
protected static EntityUtility utility = EntityUtility.instance();

public final void save() {
utility.save(this);
}
public final void refresh(){
utility.refresh(this);
}
public final void update() {
utility.update(this);
}
public final void delete() {
utility.delete(this);
}

Entity implementation

Methods such find and findAll should be implemented as static method in entity level, but use EntityUtility functionality that provides methods to find entities by using where clause passed as parameter:

@Entity
public class User extends Model {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public Long getId() {
return id;
}

public static List<User> find(String where, Object... values) {
return utility.find(User.class, where, values);
}

public static List<User> findAll() {
return utility.findAll(User.class);
}
}

Conclusion

In this case, entities should inherit a class. It could be seen as a regression as JPA brings ORM POJO based implementation. But the Model class gives ORM functionalities such:

  • persistence
  • remove
  • refresh state
  • update

Model miss methods for search functionalities. So, Entity classes can implement static methods as delegate of EntityUtility methods.

No comments:

Post a Comment