【问题标题】:Spring MVC: Generic DAO and Service classesSpring MVC:通用 DAO 和服务类
【发布时间】:2014-12-04 23:18:30
【问题描述】:

我正在使用 Spring MVC 编写网络。我使用通用 DAO 编写了所有 DAO。现在我想重写我的服务类。如何写“通用服务”?

有我的 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 方法。我明白为什么。但是我该如何重写呢?

【问题讨论】:

    标签: java spring generics dao genericdao


    【解决方案1】:

    问题是您在 GenericManager 中直接注入 GenericDao,但它们都不是具体的 Spring bean,您将永远无法使用特定的 CountryDao。

    您不能自动装配 GenericDao,而只能定义它并提供 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 而不是使用 GenericDaos,但您仍然对服务有相同的需求。

    【讨论】:

    • 好吧,我可以看到多个错误(有些是我的:-)):DAO 类型应该作为通用类型在您的 GenericManager 中传递(请参阅我的答案中的编辑代码)并且不要忘记@Inject 注解。我更新了我的答案。
    • 不知道不会忘记,你真的应该看看我之前粘贴的两个链接......
    【解决方案2】:

    我仍然不知道为什么人们实际上使用古老的 DAO / 服务 - 带有 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
    }
    

    现在您可以简单地自动装配您的存储库:

    @Autowired
    CustomerRepository customerRepo;
    

    并像这样检索数据:

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

    就这么简单。

    【讨论】:

    • 您是否尝试过针对表之间复杂的连接策略进行此操作?我对该 API 的体验非常糟糕。
    • 好吧,你可能只是这个星球上唯一的人 - JPA 标准 API(因为基本上就是这样;周围有无功能的包装器)非常经过现场验证、标准化和备受推崇。如果您对它有“糟糕的经验”,您可能会犯一些错误。如果您习惯于 SQL 语句,那么开始通过标准描述您的数据逻辑有点困难 - 但它非常值得学习。并且:是的,我已经“尝试”了它“针对表之间的复杂连接策略”,它比 DBMS 优化的 SQL 语句更有效。
    • +1 我同意这里的要点:在 Spring 应用程序(Java EE 也是)中使用 DAO 和服务的典型方式是令人难以置信的无效。太多不必要的、毫无意义的代码只会妨碍您。 Spring Data 更好,尽管仍然不是我的首选方法,即简单地拥有一个封装标准 JPA API 的通用类(例如“AppDatabase”),使其更易于使用(使用“save”等方法, “删除”,“查找(jpql,args)”等)。
    【解决方案3】:

    我认为这只是 java OO 设计的局限。您需要一种参数化的方式来传递谓词以进行搜索,例如:

    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 了解更多信息。

    【讨论】:

      【解决方案4】:

      //实现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
      }
      

      【讨论】:

        【解决方案5】:

        试试这个:

        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 问题所做的具体更改,您的答案可能会得到改进。
        猜你喜欢
        • 2011-04-19
        • 2018-01-11
        • 2013-04-20
        • 1970-01-01
        • 2021-03-19
        • 2013-06-08
        • 2015-08-31
        • 1970-01-01
        • 2012-01-25
        相关资源
        最近更新 更多