1

I'm writing a Spring MVC 3.2.3 and Hibernate 4.2.1 application, just starting it's architecture. I decided to go with the Generic Dao pattern, since I'll be using a lot of common CRUD operations. I know that JPA has been around for a while, but I'd really love to achieve this using the generic dao pattern. To the issue it self. Everything went ok when setting up things and testing WITH ONE ENTITY. But as soon as I added a second one, I began to see errors:

"No qualifying bean of type [com.segurosweb.daos.GenericDao] is defined: expected single matching bean but found 2: cobradorDaoImpl,productorDaoImpl".

I understand that this is because Spring can not tell at startup time, which component to inject, but I dont know how to solve this.

The Generic DAO implementation that I used is:

GenericDao.java

package com.segurosweb.daos;

import java.util.List;

public interface GenericDao<T> {

    public void saveOrUpdate(T dom);
    public List<T> getAll();
    public T get(long id);

}

GenericDaoImpl.java

package com.segurosweb.daos;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@SuppressWarnings("unchecked")
@Repository
public abstract class GenericDaoImpl<T> implements GenericDao<T>{

    private Class<T> type;

    @Autowired
    private SessionFactory sessionFactory;

    @SuppressWarnings("rawtypes")
    public GenericDaoImpl(){
        Type t = getClass().getGenericSuperclass();
        ParameterizedType pt = (ParameterizedType) t;
        type = (Class) pt.getActualTypeArguments()[0];
    }


    public Session getCurrentSession() {
        return sessionFactory.getCurrentSession();
    }
... all other implementation...
}

CobradorDao.java

package com.segurosweb.daos;

import com.segurosweb.entities.Cobrador;

public interface CobradorDao extends GenericDao<Cobrador>{

}

CobradorDaoImpl.java

package com.segurosweb.daos;

import org.springframework.stereotype.Repository;

import com.segurosweb.entities.Cobrador;

@Repository
public class CobradorDaoImpl extends GenericDaoImpl<Cobrador> implements CobradorDao{

}

GenericService.java

package com.segurosweb.service;

import java.util.List;

public interface GenericService<T> {

    public void saveOrUpdate(T dom);
    public List<T> getAll();
    public T get(long id);

}

GenericServiceImpl.java

package com.segurosweb.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;

import com.segurosweb.daos.GenericDao;

public class GenericServiceImpl<T> implements GenericService<T>{

    @Autowired
    private GenericDao<T> tDao;

    @Override
    public void saveOrUpdate(T dom) {
        tDao.saveOrUpdate(dom);     
    }

    @Override
    public List<T> getAll() {
        return tDao.getAll();
    }

    @Override
    public T get(long id) {
        return tDao.get(id);
    }

}

CobradorService.java

package com.segurosweb.service;

import com.segurosweb.entities.Cobrador;

public interface CobradorService extends GenericService<Cobrador>{

}

CobradorServiceImpl.java

package com.segurosweb.service;

import org.springframework.stereotype.Service;

import com.segurosweb.entities.Cobrador;

@Service
public class CobradorServiceImpl extends GenericServiceImpl<Cobrador> implements CobradorService{

}

And my Controller, which has a very simple url mapping to test that everythin works (or doesn't!)

package com.segurosweb.controllers;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import com.segurosweb.entities.Cobrador;
import com.segurosweb.service.CobradorService;

@Controller
@RequestMapping("/cobradores/**")
public class CobradorController {

    static final Logger log = LogManager.getLogger(CobradorController.class.getSimpleName());

    @Autowired
    private CobradorService cobServ;

    @RequestMapping(value="view.html",method = RequestMethod.GET)
    public ModelAndView setUpForm(ModelMap model){  

        log.info("cobradores/view.html hitted.");
        cobServ.saveOrUpdate(new Cobrador("emi","lio","hola","321"));       
        return new ModelAndView("/secure/Productores");

    }

}

Cobrador.java is a very simple POJO class annotated with @Entity.

And I also have matching interfaces and implementations for another entity, called Productor: ProductorDao, ProductorDaoImpl, ProductorService and ProductorServiceImpl.

The exact error Im getting is:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cobradorController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.segurosweb.service.CobradorService com.segurosweb.controllers.CobradorController.cobServ; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cobradorServiceImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.segurosweb.daos.GenericDao com.segurosweb.service.GenericServiceImpl.tDao; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.segurosweb.daos.GenericDao] is defined: expected single matching bean but found 2: cobradorDaoImpl,productorDaoImpl

So, am I missing something important here? I'd like to stick to this Generic DAO pattern for now, unless you guys tell me that is veeeeeeeeeeeeeeeeeery bad idea.

Thanks in advance for any help you guys can give me!

4

3 回答 3

3

For starters I would drop the Generic DAO and switch to Spring Data JPA, don't try to reinvent the wheel, and it saves yuo from writing another kind of in-house framework.

Regarding Generic Services, or better service methods which one-on-one call the corresponding methods on your dao in general this is, imho, a design smell. Your service should provide business cases/use-cases to solve. A method like saveOrUpdate is not really something that has business meaning. In those cases I would probably directly call the repository (adding another layer just for the sake of the layer isn't wise imho).

There is a question/blog out there which basically comes to the same conclusion (I just cannot find it anymore, was a comment by Oliver Gierke, the maintainer of Spring Data JPA.

The problem is that Spring versions before 4.0 have problems with dependency injection and generic interfaces, this has been solved in Spring 4.0

Links:

于 2013-11-05T07:42:31.783 回答
2

In Spring 3x version it's a problem to identify the appropriate bean even you have used generic type. Spring 4 has been solved this bug. so according to me you have to use @Qualifier annotation for tell the spring container that which bean you want to inject which have same parent type.

@Repository("cobradorDao")

@Repository("productorDao")

@Autowire
@Qualifier("cobradorDao")
CobradorDao cobradorDao;

@Autowire
@Qualifier("productorDao")
ProductorDao productorDao;

I hope it's help full for you.

于 2014-05-07T11:33:15.960 回答
0

You should be fixed by this Code.

GenericServiceImpl.java

package com.segurosweb.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;

import com.segurosweb.daos.GenericDao;

public class GenericServiceImpl<T> implements GenericService<T>{


    private GenericDao<T> tDao;

    public GenericServiceImpl(GenericDao<T> tDao) {
        this.tDao= tDao;
    }

    @Override
    public void saveOrUpdate(T dom) {
        tDao.saveOrUpdate(dom);     
    }

    @Override
    public List<T> getAll() {
        return tDao.getAll();
    }

    @Override
    public T get(long id) {
        return tDao.get(id);
    }

}

CobradorServiceImpl.java

package com.segurosweb.service;

import org.springframework.stereotype.Service;

import com.segurosweb.entities.Cobrador;

@Service
public class CobradorServiceImpl extends GenericServiceImpl<Cobrador> implements CobradorService{

    @Autowired
    public CobradorServiceImpl(CobradorDao tDao) {
      super(tDao);
    }

}
于 2015-04-15T10:05:49.370 回答