java Spring MVC:通用 DAO 和服务类
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26288373/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring MVC: Generic DAO and Service classes
提问by martin
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"?
我正在 Spring MVC 中编写 web。我使用通用 DAO 编写了所有 DAO。现在我想重写我的服务类。我该如何编写“通用服务”?
There are my DAOs:
有我的 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 ################################ */
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?
编译器(和Eclipse)看不到findByName
和findByCode
方法。我明白为什么。但是我怎样才能重写它呢?
回答by bmeurant
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.
问题是您在 GenericManager 中直接注入了 GenericDao,但它们都不是具体的 Spring bean,您将永远无法使用特定的 CountryDao。
You must not autowire GenericDao but only define it and provide setter :
您不能自动装配 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;
}
...
}
}
Then, you will have to inject a concrete spring bean in your concrete services. i.e. in CountryManagerImpl:
然后,您必须在具体服务中注入具体的 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;
}
...
}
You will have then a full spring bean injected with your concrete CountryDao type and its specific methods.
然后,您将拥有一个完整的 spring bean,其中注入了您的具体 CountryDao 类型及其特定方法。
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.javaand 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)
你可以看看我们在 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(使用控制器而不是服务但它是相似的)
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.
并且,顺便说一句,您应该考虑使用 Spring Data 而不是使用 GenericDaos,但您仍然对您的服务有相同的需求。
回答by specializt
i still dont know why people actually use archaic DAO / service - models with Spring Data; totally unnecessary, error-prone and whatnot.
我仍然不知道为什么人们实际上使用古老的 DAO/服务 - 带有 Spring Data 的模型;完全没有必要,容易出错等等。
Spring Data JPA has some extremelyuseful interfaces for that stuff : JpaRepositoryand 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 :
Spring Data JPA为这些东西提供了一些非常有用的接口:JpaRepository和JpaSpecificationExecutor——它们封装了你想要的一切,你只需要你的标准实体,就是这样——其他的一切都将由 spring 处理,你只需输入你的标准并得到什么您想要,无需重新发明轮子。难道你没有真正阅读文档?它非常有用:
official introduction : http://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/
官方介绍: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/
文档: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
小方法: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
天才本人的例子: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.
就这么简单。
回答by Divyang Upadhyay
Try This:
试试这个:
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);
}
}
回答by Divyang Upadhyay
//Implementing GenericDao and GenericService
//实现GenericDao和GenericService
// StateDaO
// 状态道
public interface StateDao extends GenericDao<State> {
}
// StateDaoImpl
// 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
}
回答by gerrytan
I think it's just the limitation of the java OO design. You need a parametrized way to pass the predicates for searching, something like:
我认为这只是java OO设计的限制。您需要一种参数化的方式来传递搜索的谓词,例如:
List<T> findByPredicate(List<Predicate> predicates, Class<T> returnType);
Where the predicate class is something like this
谓词类是这样的
class Predicate {
String columnName;
Operator operator;
String value;
}
Hence you can express "name = 'John'", age >= 21, etc
因此,您可以表达“姓名 = 'John'”、年龄 >= 21 等
This is not an ideal solution, code become less human readable, you will need to transform the predicates into database queries and there are few type casting needs to be done which is prone to runtime error.
这不是一个理想的解决方案,代码变得不太可读,您需要将谓词转换为数据库查询,并且几乎不需要进行类型转换,这很容易出现运行时错误。
You can avoid reinventing the wheel with library like Spring Data. You don't even need a generic DAO, you just need to supply an interface method like
您可以避免使用 Spring Data 之类的库重新发明轮子。你甚至不需要一个通用的 DAO,你只需要提供一个接口方法,比如
List<Person> findByName(String name);
and an implementation will be automatically generated at application bootstrap. Have a look at Spring Data JPA for more.
并且将在应用程序引导时自动生成一个实现。查看 Spring Data JPA 了解更多信息。