Spring MVC: Generic DAO and Service classes

15,191

Solution 1

The problem is that your inject directly your GenericDao in your GenericManager but none of them is a concrete Spring bean and you will never be able to use your specific CountryDao.

You must not autowire GenericDao but only define it and provide setter :

// Add DAO as a genric parameter
public abstract class GenericManagerImpl<T, D extends GenericDao<T>> implements GenericManager<T> {
    private D dao;

    protected void setDao (D dao) {
        this.dao = dao;
    }

...

}

Then, you will have to inject a concrete spring bean in your concrete services. i.e. in CountryManagerImpl:

// Instantiate your concrete service with your concrete DAO
public class CountryManagerImpl extends GenericManagerImpl<Country, CountryDao> implements CountryManager {

    // Do not redeclare your dao here in order to keep the inherited one

    // Don't forget to inject
    @Inject("countryDao")
    @Override
    protected void setDao (CountryDao dao) {
        this.dao = dao;
    }

...

}

You will have then a full spring bean injected with your concrete CountryDao type and its specific methods.

You can take a look at what we did on RESThub project regarding generic services : https://github.com/resthub/resthub-spring-stack/blob/master/resthub-common/src/main/java/org/resthub/common/service/CrudServiceImpl.java and some concrete example : https://github.com/resthub/todo-backbone-example/blob/master/src/main/java/todo/TodoController.java (with a Controller instead of a Service but it is similar)

Hope it will help.

(and sorry if there is some typos, I cannot double check right now)

and, BTW, you should consider using Spring Data instead of using GenericDaos but you will still have the same needs regarding your Services.

Solution 2

i still dont know why people actually use archaic DAO / service - models with Spring Data; totally unnecessary, error-prone and whatnot.

Spring Data JPA has some extremely useful interfaces for that stuff : JpaRepository and JpaSpecificationExecutor - these encapsulate EVERYTHING you would want, you only need your standard Entities and thats it - everything else will be handled by spring, you simply input your criterias and get exactly what you want, without reinventing the wheel. Could it be that you didnt actually read the documentation? Its very useful :

official introduction : http://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/

doc : http://docs.spring.io/spring-data/jpa/docs/1.7.0.RELEASE/reference/html/

small howto : http://www.cubrid.org/wiki_ngrinder/entry/how-to-create-dynamic-queries-in-springdata

examples from the genius himself : https://github.com/spring-projects/spring-data-jpa-examples/tree/master/spring-data-jpa-example

example classes :

public CustomerSpecifications {

  public static Specification<Customer> customerHasBirthday() {
    return new Specification<Customer> {
      public Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb) {
        return cb.equal(root.get(Customer_.birthday), today);
      }
    };
  }

  public static Specification<Customer> isLongTermCustomer() {
    return new Specification<Customer> {
      public Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb) {
        return cb.lessThan(root.get(Customer_.createdAt), new LocalDate.minusYears(2));
      }
    };
  }
}

public interface CustomerRepository extends JpaRepository<Customer>, JpaSpecificationExecutor {
  // Your query methods here
}

and now you can simply Autowire your repository :

@Autowired
CustomerRepository customerRepo;

and retrieve data like this :

List<Customer> customersWithBirthDay = customerRepo.findAll(CustomerSpecifications.customerHasBirthDay());

its that easy.

Share:
15,191
martin
Author by

martin

Updated on June 04, 2022

Comments

  • martin
    martin almost 2 years

    I am writting web in Spring MVC. I wrote all DAOs using Generic DAO. Now I would like to rewrite my Service classes. How can I write "Generic Service"?

    There are my DAOs:

    /* ################################# DAO ################################ */
    package net.example.com.dao;
    
    import java.util.List;
    
    public interface GenericDao<T> {       
            public T findById(int id);     
            public List<T> findAll();      
            public void update(T entity);  
            public void save(T entity);    
            public void delete(T entity);
    }
    
    /* ------------------------------------------------------ */
    
    package net.example.com.dao;
    
    import java.io.Serializable;
    import java.util.List;
    
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Scope;
    
    @Scope("prototype")
    public abstract class GenericHibernateDaoImpl<T extends Serializable> implements GenericDao<T> {
    
            private Class<T> clazz;
    
            @Autowired
            private SessionFactory sessionFactory;
    
            public final void setClazz(Class<T> clazzToSet) {
                    this.clazz = clazzToSet;               
            }
    
            @SuppressWarnings("unchecked")
            public T findById(int id) {
                    return (T) getCurrentSession().get(clazz, id);
            }
    
            @SuppressWarnings("unchecked")
            public List<T> findAll() {
                    return getCurrentSession().createQuery("FROM " + clazz.getName()).list();              
            }
    
            public void update(T entity) {
                    getCurrentSession().update(entity);            
            }
    
            public void save(T entity) {
                    getCurrentSession().save(entity);              
            }
    
            public void delete(T entity) {
                    getCurrentSession().delete(entity);            
            }
    
            protected final Session getCurrentSession(){
                    return sessionFactory.getCurrentSession();
            }
    }
    
    /* ------------------------------------------------------ */
    
    package net.example.com.dao;
    
    import net.example.com.entity.Country;
    
    public interface CountryDao extends GenericDao<Country> {
    
        public Country findByName(String name);    
        public Country findByCode(String code);
    
    }
    
    /* ------------------------------------------------------ */
    
    package net.example.com.dao;
    
    import org.springframework.stereotype.Repository;
    
    import net.example.com.entity.Country;
    
    @Repository
    public class CountryDaoImpl extends GenericHibernateDaoImpl<Country> implements CountryDao {
    
            @Override
            public Country findByName(String name) {
                    return (Country) getCurrentSession()
                                    .createQuery("FROM Country WHERE name = :name")
                                    .setString("name", name).uniqueResult();
            }
    
            @Override
            public Country findByCode(String code) {
                    return (Country) getCurrentSession()
                                    .createQuery("FROM Country WHERE code = :code")
                                    .setString("code", code).uniqueResult();
            }
    
    }
    
    /* ################################# DAO ################################ */
    

    and Services:

    /* ################################# SERVICE ################################ */
    
    package net.example.com.service;
    
    import java.util.List;
    
    public interface GenericManager<T> { // GenericManager<T> is the same as GenericDao<T>
    
            public T findById(int id);     
            public List<T> findAll();      
            public void update(T entity);  
            public void save(T entity);    
            public void delete(T entity);
    }
    
    /* ------------------------------------------------------ */
    
    package net.example.com.service;
    
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import net.example.com.dao.GenericDao;
    
    @Service
    public abstract class GenericManagerImpl<T> implements GenericManager<T> {
    
            @Autowired
            protected GenericDao<T> dao;
    
            @Override
            public T findById(int id) {
                    return dao.findById(id);
            }
    
            @Override
            public List<T> findAll() {
                    return dao.findAll();
            }
    
            @Override
            public void update(T entity) {
                    dao.update(entity);
            }
    
            @Override
            public void save(T entity) {
                    dao.save(entity);
            }
    
            @Override
            public void delete(T entity) {
                    dao.delete(entity);    
            }
    }
    /* ------------------------------------------------------ */
    
    package net.example.com.dao;
    
    import net.example.com.entity.Country;
    
    public interface CountryManager extends GenericDao<Country> { // CountryManager is the same as CountryDao
    
        public Country findByName(String name);    
        public Country findByCode(String code);
    }
    
    /* ------------------------------------------------------ */
    
    package net.example.com.service;
    
    import java.util.List;
    
    import javax.transaction.Transactional;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import net.example.com.dao.CountryDao;
    import net.example.com.entity.Country;
    
    @Service
    @Transactional
    public class CountryManagerImpl extends GenericManagerImpl<Country> implements CountryManager {
    
            @Override
            public List<Country> findAll() {
                    return dao.findAll();
            }
    
            public Country findById(int id) {
                    return dao.findById(id);
            }
    
            @Override
            public Country findByName(String name) {
                    return dao.findByName(name); // compiler (and Eclipse) do not see findByName !!!!!!!!!
            }
    
            @Override
            public Country findByCode(String code) {
                    return dao.findByCode(code); // compiler (and Eclipse) do not see findByCode !!!!!!!!!
            }
    
            @Override
            public void save(Country country) {
                    dao.save(country);
            }
    
            @Override
            public void delete(Country country) {
                    dao.delete(country);
            }
    
            @Override
            public void update(Country country) {
                    dao.update(country);
            }
    
    }
    
    /* ------------------------------------------------------ */
    
    /* ################################# SERVICE ################################ */
    

    Compiler (and Eclipse) do not see findByName and findByCode methods. I understand why. But how can I rewrite it?