Spring MVC:通用DAO和Service类

9

我正在使用Spring MVC编写Web应用程序。我使用通用DAO编写了所有DAO。现在我想重写我的Service类。如何编写“通用Service”?

这是我的DAO:

/* ################################# 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 ################################ */

服务和解决方案:

/* ################################# 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 ################################ */

编译器(以及Eclipse)无法识别findByNamefindByCode方法。我理解为什么会这样,但是我该如何重写它呢?
5个回答

2
问题在于您直接将通用Dao注入到通用Manager中,但它们都不是具体的Spring bean,因此您永远无法使用特定的CountryDao。
您不应该自动装配通用Dao,而应该只定义它并提供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;
    }

...

然后,您需要在具体的服务中注入一个具体的Spring Bean,例如在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;
    }

...

}

您将拥有一个完整的Spring Bean,注入您具体的CountryDao类型及其特定方法。
您可以查看RESThub项目中关于通用服务的实现:https://github.com/resthub/resthub-spring-stack/blob/master/resthub-common/src/main/java/org/resthub/common/service/CrudServiceImpl.java 以及一些具体示例:https://github.com/resthub/todo-backbone-example/blob/master/src/main/java/todo/TodoController.java(使用控制器而不是服务,但类似)。
希望这可以帮助您。
另外,顺便提一下,您应该考虑使用Spring Data而不是使用通用DAO,但是您仍然需要满足服务方面的需求。

好的,我可以看到多个错误(其中一些是我的错误):DAO类型应该作为通用类型传递给您的GenericManager(请参见我的答案中编辑的代码),不要忘记@Inject注释。我已更新我的答案。 - bmeurant
如果不确定是否遗漏了什么,你真的应该看一下我之前粘贴的两个链接... - bmeurant

1

我仍然不知道为什么人们会使用过时的DAO / service模型与Spring Data一起使用;这是完全不必要的,容易出错等等。

Spring Data JPA有一些非常有用的接口:JpaRepositoryJpaSpecificationExecutor - 它们封装了您想要的所有内容,您只需要标准实体即可 - 其他所有内容都将由Spring处理,您只需输入自己的条件,就可以得到所需的结果,无需重新发明轮子。

难道你没有阅读文档吗?它非常有用:

官方介绍:http://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/

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

小型教程: http://www.cubrid.org/wiki_ngrinder/entry/how-to-create-dynamic-queries-in-springdata

来自天才本人的示例: https://github.com/spring-projects/spring-data-jpa-examples/tree/master/spring-data-jpa-example

示例类:

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
}

现在您可以简单地使用Autowire自动装配您的存储库:
@Autowired
CustomerRepository customerRepo;

并且像这样检索数据:

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

这很容易。


你尝试过在表之间使用复杂的连接策略吗?我对那个API有非常糟糕的经历。 - John
你可能是地球上唯一一个认为 JPA Criteria API(因为基本上就是它;只是在其周围加了一些无用的包装器)不好用的人。它经过了很多现场验证,标准化和备受尊重。如果你对它有“不好的经历”,那么你可能犯了几个错误。如果你习惯于 SQL 语句,通过条件来描述你的数据逻辑可能有点困难,但这值得学习。并且:是的,我已经“尝试”将其“应用于表之间的复杂连接策略”,它甚至比经过 DBMS 优化的 SQL 语句更好用。 - specializt
+1 我同意这里的观点:Spring应用程序(Java EE也是如此)中使用DAO和服务的典型方式非常低效。有很多不必要、毫无意义的代码只会妨碍开发。Spring Data更好一些,但仍不是我首选的方法,我的做法是简单地拥有一个通用类(比如“AppDatabase”),它包装了标准的JPA API,使其更易于使用(例如“save”,“remove”,“find(jpql,args)”等方法)。 - Rogério

0

我认为这只是 Java 面向对象设计的限制。你需要一种参数化的方式来传递搜索谓词,类似于:

List<T> findByPredicate(List<Predicate> predicates, Class<T> returnType);

当谓词类别是这样的时候

class Predicate {
   String columnName;
   Operator operator;
   String value;
}

因此,您可以表达“name = 'John'”,age >= 21等。
这不是一种理想的解决方案,代码变得不那么易读,您需要将谓词转换为数据库查询,并且需要进行少量类型转换,这容易导致运行时错误。
您可以使用Spring Data这样的库来避免重新发明轮子。您甚至不需要一个通用DAO,只需要提供一个接口方法即可。
List<Person> findByName(String name);

并且在应用程序启动时将自动生成一个实现。请查看Spring Data JPA获取更多信息。


0

试试这个:

public interface GenericDao<T> {

    public List<T> loadAll() throws Exception;
    public Long saveOrUpdate(T domain) throws Exception;
    public void saveOrUpdate(List domainList) throws Exception;
    public void delete(T domain) throws Exception;
    public T get(Serializable id) throws Exception;
    public List<T> getListByCriteria(DetachedCriteria detachedCriteria);
    public List<T> getListByCriteria(DetachedCriteria detachedCriteria,
                                     int offset, int size);
    public List<T> filterListWithCondition(T domain) throws Exception;

}

public class GenericDaoImpl<T> extends HibernateDaoSupport implements GenericDao<T> {

        @Autowired
        SessionFactory sessionFactory;

        private Class<T> entityClass;
        private MySQLIntegrityConstraintViolationException sqlException = new MySQLIntegrityConstraintViolationException("Duplicate Record inserted");

        @Autowired
        public void setSession(SessionFactory sessionFactory){
        this.setSessionFactory(sessionFactory);
        }

        public GenericDaoImpl() {
            entityClass = (Class<T>) ((ParameterizedType) getClass()
                          .getGenericSuperclass()).getActualTypeArguments()[0];
        }

        public List<T> loadAll() throws Exception{
            Session session = getHibernateTemplate().getSessionFactory().openSession();
            List<T> list = session.createQuery("from "+entityClass.getName()).list();
            session.close();
            return list;
        }

        public void delete(T domain) throws Exception {

                Session session = sessionFactory.openSession();
                Transaction tx = session.beginTransaction();

                session.delete(domain);
                tx.commit();
                session.close();

        }

        public Long saveOrUpdate(T domain) throws Exception {

            try {
                Session session = sessionFactory.openSession();
                Transaction tx = session.beginTransaction();

                session.saveOrUpdate(domain);
                tx.commit();
                Serializable ids = session.getIdentifier(domain);
                session.close();
                return (Long)ids;

            } catch (ConstraintViolationException  e) {
                throw new ConstraintViolationException("Duplicate Record inserted", sqlException, "");
            } 

        }

        public void saveOrUpdate(List domainList) throws Exception {
            try {
                Session session = sessionFactory.openSession();
                Transaction tx = session.beginTransaction();

                Object dom  = null;

                for(int i =0; i<domainList.size(); i++) {

                    dom = domainList.get(i);
                    session.saveOrUpdate(dom);

                     if ( i % 10 == 0 ) { 
                          //10, same as the JDBC batch size
                          //flush a batch of inserts and release memory:
                         session.flush();
                         session.clear();
                     }

                }

                tx.commit();
                session.close();

            } catch (ConstraintViolationException  e) {
                throw new ConstraintViolationException("Duplicate Record inserted", sqlException, "");
            } 

        }

        public T get(Serializable id) throws Exception{

                Session session = getHibernateTemplate().getSessionFactory().openSession();
                T o = (T) session.get(entityClass, id);
                return (T)o;

        }

        public List<T> getListByCriteria(DetachedCriteria detachedCriteria,
                                         int offset, int size) {
            return (List<T>) getHibernateTemplate().findByCriteria(detachedCriteria, offset, size);
        }

        public List<T> getListByCriteria(DetachedCriteria detachedCriteria) {
            return (List<T>) getHibernateTemplate().findByCriteria(detachedCriteria);
        }

        public List<T> filterListWithCondition(T domain) throws Exception {
            return (List<T>) getHibernateTemplate().findByExample(domain);
        }

}

public interface GenericService<T> {

    public List<T> loadAll() throws Exception;
    public Long saveOrUpdate(T domain) throws Exception;
    public void saveOrUpdate(List domainList) throws Exception;
    public void delete(T domain) throws Exception;
    public T get(Serializable id) throws Exception;
    public List<T> getListByCriteria(DetachedCriteria detachedCriteria);
    public List<T> getListByCriteria(DetachedCriteria detachedCriteria, int offset, int size);
    public List<T> filterListWithCondition(T domain) throws Exception;

}

public class GenericServiceImpl<T, T2 extends GenericDao<T>> implements GenericService<T> {

    @Autowired
    private T2 genericDao;

    @Override
    public List<T> loadAll() throws Exception {
        return genericDao.loadAll();
    }

    @Override
    public Long saveOrUpdate(T domain) throws Exception{
        return genericDao.saveOrUpdate(domain);
    }

    @Override
    public void delete(T domain) throws Exception {
        genericDao.delete(domain);
    }

    @Override
    public T get(Serializable id) throws Exception {
        return genericDao.get(id);
    }

    @Override
    public List<T> getListByCriteria(DetachedCriteria detachedCriteria) {
        return genericDao.getListByCriteria(detachedCriteria);
    }

    @Override
    public List<T> getListByCriteria(DetachedCriteria detachedCriteria,
            int offset, int size) {
        return genericDao.getListByCriteria(detachedCriteria, offset, size);
    }

    @Override
    public List<T> filterListWithCondition(T domain) throws Exception {
        return genericDao.filterListWithCondition(domain);
    }

    @Override
    public void saveOrUpdate(List domainList) throws Exception {
        genericDao.saveOrUpdate(domainList);
    }

}

欢迎来到Stack Overflow!由于这是一段相当长的代码块,我认为如果您评论一下确切地更改了什么以修复OP的问题,那么您的答案可能会得到改进。 - josliber

0

//实现GenericDao和GenericService

//StateDao

public interface StateDao extends GenericDao<State> {

}

// StateDaoImpl

@Repository("stateDao")

public class StateDaoImpl extends GenericDaoImpl<State> implements StateDao {

    @Autowired
    SessionFactory sessionFactory;
// another specific businness operation perform

}

// 状态服务

public interface StateService extends  GenericService<State> {


}

// StateServiceImpl

@Repository("stateService")

public class StateServiceImpl extends GenericServiceImpl<State, StateDao> implements StateService { 

   @Resource
   StateDao stateDao;

//using stateDao object of another specific operation
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接