Java EE: Generic Data Access

2014/06

Here's a simple yet practical generic approach for accessing and storing data in your enterprise model.

First we define the conceptual abstraction of a specific service which access the data in our persistence layer:

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

public class DataAccessService<T extends AbstractEntity> {
    @PersistenceContext
    protected EntityManager em;
    private final Class<T> type;

    protected DataAccessService(Class<T> type) {
        this.type = type;
    }
}

We keep the type generic since we do want to implement the specifics in separate services, for example:

import javax.ejb.Stateless;

@Stateless
public class SpecificService extends DataAccessService<SpecificEntity> {

    public SpecificService() {
        super(SpecificEntity.class);
    }
}

To have generics in our abstract service, we need each specific entity to be a concrete implementation of an abstract entity that bundles common attributes and methods of all entities, for example:

import java.io.Serializable;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;

@MappedSuperclass
public abstract class AbstractEntity implements Serializable, Comparable<AbstractEntity> {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    // hashCode
    // equals
    // toString
    // getId
    // setId
}

With the a specific entity implementation example:

import javax.persistence.Entity;

@Entity
public class SpecificEntity extends AbstractEntity {
    // Fields and methods
}

Now that we are set up, we can implement a more abstract approach to CRUD in our DataAccessService.

Create/Update

public T save(T t) {
    if (t.getId() == null || t.getId() == 0) {
        em.persist(t);
    }
    else {
        em.merge(t);
    }
    return t;
}

Read

public T find(Object id) {
    return this.em.find(this.type, id);
}

public List<T> findWithNamedQuery(String namedQuery) {
    return this.em.createNamedQuery(namedQuery, this.type).getResultList();
}

public List<T> findWithNamedQuery(String namedQuery, HashMap<String, Object> params) {
    TypedQuery query = em.createNamedQuery(namedQuery, this.type);

    for (Map.Entry e : params.entrySet()) {
        query.setParameter((String) e.getKey(), e.getValue());
    }
    return query.getResultList();
}

Delete

public void remove(T t) {
    if (em.contains(t)) {
        em.remove(t);
    }
    else {
        em.remove(em.merge(t));
    }
}