CDI Generic Dao

UPDATE(November/2014):

Just added multiple datasources support to our example application, see here details: https://rpestano.wordpress.com/2014/11/04/cdi-crud-multi-tenancy/

<END UPDATE>

UPDATE(September/2014):

I’ve revisited this post some days ago and refactored the code, of course i keeped everything here and the github repo untouched so you can folow this post. Instead i’ve created a new github repository to share the new code there, i have updated the apis, added some (arquillian)tests, new functionality like true pagination and new apis(hibernate and deltaspike) and now it works on wildfly, Glassfish and Jboss7. I’ve also changed a bit of the project structure but the idea is the same, the code can be found here: cdi-crud.

<END UPDATE>

In this post i will show how to implement basic CRUD operations using a generic dao based on CDI beans. Its not the purpose to discuss about the Dao pattern itself, there are already long discussions about it see[1],[2],[3] and [4]. For a good and detailed introduction about the pattern see [5].

The source code can be found here: https://github.com/rmpestano/cdi-generic-dao and also as usual there is a video produced by this post here: http://www.youtube.com/watch?v=9mGQx0tjxgo&feature=youtu.be

Show me the code

lets get hands dirty with some code, here is the classic BaseEntity which in this case will hold only the primary key of our JPA entities:

@MappedSuperclass
public abstract class BaseEntity<ID> {

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

    public ID getId() {
        return id;
    }

    public void setId(ID id) {
        this.id = id;
    }

}

Next is the simple Car entity:

@Entity
public class Car extends BaseEntity<Integer>{

    private String model;
    private Double price;

 //getter & setters

Now the so called generic dao:

@Stateless
@Named("baseDao")
public class<T extends BaseEntity<ID>, ID>> implements Serializable {

    @PersistenceContext
    private EntityManager entityManager;
    private Class entityClass;

    public EntityManager getEntityManager() {
        return entityManager;
    }

    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    public Class getEntityClass() {
        if (entityClass == null) {
            //only works if one extends BaseDao, we will take care of it with CDI
            entityClass = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        }
        return entityClass;
    }

    public void setEntityClass(Class entityClass) {
        this.entityClass = entityClass;
    }

    //utility database methods
    @TransactionAttribute(TransactionAttributeType.SUPPORTS)
    public T find(ID id) {
        return (T) this.entityManager.find(getEntityClass(), id);
    }

    public void delete(ID id) {
        Object ref = this.entityManager.getReference(getEntityClass(), id);
        this.entityManager.remove(ref);
    }

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

    public void insert(T t) {
        this.entityManager.persist(t);
    }

    @TransactionAttribute(TransactionAttributeType.SUPPORTS)
    public List findAll() {
        return entityManager.createQuery("Select entity FROM "+getEntityClass().getSimpleName() +" entity").getResultList();
    }
//more utility methods

It is an usual dao, it has a PersistenceContext injected by the EJB container, it’s a Stateless Session Bean, have some utility database methods but it cannot work alone, you need to feed it with an Entity so it can perform database operations.

There are several ways to provide an entity to a generic dao, via constructor, annotation, extending it etc. In this example we provide the Entity by two manners, extending it and via CDI producer.

Extending the Dao

Here is a generic dao specialization, the CarDao:

@Stateless
public class CarDao extends BaseDao<Car, Integer>{
    //put here specific car business
}

The entity is passed to BaseDao in getEntityClass() via ParameterizedType and now we can inject our dao in any bean:


public class SomeBean{
    @Inject
    CarDao carDao;

    public void createCar(Car car){
      carDao.insert(car);
    }
}

this is good cause you usually will extend the base dao to add some more complex operations then CRUD but sometimes you just need the CRUD and you will have to create empty daos extending the BaseDao just to benefit from its utility methods, what i really want is to inject the BaseDao directly:

   @Inject
   BaseDao<Car,Integer> baseCarDao;

The only thing that prohibits me from doing that is that the ParameterizedType can only be got from a superclass with getGenericSuperclass()(the only way I know 😉 ), at least before CDI came.

Producing the Dao

We are going to set the entity in a CDI producer, to do that we provide a qualifier that will tell CDI we want a produced Dao not the real one, here is the Dao qualifier:

@Qualifier
@Retention(RUNTIME)
@Target({FIELD,PARAMETER,METHOD,TYPE})
public @interface Dao {
    }

And here is our producer responsible of creating a baseDao with a setted Entity based on the InjectionPoint ParameterizedType:

public class DaoProducer implements Serializable {

    private static final long serialVersionUID = 1L;

    @Produces
    @Dependent//must be dependent pseudoScope cause baseDao is a SLB
    @Dao
    public <ID, T extends BaseEntity> BaseDao<T, ID> produce(InjectionPoint ip, BeanManager bm) {
        if (ip.getAnnotated().isAnnotationPresent(Dao.class)) {
            BaseDao<T, ID> genericDao = (BaseDao<T, ID>)  this.getBeanByName("baseDao", bm);//ask bean manager for a instance of GenericDao
            ParameterizedType type = (ParameterizedType) ip.getType();
            Type[] typeArgs = type.getActualTypeArguments();
            Class<T> entityClass = (Class<T>) typeArgs[0];
            genericDao.setEntityClass(entityClass);
            return genericDao;
        }
        throw new IllegalArgumentException("Annotation @Dao is required when injecting BaseDao");
    }

    public Object getBeanByName(String name, BeanManager bm) { // eg. name=availableCountryDao{
        Bean bean = bm.getBeans(name).iterator().next();
        CreationalContext ctx = bm.createCreationalContext(bean); // could be inlined below
        Object o = bm.getReference(bean, bean.getBeanClass(), ctx); // could be inlined with return
        return o;
    }

}

The ‘secret’ here is that we are infering the Dao’s entity in InjectionPoint so when we Inject BaseDao with:

  @Inject @Dao BaseDao<User,Long> baseUserDao;

the (Class<T>) typeArgs[0]; in line 13 will return User.class and we set it in the Dao. Below is a concrete bean using our Dao to perform crud operations:

@Named
@ViewAccessScoped
public class CarBean implements Serializable{

    private List carList;
    private List filteredValue;//datatable filteredValue attribute
    private Integer id;
    private Car car;

    @Inject CarDao carDao;

    @Inject @Dao
    BaseDao<Car,Integer> genericDao;//reuse generic dao for basic crud operation in various entities
//    @Inject @Dao
//    BaseDao<Person,Long> genericDao;
//    @Inject @Dao
//    BaseDao<Client,IDClass> genericDao;

    @PostConstruct
    public void init(){
        if(genericDao.findAll().isEmpty()){
            for (int i = 1; i < 10; i++) {
                Car c = new Car("Car"+i, i);
                genericDao.insert(c);
            }
        }
        //same as above
//         if(carDao.findAll().isEmpty()){
//            for (int i = 0; i < 10; i++) {
//                Car c = new Car("Car"+i, i);
//                carDao.insert(c);
//            }
//        }

    }

    public List getCarList(){
        if(carList == null){
            carList = carDao.findAll();
        }
        return carList;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Car getCar() {
        if(car == null){
            car = new Car();
        }
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }

    public void findCarById(Integer id){
         car = genericDao.find(id);
    }

    public List getFilteredValue() {
        return filteredValue;
    }

    public void setFilteredValue(List filteredValue) {
        this.filteredValue = filteredValue;
    }

    public void remove(){
        if(car != null && car.getId() != null){
            genericDao.delete(car.getId());
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Car "+car.getModel() +" removed successfully"));
            clear();
        }
    }

    public void update(){
        String msg;
        if(car.getId() == null){
             genericDao.insert(car);
             msg = "Car "+car.getModel() +" created successfully";
        }
        else{
           genericDao.update(car);
           msg = "Car "+car.getModel() +" updated successfully";
        }
        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(msg));
        clear();//reload car list
    }

    public void clear(){
        car = new Car();
        carList = null;
        id = null;
    }

    public void onRowSelect(SelectEvent event) {
        setId(((Car) event.getObject()).getId());
        findCarById(getId());
    }

    public void onRowUnselect(UnselectEvent event) {
        car = new Car();
    }
}

Addendum

Just a small addendum(which is not that small) there was an issue with the above implementation which showed up when using BaseDao to crud more than one entity in the same bean. The bug was caused because i was storing EntityClass in a stateless session bean(BaseDao), although CDI produces different instances for each injection point the container was returning the same session bean(from the pool of BaseDaos) for each produced bean. The solution was to remove EntityClass from BaseDao and put it in a CDI Bean called CrudDao which has a composion association with BaseDao making it really stateless(as it should be).Here is the CrudDao:

@Named("crudDao")
public class CrudDao<T extends BaseEntity<ID>, ID> implements Serializable{

    @Inject
    protected BaseDao<T,ID> dao;

    protected Class<T> entityClass;

   public Class<T> getEntityClass() {
        if (entityClass == null) {
            //only works if one extends CrudDao, we will take care of it with CDI
            entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        }
        return entityClass;
    }

    public void setEntityClass(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    public EntityManager getEntityManager(){
        return dao.getEntityManager();
    }

    public T find(ID id){
        return dao.find(id, getEntityClass());
    }

    public void delete(ID id){
         dao.delete(id, getEntityClass());
    }

    public T update(T t){
        return dao.update(t);
    }

    public void insert(T t){
        dao.insert(t);
    }

    public List<T> findAll(){
        return dao.findAll(getEntityClass());
    }

    public List<T> findWithNamedQuery(String namedQueryName){
        return dao.findWithNamedQuery(namedQueryName);
    }
}

References:

[1]http://www.adam-bien.com/roller/abien/entry/generic_crud_service_aka_dao

[2http://www.infoq.com/news/2007/09/jpa-dao

[3]http://www.adam-bien.com/roller/abien/entry/jpa_ejb3_killed_the_dao

[4]http://www.rponte.com.br/2009/06/08/no-more-daos/ [PT_BR]

[5]]http://tutorials.jenkov.com/java-persistence/dao-design-pattern.html

Advertisements

26 thoughts on “CDI Generic Dao

  1. Hi sugam,

    do you have any example? as a newbie in spring i google it and found this post: http://nurkiewicz.blogspot.com.br/2013/01/spring-data-jdbc-generic-dao.html

    looking at it i see a UserRepository which is extending JdbcRepository

    can you for example Inject JdbcRepository directly with Spring?

    something like @Autowire JdbcRepository<User,Long>genericRepository?

    Also note that everything i posted here is for free 😉

    thanks for your comment!

    Like

  2. Hi Rafeal,
    You don’t inject the repository directly you extend the interface and inject that new interface. This allows the Spring Data infrastructure to scan the classpath and create a spring bean for it. You don’t physically implement the interface yourself so injecting an interface that extends the repository should not make any difference to you. By extending the interface you can add attribute finder methods that Spring Data will then implement for you. (This was what I meant by for free – that Spring Data provides the implementations for you without you having to write any implementation code) 🙂

    Like

  3. I see(im not so newbie with Spring, already used Spring data ;)), that’s what i’m doing with CarDao, i just extend BaseDao and add no implementation code. what i really focus on this post is to avoid ’empty daos'(or empty Repositories in your case) and i can do that Injecting the generic implementation directly.

    Also note that empty daos are not common you usually will add some extra functionality, even when cruding.

    Thanks again!

    Like

  4. During deploy on Glassfish 4 I get following exception:

    2014-05-16T14:21:55.295+0200|Info: Computed the following CODI ProjectStage: Production
    2014-05-16T14:21:55.700+0200|Severe: Exception during lifecycle processing
    org.glassfish.deployment.common.DeploymentException: CDI deployment failure:WELD-001408 Unsatisfied dependencies for type [Validator] with qualifiers [@Default] at injection point [[UnbackedAnnotatedField] @Inject private org.hibernate.validator.internal.cdi.interceptor.ValidationInterceptor.validator]
    at org.glassfish.weld.WeldDeployer.event(WeldDeployer.java:225)
    at org.glassfish.kernel.event.EventsImpl.send(EventsImpl.java:131)
    at org.glassfish.internal.data.ApplicationInfo.load(ApplicationInfo.java:328)
    at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:493)
    at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:219)
    at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:491)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:527)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl$2$1.run(CommandRunnerImpl.java:523)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.Subject.doAs(Subject.java:356)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl$2.execute(CommandRunnerImpl.java:522)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:546)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl.doCommand(CommandRunnerImpl.java:1423)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl.access$1500(CommandRunnerImpl.java:108)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1762)
    at com.sun.enterprise.v3.admin.CommandRunnerImpl$ExecutionContext.execute(CommandRunnerImpl.java:1674)
    at com.sun.enterprise.v3.admin.AdminAdapter.doCommand(AdminAdapter.java:534)
    at com.sun.enterprise.v3.admin.AdminAdapter.onMissingResource(AdminAdapter.java:224)
    at org.glassfish.grizzly.http.server.StaticHttpHandler.service(StaticHttpHandler.java:297)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:246)
    at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191)
    at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168)
    at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189)
    at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
    at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
    at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
    at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
    at java.lang.Thread.run(Thread.java:722)
    Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [Validator] with qualifiers [@Default] at injection point [[UnbackedAnnotatedField] @Inject private org.hibernate.validator.internal.cdi.interceptor.ValidationInterceptor.validator]
    at org.jboss.weld.bootstrap.Validator.validateInjectionPointForDeploymentProblems(Validator.java:403)
    at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:325)
    at org.jboss.weld.bootstrap.Validator.validateInterceptor(Validator.java:554)
    at org.jboss.weld.bootstrap.Validator.validateInterceptors(Validator.java:530)
    at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:479)
    at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:536)
    at org.glassfish.weld.WeldDeployer.event(WeldDeployer.java:216)
    … 36 more

    Like

  5. Pingback: CDI Crud Multi Tenancy | Rafael Pestano
  6. Hi Rafael,

    I have tried your example with CarDao extending the BaseDao, it works like a charm.
    However, from the CarDao class, my NetBeans underlined the class name “CarDao” with the
    error message “A session bean must not extend another session bean.” But I can compile,
    deploy and run the application without any problem.
    I have also heard that a session bean cannot extend another session bean, but why it works here?

    I am using NetBeans 8.0.1 and WebLogic 12c for this code testing.

    Thanks

    Liked by 1 person

  7. Pingback: Some Words on JavaEE, REST and Swagger | Rafael Pestano
  8. Hi, good explanation, i have tried this as you mentioned and getting below error. Any help would be appreciated.

    Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [GenericBaseDao] with qualifiers [@JpaDao] at injection point [[field] @JpaDao @Inject private rest.service.EntitlementsServiceEJB.userDao]

    Like

  9. Hi Rafael! I get this exception while building:

    — jaxrs-analyzer-maven-plugin:0.15:analyze-jaxrs (default) @ cdi-crud —
    ago 14, 2017 5:20:21 PM org.sonatype.guice.bean.reflect.Logs$JULSink warn
    ADVERTENCIA: Error injecting: com.sebastian_daschner.jaxrs_analyzer.maven.JAXRSAnalyzerMojo
    com.google.inject.ProvisionException: Guice provision errors:

    1) No implementation for org.eclipse.aether.RepositorySystem was bound.
    while locating com.sebastian_daschner.jaxrs_analyzer.maven.JAXRSAnalyzerMojo

    1 error
    at com.google.inject.internal.InjectorImpl$3.get(InjectorImpl.java:974)
    at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1000)
    at org.sonatype.guice.bean.reflect.AbstractDeferredClass.get(AbstractDeferredClass.java:45)
    at com.google.inject.internal.ProviderInternalFactory.provision(ProviderInternalFactory.java:84)
    at com.google.inject.internal.InternalFactoryToInitializableAdapter.provision(InternalFactoryToInitializableAdapter.java:52)
    at com.google.inject.internal.ProviderInternalFactory$1.call(ProviderInternalFactory.java:70)
    at com.google.inject.internal.ProvisionListenerStackCallback$Provision.provision(ProvisionListenerStackCallback.java:100)
    at org.sonatype.guice.plexus.lifecycles.PlexusLifecycleManager.onProvision(PlexusLifecycleManager.java:138)
    at com.google.inject.internal.ProvisionListenerStackCallback$Provision.provision(ProvisionListenerStackCallback.java:108)
    at com.google.inject.internal.ProvisionListenerStackCallback.provision(ProvisionListenerStackCallback.java:55)
    at com.google.inject.internal.ProviderInternalFactory.circularGet(ProviderInternalFactory.java:68)
    at com.google.inject.internal.InternalFactoryToInitializableAdapter.get(InternalFactoryToInitializableAdapter.java:45)
    at com.google.inject.internal.InjectorImpl$3$1.call(InjectorImpl.java:965)
    at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1011)
    at com.google.inject.internal.InjectorImpl$3.get(InjectorImpl.java:961)
    at com.google.inject.Scopes$1$1.get(Scopes.java:59)
    at org.sonatype.guice.bean.locators.LazyBeanEntry.getValue(LazyBeanEntry.java:83)
    at org.sonatype.guice.plexus.locators.LazyPlexusBean.getValue(LazyPlexusBean.java:49)
    at org.codehaus.plexus.DefaultPlexusContainer.lookup(DefaultPlexusContainer.java:253)
    at org.codehaus.plexus.DefaultPlexusContainer.lookup(DefaultPlexusContainer.java:245)
    at org.apache.maven.plugin.internal.DefaultMavenPluginManager.getConfiguredMojo(DefaultMavenPluginManager.java:455)
    at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:92)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:209)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
    at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:84)
    at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:59)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.singleThreadedBuild(LifecycleStarter.java:183)
    at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:161)
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:320)
    at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:156)
    at org.apache.maven.cli.MavenCli.execute(MavenCli.java:537)
    at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:196)
    at org.apache.maven.cli.MavenCli.main(MavenCli.java:141)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:290)
    at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:230)
    at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:409)
    at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:352)

    Any idea how can I fix this? Thanks in advance, and you did a very nice work here!

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s