Generic CRUD Service aka DAO - EJB 3.1/0 Code - Only If You Really Needed

The term Data Access Object (DAO) is actually wrong. An object has state and behavior, and the behavior even can depend on the state. DAO is stateless, only only consists of data manipulation operations. Data Access Procedure (DAP) would be a more exact term. A Data Access Service, or "CRUD Service" would be a better, and even buzzword-compatible, term :-). 

javax.persistence.EntityManager is already a perfect realization of the DAO (DAP) pattern - it is directly injectable into Services (I will cover Services later) or ServiceFacades. Just for CRUD use cases - DAOs are not needed - they would degrade to plain delegates and so dead code. In real projects, however, you will get likely some reusable queries, which should be maintained in a central place. In that case it is absolutely worth to introduce a DAO. If you really need it, you could deploy one DAO-instance, for all use cases. It is easy with generics:

public interface CrudService {
    public  T create(T t);
    public   T find(Class type,Object id);
    public   T update(T t);
    public void delete(Class type,Object id);
    public List findWithNamedQuery(String queryName);
    public List findWithNamedQuery(String queryName,int resultLimit);
    public List findWithNamedQuery(String namedQueryName, Map parameters);
    public List findWithNamedQuery(String namedQueryName, Map parameters,int resultLimit);
}

 The interface-implementation is actually a Service, which always has to be executed behind a ServiceFacade:

@Stateless
@Local(CrudService.class)
@TransactionAttribute(TransactionAttributeType.MANDATORY)
public class CrudServiceBean implements CrudService {
   
    @PersistenceContext
    EntityManager em;
    
    public  T create(T t) {
        this.em.persist(t);
        this.em.flush();
        this.em.refresh(t);
        return t;
    }

    @SuppressWarnings("unchecked")
    public  T find(Class type,Object id) {
       return (T) this.em.find(type, id);
    }

    public void delete(Class type,Object id) {
       Object ref = this.em.getReference(type, id);
       this.em.remove(ref);
    }

    public  T update(T t) {
        return (T)this.em.merge(t);
    }

    public List findWithNamedQuery(String namedQueryName){
        return this.em.createNamedQuery(namedQueryName).getResultList();
    }
    
    public List findWithNamedQuery(String namedQueryName, Map parameters){
        return findWithNamedQuery(namedQueryName, parameters, 0);
    }

    public List findWithNamedQuery(String queryName, int resultLimit) {
        return this.em.createNamedQuery(queryName).
                setMaxResults(resultLimit).
                getResultList();    
    }

    public List findByNativeQuery(String sql, Class type) {
        return this.em.createNativeQuery(sql, type).getResultList();
    }
    
   public List findWithNamedQuery(String namedQueryName, Map parameters,int resultLimit){
        Set> rawParameters = parameters.entrySet();
        Query query = this.em.createNamedQuery(namedQueryName);
        if(resultLimit > 0)
            query.setMaxResults(resultLimit);
        for (Entry entry : rawParameters) {
            query.setParameter(entry.getKey(), entry.getValue());
        }
        return query.getResultList();
    }
}


The only problem are the parameters. The number of parameters is not known in advance, so it has to be passed from the application. I used a simple Builder-Utility, which makes the parameter construction a bit more concise.

public class QueryParameter {
    
    private Map parameters = null;
    
    private QueryParameter(String name,Object value){
        this.parameters = new HashMap();
        this.parameters.put(name, value);
    }
    public static QueryParameter with(String name,Object value){
        return new QueryParameter(name, value);
    }
    public QueryParameter and(String name,Object value){
        this.parameters.put(name, value);
        return this;
    }
    public Map parameters(){
        return this.parameters;
    }
}

 The query-construction is almost fluent:


       int size = this.crudServiceBean.findWithNamedQuery(Book.BY_NAME_AND_PAGES,
                with("name",book.getName()).
                and("pages", book.getNumberOfPages()).
                parameters()).
                size();
        assertEquals(1,size);


I used the implementation above in many Glassfish v2(1) projects, but never in that puristic way. I always added project-specific queries - the actual added value. I wouldn't deploy DAOs just for CRUD, but many architects love it. Its sometimes easier to deploy some superfluous code - just to avoid more expensive discussions :-).

I pushed the origin sample project into http://kenai.com/projects/javaee-patterns - with some unit-tests and two variants. I tested the sample project with Glassfish Prelude v3 and WAR-deployment with Netbeans 6.7m1 - it should work with 6.7rc3 as well.

[The code was originally published in this Real World Java EE Patterns book, Page 141

Comments:

Hi Adam,

that is exactly the way, we implemented the service in our project.
Just the findByXXX we have not implemented. These are implemented in the specific way for the special services then, but I will think on adding these too.

For creating queries we use ejb3-criteria [http://code.google.com/p/ejb3criteria] It does not support all features jet, but enough for us to build queries.

Regards,

Stephan

Posted by Stephan on June 24, 2009 at 11:43 AM CEST #

By the way, we added also a validate-method wich is called before insert/update. Because of missing validation in JPA, we use validation of Hibernate:
<code>
/**
* Validates the given entity.
* If the validation fails an {@link ValidationException} is thrown.
* @param entity the entity to validate
*/
@SuppressWarnings("unchecked")
protected void validate(T entity) {

if (entity != null) {
ClassValidator validator = new ClassValidator(entity.getClass());
// get the invalid values
InvalidValue[] invalidValues = validator.getInvalidValues(entity);
if (invalidValues.length > 0) {
List<String> messages = new ArrayList<String>();
for (InvalidValue value : invalidValues) {
String message = value.getPropertyName() + ": " + value.getMessage();
// add an error message for each invalid value, these
// messages will be shown to the user
messages.add(message);
}

throw new ValidationException(messages);
}
}
}
</code>

Posted by Stephan on June 24, 2009 at 11:49 AM CEST #

Hi Adam,

You are so resourceful to say the least.
You design in this blog is almost exactly what I have used in many project (about 7 of them now) except with the finder methods. I go with something like

List<BaseEntity> findByCriteria(BaseEntityCriteria b)

where all entities extends BaseEntity and all Criteria object extends BaseEntityCriteria.
the BaseEntityCriteria object is a object that describes the entity is such a way that a consumer can load its getters and the service would generate a query string out of the loaded parameters. I built the library to take care of multiple joins and various JPQL operators and It works just fine.

Thanks

Posted by Michael Enudi Chuks on June 24, 2009 at 02:36 PM CEST #

@Michael,

thanks! - yes it just works. In JPA 2.0 we will get a typesafe criteria...

regards,

adam

Posted by Adam Bien on June 24, 2009 at 06:46 PM CEST #

Why do you call persist, flush and refresh at every create?

Posted by Kristof Jozsa on June 24, 2009 at 09:41 PM CEST #

@Kristof,

it's paranoia-driven :-). It could happen, that the generated PK (from e.g. DB instance) will be not updated in the entity. With persist, flush and refresh it is more likely to receive the actual @Id...

thanks for your comment!,

adam

Posted by Adam Bien on June 24, 2009 at 10:15 PM CEST #

Adam, I haven't seen this behaviour yet - it might depend on the specific JPA implementation used. Anyway, we're better off with tech-driven reasons ;) and this approach might introduce heavy performance problems based on the use cases (but I'm sure this aint sound like news to you either ;))

cheers,
K

ps. can you please check if your email notification system is working correctly?

Posted by Kristof on June 26, 2009 at 10:22 AM CEST #

Hi Adam,

First of all, thanks for share your solution.

I think some parts of your code are missing, because of the unescaped Java code, like in the declaration public <T>T create(T t); (the <T> is missing in the code of your post).

Regards,
Davi.

Posted by Davi on June 29, 2009 at 09:48 PM CEST #

Hi Davi,

thanks for your hint. I had another problem - in the method delete - but fixed that. The code is actually running (at least the JUnit). Check out - http://kenai.com/projects/javaee-patterns,

thanks for the nice feedback!,

adam

Posted by Adam Bien on June 29, 2009 at 10:27 PM CEST #

Cool stuff! Thx.

Posted by launsebay on August 01, 2009 at 04:54 PM CEST #

Hi Adam,

thank you for your post. A good technique, but why don't you use generics when the method returns a list, e.g. like this:

public <T> List<T> findWithNamedQuery(Class<T> type, String queryName);

Regards,
Peter

Posted by Peter on August 03, 2009 at 03:34 AM CEST #

@Peter,

yes and no. You will need additional parameter, but can save the cast later. So it would not be a huge benefit - but thanks for the idea.

thanks!,

adam

Posted by Adam Bien on August 03, 2009 at 12:17 PM CEST #

@launsebay,

if you like it, see: http://kenai.com/projects/javaee-patterns/

thanks!,

adam

Posted by Adam Bien on August 03, 2009 at 12:18 PM CEST #

Hi Adam,

I adopted the generic crud service in my application. Why do you use the @TransactionAttribute(TransactionAttributeType.MANDATORY)? Because my JSF-managed bean calls the crud layer directly. Do you think I should include facade between them?

regards,
launsebay

Posted by launsebay on August 03, 2009 at 07:05 PM CEST #

@Launsebay,

if your application will remain simple - just make it a Facade / Boundary. If you notice any duplication, just introduce a ServiceFacade, then make it MANDATORY again... I don't like empty facades...

regards,

adam

Posted by Adam Bien on August 03, 2009 at 10:14 PM CEST #

Hi Adam,

I ran your example using the latest version OpenEJB with the embedded OpenJPA DB.

While create (em.persist) seems to work, I experienced that the update (em.merge) does not work since the 'newName' is not found with findWithNamedQuery.

The Transaction Type is set to EXTENDED.

Any ideas?

Greetings, Detlef

Posted by Detlef Folger on September 03, 2009 at 01:06 AM CEST #

This is great, i implement the same idea in my projects.
I save a lot of code with this kind of generic CRUD service bean.
"One bean to rule them all"

Posted by jrico on November 05, 2009 at 04:24 PM CET #

Sorry about being late, just stumbled upon your blog days ago.

This approach is also described here:

http://www.ibm.com/developerworks/java/library/j-genericdao.html

But this approach goes a bit deeper, with type safety ...

Posted by Dirk on February 19, 2010 at 12:12 PM CET #

We actually used your implementation and it is excellent. Over the time, we had to make two amendments. One was to reflect getReference, and the other was just a convention based approach

/**
* @param type
* @param id
* @return
*/
@SuppressWarnings("unchecked")
@Override
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
public T getReference(final Class type, final Object id) {
return (T) entityManager.getReference(type, id);
}

/**
* This method can be used for eager loading, but with a
* thrown npe exception if null. In fact, this method
* makes sure you can avoid a null check. If you are doing
* a null check, you do not need to call this. use find
* instead.
*
* A common approach to using this method is when you are sure a given ID is in the database when looking it up.
* While this maybe tagged defensive, this approach actually reveals a possible null at the earliest.
* Noticing a null, which is an attribute of all classes and has no type, at the earliest is best.
*
* <p/>
* Use find if this check is not required.
* <p/>
* Use getReference if you need to load lazy with this behavior.
*
* @param typeOfEntity
* @param idByWhichToLookup
* @return
*/
@SuppressWarnings("unchecked")
@Override
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
public T findBadly(Class typeOfEntity, Object idByWhichToLookup) {
final Object object = entityManager.find(typeOfEntity, idByWhichToLookup);
if (object != null) {
return (T) object;
} else {
throw ENTITY_NOT_FOUND_EXCEPTION;
}
}

Once again, thank you for this innovative class.

Posted by Ravindranath Akila on March 25, 2010 at 09:35 AM CET #

Hi Adam,

Thanks for this nice blog.

What about the injection of Generic EJB's?

I created Generic Beans
@Stateless...
public class MyGenericBean<T extends SomeBaseEntityClass, ID extends IdClassForEntity> implements MyGenericBeanLocal<T,ID> {
.....
}

my Injection :

@EJB(name="...")
MyGenericBeanLocal<AnEntity,IdForEntity> anEntity;

with this injection I don't have to create a concrete Class for each entity, I just do an injection.

this seams to be working on Glassfishv2.1

Is this safe?

Kind regards,

Joeri

Posted by Joeri on June 23, 2010 at 06:50 PM CEST #

Hi Adam,
I wonder about general DAO usage as it is said to be a complete data-storage layer abstraction. If so it should abstract both operations and exceptions. To handle persistence exceptions and map them to application-specific exceptions I created an interceptor on CrudService. But it doesn't catch exceptions without calling flush on persist, update and delete methods. Is it ok to use flush like that? It is quite heavy and can be a real performance pain on e.g. series of updates.

Posted by Mike on March 21, 2011 at 11:00 PM CET #

Hi Adam!

Could you advise what should be the way of using this generic CRUD service from the actual service?

I assume that from the concrete service facade you should inject this generic CRUD (@EJB or @Inject) and do the delegation, right?

So, let's say I have 20 services, each should implement basic CRUD methods. In many cases those will be a plain delegation to the CrudServiceBean (in every service for every CRUD method...).

Can the EJB's inheritance be used to inherit the implementation of CRUD methods to avoid this code duplication?

Thanks in advance!

Posted by PedroKowalski on June 18, 2011 at 03:57 PM CEST #

Hello Adam, I'm from Brazil and your post helped me a lot. Thank you.

Posted by Rafael Rossi on February 23, 2013 at 05:14 AM CET #

What about Spring Data JPA? It has support for CDI (http://static.springsource.org/spring-data/data-jpa/docs/current/reference/html/jpa.repositories.html#jpd.misc.cdi-integration) and does pretty much the same thing + automatic paging, sorting, query generation based on method names, transparent usage of named queries, etc. Also the DAO interface is standardized across different databases (SQL, NoSQL, graph...)

Posted by Tomek N. on March 08, 2013 at 01:00 AM CET #

Thank you so much Adam it was what i needed best explanation...

Posted by hewan on January 15, 2015 at 02:55 PM CET #

Post a Comment:
  • HTML Syntax: NOT allowed
...the last 150 posts
...the last 10 comments
License