Спящий режим: CRUD Generic DAO - PullRequest
36 голосов
/ 15 марта 2012

В моем веб-приложении есть много таблиц / сущностей служб, таких как payment_methods, tax_codes, province_codes и т. Д.

Каждый раз, когда я добавляю новую сущность, я должен написать DAO. Дело в том, что, по сути, все они одинаковы, но единственное отличие - это сам класс сущностей .

Я знаю, что инструменты Hibernate могут генерировать код для меня автоматически, но я не могу использовать их сейчас (не спрашивайте, почему), поэтому я думаю о Generic DAO . Об этом много литературы, но я не могу собрать кусочки и заставить работать с Spring.

Я думаю, что все дело в генериках, в нем будет четыре основных метода:

  • listAll
  • saveOrUpdate
  • deleteById
  • getById

и все.


Вопрос:

Какая лучшая практика для того, чтобы не изобретать колесо? Есть ли еще что-нибудь готовое для использования?

Ответы [ 5 ]

42 голосов
/ 15 марта 2012

вот мой

@Component
public class Dao{

    @Resource(name = "sessionFactory")
    private SessionFactory sessionFactory;

    public <T> T save(final T o){
      return (T) sessionFactory.getCurrentSession().save(o);
    }


    public void delete(final Object object){
      sessionFactory.getCurrentSession().delete(object);
    }

    /***/
    public <T> T get(final Class<T> type, final Long id){
      return (T) sessionFactory.getCurrentSession().get(type, id);
    }

    /***/
    public <T> T merge(final T o)   {
      return (T) sessionFactory.getCurrentSession().merge(o);
    }

    /***/
    public <T> void saveOrUpdate(final T o){
      sessionFactory.getCurrentSession().saveOrUpdate(o);
    }

    public <T> List<T> getAll(final Class<T> type) {
      final Session session = sessionFactory.getCurrentSession();
      final Criteria crit = session.createCriteria(type);
  return crit.list();
    }
// and so on, you shoudl get the idea

и вы можете получить доступ к сервису на таком же уровне:

 @Autowired
    private Dao dao;

   @Transactional(readOnly = true)
    public List<MyEntity> getAll() {
      return dao.getAll(MyEntity.class);
    }
25 голосов
/ 15 марта 2012

Spring Data JPA - замечательный проект, который генерирует DAO для вас и многое другое! Вам нужно только создать интерфейс (без какой-либо реализации):

interface PaymentMethodsDao extends JpaRepository<PaymentMethods, Integer> {}

Этот интерфейс (через унаследованный JpaRepository) автоматически выдаст вам:

PaymentMethod save(PaymentMethod entity);
Iterable<PaymentMethod> save(Iterable<? extends PaymentMethod> entities);
PaymentMethod findOne(Integer id);
boolean exists(Integer id);
Iterable<PaymentMethod> findAll();
long count();
void delete(Integer id);
void delete(PaymentMethod entity);
void delete(Iterable<? extends PaymentMethod> entities);
void deleteAll();
Iterable<PaymentMethod> findAll(Sort sort);
Page<PaymentMethod> findAll(Pageable pageable);
List<PaymentMethod> findAll();
List<PaymentMethod> findAll(Sort sort);
List<PaymentMethod> save(Iterable<? extends PaymentMethods> entities);
void flush();
PaymentMethod saveAndFlush(PaymentMethods entity);
void deleteInBatch(Iterable<PaymentMethods> entities);

Интерфейс строго типизирован (обобщен) и автоматически реализован для вас. Для каждой сущности все, что вам нужно сделать, - это создать интерфейс, расширяющий JpaRepository<T,Integer extends Serializable>.

Но подождите, это еще не все! Предполагая, что ваши PaymentMethod имеют name и validSince постоянные поля. Если вы добавите следующий метод в ваш интерфейс:

interface PaymentMethodsDao extends JpaRepository<PaymentMethods, Integer> {

  Page<PaymentMethod> findByNameLikeAndValidSinceGreaterThan(
    String name, Date validSince, Pageable page
  );

}

фреймворк проанализирует имя метода:

findBy ( Имя как) And ( ValidSince больше чем)

создайте запрос JPA QL, примените разбивку на страницы и сортировку (Pageable page) и запустите его для вас. Нет необходимости в реализации:

paymentMethodsDao.findByNameLikeAndValidSinceGreaterThan(
  "abc%",
  new Date(),
  new PageRequest(0, 20, Sort.Direction.DESC, "name"
);

Результирующий запрос:

SELECT *  //or COUNT, framework also returns the total number of records
FROM PaymentMethods
WHERE name LIKE "abc%"
  AND validSince > ...

И с подкачкой.

Единственным недостатком является то, что проект довольно новый, и его довольно легко достичь (но он очень активно развивается).

4 голосов
/ 09 июля 2015

вы можете использовать Generic DAO в качестве рычага для других специфичных для домена классов DAO. Предположим, у вас есть класс Employee Domain:

  @Entity
  @Table(name="employee")
  public class Employee {

    @Id
    @Column(name="id")
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

    @Column(name="emp_name")
    private String empName;

    @Column(name="emp_designation")
    private String empDesignation;

    @Column(name="emp_salary")
    private Float empSalary;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public String getEmpDesignation() {
        return empDesignation;
    }

    public void setEmpDesignation(String empDesignation) {
        this.empDesignation = empDesignation;
    }

    public Float getEmpSalary() {
        return empSalary;
    }

    public void setEmpSalary(Float empSalary) {
        this.empSalary = empSalary;
    }


}

тогда требуемый универсальный DAO будет выглядеть примерно так:

Общий интерфейс DAO:

 public interface GenericRepositoryInterface<T> {

    public T save(T emp);
    public Boolean delete(T emp);
    public T edit(T emp);
    public T find(Long empId);
}

Общая реализация DAO:

@Repository
public class GenericRepositoryImplementation<T> implements GenericRepositoryInterface<T> {

protected EntityManager entityManager;
private Class<T> type;

public GenericRepositoryImplementation() {
    // TODO Auto-generated constructor stub

}

public GenericRepositoryImplementation(Class<T> type) {
    // TODO Auto-generated constructor stub

    this.type = type;
}

public EntityManager getEntityManager() {
    return entityManager;
}

@PersistenceContext
public void setEntityManager(EntityManager entityManager) {
    this.entityManager = entityManager;
}
@Override
public T save(T emp) {
    // TODO Auto-generated method stub
    entityManager.persist(emp);
    entityManager.flush();
    return emp;
}

@Override
public Boolean delete(T emp) {
    // TODO Auto-generated method stub
    try {
         entityManager.remove(emp);
    } catch (Exception ex) {
        return false;
    }
    return true;
}

@Override
public T edit(T emp) {
    // TODO Auto-generated method stub
    try{
       return entityManager.merge(emp);
    } catch(Exception ex) {
        return null;
    }
}

@Override
public T find(Long empId) {
    // TODO Auto-generated method stub
    return (T) entityManager.find(Employee.class, empId);
}
} 

Этот общий класс DAO затем должен быть расширен каждым классом DAO для конкретного домена. Специфичный для домена класс DAO может даже реализовывать другой интерфейс для операций, которые не являются общими в целом. И предпочитаю отправлять информацию о типах используя конструктор.

4 голосов
/ 15 марта 2012

Не пишите определенный дао для каждой сущности.Вы можете реализовать один общий DAO, который выполняет 90% работы для всех необходимых вам объектов.Вы можете расширить его в тех случаях, когда вам нужна конкретная обработка определенных сущностей.

В проекте, над которым я сейчас работаю, есть такой DAO, который оборачивает сеанс Hibernate, предоставляя методы, аналогичные описанным вами.Кроме того, мы используем ISearch API - проект с открытым исходным кодом, размещенный в коде Google и предоставляющий очень удобный интерфейс для построения критериев для Hibernate и JPA.

1 голос
/ 25 августа 2017

вы можете создать интерфейс baseDAO и класс реализации baseDAO.И когда вам нужен конкретный сценарий использования с различными типами классов, вы можете просто создать DAO этого класса, который наследует класс baseDAO и реализует дополнительный интерфейс с конкретными потребностями этого класса, как это1006 *

Класс BaseDAO

public class BaseDAO<T> implements IBaseDAO<T> {

@Autowired
private SessionFactory sessionFactory;

public Object persist(T entity) {
    return this.getSession().save(entity);
}

@Override
public void remove(T persistentInstance) {
    this.getSession().delete(persistentInstance);
}

@SuppressWarnings("unchecked")
@Override
public T merge(T detachedInstance) {
    return (T) this.getSession().merge(detachedInstance);
}

@SuppressWarnings("unchecked")
@Override
public T findById(Long identifier, Class<?> persistClass) {
    return (T) this.getSession().get(persistClass, identifier);
}

public SessionFactory getSessionFactory() {
    return sessionFactory;
}

public Session getSession() {
    return getSessionFactory().getCurrentSession();
}

}

и специальный интерфейс

public interface IUserDAO extends IBaseDAO<User> {

   public User getUserById(long userId);

   public User findUserByUsername(String username);

}

и подобные классы

@Repository("userDAO")
public class UserDAO extends BaseDAO<User> implements IUserDAO {

public User getUserById(long userId) {
    return findById(userId, User.class);
}

@Override
    public User findUserByUsername(String username) {
        Criteria criteria = getSession().createCriteria(User.class);
        criteria.add(Restrictions.eq("username", username));
        return (User) criteria.uniqueResult();
    }

}
...