web-dev-qa-db-fra.com

Hibernate: CRUD Generic DAO

Mon application Web possède de nombreuses tables/entités de service, telles que payment_methods, tax_codes, province_codes, etc.

Chaque fois que j'ajoute une nouvelle entité, je dois écrire un DAO. Le fait est que, fondamentalement, ils sont tous les mêmes, mais la seule différence est la classe d'entité elle-même.

Je sais que les outils Hibernate peuvent générer automatiquement le code pour moi mais je ne peux pas les utiliser maintenant (ne demandez pas pourquoi) donc je pense à un Generic DAO. Il y a beaucoup de littérature à ce sujet mais je ne peux pas assembler des morceaux et les faire fonctionner avec Spring.

Il s'agit de génériques, je pense, il y aura quatre méthodes de base:

  • listAll
  • saveOrUpdate
  • deleteById
  • getById

et c'est tout.


Question:

Quelle est la meilleure pratique pour ne pas réinventer la roue? N'y a-t-il pas encore quelque chose de prêt à l'emploi?

36
Fabio B.

voilà le mien

@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

et vous pouvez ensuite accéder comme ceci dans la couche de service:

 @Autowired
    private Dao dao;

   @Transactional(readOnly = true)
    public List<MyEntity> getAll() {
      return dao.getAll(MyEntity.class);
    }
42
NimChimpsky

Spring Data JPA est un merveilleux projet qui génère des DAO pour vous, et plus encore! Il suffit de créer une interface (sans aucune implémentation):

interface PaymentMethodsDao extends JpaRepository<PaymentMethods, Integer> {}

Cette interface (via hérité JpaRepository ) vous donnera automatiquement:

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

L'interface est fortement typée (génériques) et implémentée automatiquement pour vous. Pour chaque entité, il vous suffit de créer une interface étendant JpaRepository<T,Integer extends Serializable>.

Mais attendez, il y a plus! En supposant que votre PaymentMethod a name et validSince champs persistants. Si vous ajoutez la méthode suivante à votre interface:

interface PaymentMethodsDao extends JpaRepository<PaymentMethods, Integer> {

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

}

le framework analysera le nom de la méthode:

findBy ( Nom comme) And ( ValidSince supérieur à)

créer la requête JPA QL, appliquer la pagination et le tri (Pageable page) et exécutez-le pour vous. Aucune implémentation nécessaire:

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

Requête résultante:

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

Et avec la pagination appliquée.

Le seul inconvénient est que le projet est plutôt nouveau et qu'il est relativement facile de toucher des buts (mais il est très activement développé).

25
Tomasz Nurkiewicz

vous pouvez utiliser DAO générique comme levier pour d'autres classes DAO spécifiques à un domaine. Supposons que vous ayez une classe de domaine d'employé comme:

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


}

alors le DAO générique requis ressemblerait à ceci:

Interface DAO générique:

 public interface GenericRepositoryInterface<T> {

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

Implémentation générique de 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);
}
} 

Cette classe DAO générique doit ensuite être étendue par chaque classe DAO spécifique au domaine. La classe DAO spécifique au domaine peut même implémenter une autre interface pour des opérations qui ne sont pas courantes en général. Et préférez envoyer des informations de type à l'aide du constructeur.

4
Shahid Yousuf

N'écrivez pas de dao spécifique pour chaque entité. Vous pouvez implémenter un DAO générique qui effectue 90% du travail pour toutes les entités dont vous avez besoin. Vous pouvez l'étendre au cas où vous souhaiteriez un traitement spécifique de certaines entités.

Dans le projet sur lequel je travaille actuellement, nous avons un tel DAO qui encapsule la session Hibernate fournissant des méthodes similaires à celles que vous avez décrites. De plus, nous utilisons ISearch API - le projet open source hébergé chez google code et fournissant une interface de construction de critères très pratique pour Hibernate et JPA.

4
AlexR

vous pouvez créer une interface baseDAO et une classe d'implémentation baseDAO. Et lorsque vous avez besoin d'un cas d'utilisation spécifique avec différents types de classe, vous pouvez simplement créer le DAO de cette classe qui hérite de la classe baseDAO et implémenter une interface supplémentaire avec les besoins spécifiques de cette classe comme celui-ci.

IBaseDAO

 public interface IBaseDAO<T> {

/**
 * @Purpose :Save object of type T
 * @param transientInstance
 */
public Object persist(final T transientInstance);

/**
 * @Purpose :Delete object of type T
 * @param persistentInstance
 */
public void remove(final T persistentInstance);

/**
 * @Purpose :Update Object of type T
 * @param detachedInstance
 * @return
 */
public T merge(final T detachedInstance);

/**
 * @Purpose :Find object by 'id' of type T
 * @param identifier
 * @return
 */
public T findById(final Long identifier, Class<?> persistClass);
}

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

}

et interface spécifique

public interface IUserDAO extends IBaseDAO<User> {

   public User getUserById(long userId);

   public User findUserByUsername(String username);

}

et des classes comme celle-ci

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

}
1
Mandar Kawtakwar