2014-10-09 13 views
6

Я пишу веб-сайт весной MVC. Я написал все DAO, используя Generic DAO. Теперь я хотел бы переписать мои классы обслуживания. Как я могу написать «Generic Service»?Spring MVC: общие классы 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 ################################ */ 

Compiler (и Eclipse) не видят findByName и findByCode методы. Я понимаю, почему. Но как я могу его переписать?

ответ

0

Я думаю, что это просто ограничение дизайна Java-OO. Вам нужно параметризованным способ передать предикаты для поиска, что-то вроде:

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

Если класс предикат что-то вроде этого

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

Таким образом, вы можете выразить «имя =„John“», возраст > = 21 и т. Д.

Это не идеальное решение, код становится менее понятным для человека, вам нужно будет преобразовать предикаты в запросы базы данных, и нужно выполнить небольшое количество типов, которые подвержены ошибкам во время выполнения.

Вы можете избежать повторного использования колеса с помощью библиотеки типа Spring Data. Вам даже не нужен обычный DAO, вам просто нужно поставить метод интерфейса как

List<Person> findByName(String name); 

и реализация будет сгенерирован автоматически при применении бутстрапе. Взгляните на Spring Data JPA для получения дополнительной информации.

1

Проблема в том, что вы вводите непосредственно свой GenericDao в свой GenericManager, но ни один из них не является конкретным весенним бобом, и вы никогда не сможете использовать свой конкретный CountryDao.

Вы не должны autowire GenericDao, но только определить его и обеспечить сеттер:

// 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; 
    } 

... 

}

Тогда вам придется вводить конкретную пружинный боб в ваших конкретных услуг. то есть в 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; 
    } 

... 

} 

У вас будет затем полная весной фасоль впрыскивается с конкретным типом 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, но вы по-прежнему имеете ту же потребность в отношении ваших услуг.

+0

Теперь у меня есть http://pastebin.com/rN1C0i3w и она по-прежнему не работает :-( – martin

+0

Ну, я могу видеть несколько ошибок (некоторые мои :-)): Тип DAO должен передаваться как общий тип в GenericManager (см. Отредактированный код в моем ответе) и не забывайте аннотацию @Inject. Я обновил свой ответ. – bmeurant

+0

Не забудьте ничего не забыть, вы действительно должны взглянуть на две ссылки, которые я вставляю раньше ... – bmeurant

1

Я до сих пор не знаю, почему люди на самом деле используют архаичный DAO/сервис - модели с данными Spring; совершенно ненужный, подверженный ошибкам и еще много чего.

Spring Data JPA имеет некоторые чрезвычайно полезные интерфейсов для этого материала: JpaRepository и JpaSpecificationExecutor - это инкапсулировать все, что вы хотите, вам нужны только ваши стандартные Сущности и то это - все остальное будет обработано весной, вы просто ввести ваши критерии и получите именно то, что вы хотите, не изобретая колесо. Не могли бы вы действительно прочитать документацию? Его очень полезно:

официальное введение: 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/

небольшой HOWTO: 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()); 

его просто.

+0

Вы пробовали это против сложных политик объединения между таблицами? У меня был очень плохой опыт работы с этим API. – John

+0

хорошо, что вы можете быть единственным человеком на этой планете - API-критерий JPA (потому что это в основном то, что он есть, с бесфункционными обертки вокруг него) является проверенным на практике, стандартизированным и уважаемым. Если у вас был «плохой опыт», вы, вероятно, допустили несколько ошибок. Своеобразно трудно начать описывать вашу логику данных через критерии, если вы привыкли к операторам SQL, но это хорошо стоит усилий по изучению. И: Да, я «попробовал» его «против сложных политик объединения между таблицами», он работает даже лучше, чем SQL-оптимизированные SQL-операторы. – specializt

+0

+1 Я согласен с сущностью данного пункта: типичный способ использования DAO и сервисов в приложениях Spring (Java EE тоже) - это неистово неэффективно. Столько ненужного, бессмысленного кода, который только мешает. Spring Data лучше, хотя и не является моим предпочтительным подходом, который состоит в том, чтобы просто иметь один универсальный класс (скажем, «AppDatabase»), который обертывает стандартный API JPA, что упрощает его использование (с помощью таких методов, как «save», «удалить», «найти (jpql, args)» и т. д.). –

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); 
    } 

} 
+0

Добро пожаловать в переполнение стека! Поскольку это довольно длинный блок кода, я думаю, что ваш ответ может быть улучшен, если вы прокомментируете, что именно вы изменили, чтобы исправить проблему 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 

} 

// StateService

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 
} 
Смежные вопросы