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?
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.