Simplifique o DAO com Spring e Java Generics
1. Visão geral
Este artigo se concentrará emsimplifying the DAO layer usando um único objeto de acesso a dados generificado para todas as entidades no sistema, o que resultará em um acesso de dados elegante, sem confusão ou detalhamento desnecessário.
Vamos construir na classe Abstract DAO que vimos emour previous article no Spring e Hibernate, e adicionar suporte genérico.
Leitura adicional:
Introdução ao Spring Data JPA
Introdução ao Spring Data JPA com Spring 4 - a configuração do Spring, o DAO, consultas manuais e geradas e gerenciamento de transações.
Um Guia para JPA com Spring
Configure o JPA com o Spring - como configurar o EntityManager de fábrica e usar as APIs JPA brutas.
Auditoria com JPA, Hibernate e Spring Data JPA
Este artigo demonstra três abordagens para introduzir a auditoria em um aplicativo: JPA, Hibernate Envers e Spring Data JPA.
[more-722] #
2. Os DAOs Hibernate e JPA
A maioria das bases de código de produção possui algum tipo de camada DAO. Geralmente, a implementação varia de várias classes sem classe base abstrata a algum tipo de classe gerada. No entanto, uma coisa é consistente - háalways more than one. Provavelmente, existe uma relação individual entre os DAOs e as entidades no sistema.
Além disso, dependendo do nível de genéricos envolvidos, as implementações reais podem variar de código fortemente duplicado a quase vazio, com a maior parte da lógica agrupada em uma classe abstrata de base.
These multiple implementations can usually be replaced by a single parametrized DAO. Podemos implementar isso de forma que nenhuma funcionalidade seja perdida, aproveitando totalmente a segurança de tipo fornecida pelo Java Generics.
Mostraremos duas implementações desse conceito a seguir, uma para uma camada de persistência centrada no Hibernate e outra com foco em JPA. Essas implementações não são de forma alguma completas, mas podemos facilmente adicionar mais métodos adicionais de acesso a dados.
2.1. O DAO do Hibernate Abstrato
Vamos dar uma olhada rápida na classeAbstractHibernateDao:
public abstract class AbstractHibernateDao {
private Class clazz;
@Autowired
SessionFactory sessionFactory;
public void setClazz(Class< T > clazzToSet){
this.clazz = clazzToSet;
}
public T findOne(long id){
return (T) getCurrentSession().get(clazz, id);
}
public List findAll() {
return getCurrentSession().createQuery("from " + clazz.getName()).list();
}
public T create(T entity) {
getCurrentSession().saveOrUpdate(entity);
return entity;
}
public T update(T entity) {
return (T) getCurrentSession().merge(entity);
}
public void delete(T entity) {
getCurrentSession().delete(entity);
}
public void deleteById(long entityId) {
T entity = findOne(entityId);
delete(entity);
}
protected Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
}
Esta é uma classe abstrata com vários métodos de acesso a dados, que usaSessionFactory para manipular entidades.
2.2. O Hibernate DAO genérico
Agora que temos a classe abstrata DAO, podemos estendê-la apenas uma vez. The generic DAO implementation will become the only implementation precisamos de:
@Repository
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class GenericHibernateDao
extends AbstractHibernateDao implements IGenericDao{
//
}
Primeiro,note that the generic implementation is itself parameterized, permitindo ao cliente escolher o parâmetro correto caso a caso. Isso significa que os clientes obtêm todos os benefícios da segurança de tipo sem precisar criar vários artefatos para cada entidade.
Em segundo lugar, observethe prototype scope of this generic DAO implementation. Usar esse escopo significa que o contêiner Spring criará uma nova instância do DAO cada vez que for solicitado (incluindo no autowiring). Isso permitirá que um serviço use vários DAOs com parâmetros diferentes para entidades diferentes, conforme necessário.
A razão pela qual esse escopo é tão importante se deve à maneira como o Spring inicializa os grãos no contêiner. Deixar o DAO genérico sem um escopo significaria usarthe default singleton scope, which would lead to a single instance of the DAO living in the container. Obviamente, isso seria bastante restritivo para qualquer tipo de cenário mais complexo.
OIGenericDao é simplesmente uma interface para todos os métodos DAO para que possamos injetar a implementação de que precisamos:
public interface IGenericDao {
T findOne(final long id);
List findAll();
void create(final T entity);
T update(final T entity);
void delete(final T entity);
void deleteById(final long entityId);
}
2.3. O JPA DAO abstrato
OAbstractJpaDao é muito semelhante aoAbstractHibernateDao:
public abstract class AbstractJpaDao< T extends Serializable > {
private Class< T > clazz;
@PersistenceContext
EntityManager entityManager;
public void setClazz( Class< T > clazzToSet ) {
this.clazz = clazzToSet;
}
public T findOne( Long id ){
return entityManager.find( clazz, id );
}
public List< T > findAll(){
return entityManager.createQuery( "from " + clazz.getName() )
.getResultList();
}
public void save( T entity ){
entityManager.persist( entity );
}
public void update( T entity ){
entityManager.merge( entity );
}
public void delete( T entity ){
entityManager.remove( entity );
}
public void deleteById( Long entityId ){
T entity = getById( entityId );
delete( entity );
}
}
Semelhante à implementação do Hibernate DAO, estamos usando a API Java Persistence diretamente, sem depender do SpringJpaTemplate, agora obsoleto.
2.4. O JPA DAO genérico
Semelhante à implementação do Hibernate, o JPA Data Access Object também é direto:
@Repository
@Scope( BeanDefinition.SCOPE_PROTOTYPE )
public class GenericJpaDao< T extends Serializable >
extends AbstractJpaDao< T > implements IGenericDao< T >{
//
}
3. Injetando este DAO
We now have a single DAO interface we can inject. Também precisamos especificar oClass:
@Service
class FooService implements IFooService{
IGenericDao dao;
@Autowired
public void setDao(IGenericDao daoToSet) {
dao = daoToSet;
dao.setClazz(Foo.class);
}
// ...
}
Springautowires the new DAO instance using setter injection para que a implementação possa ser customizada com o objetoClass. Após esse ponto, o DAO está totalmente parametrizado e pronto para ser usado pelo serviço.
É claro que existem outras maneiras pelas quais a classe pode ser especificada para o DAO - via reflexão ou mesmo em XML. Minha preferência é por essa solução mais simples, devido à legibilidade e transparência aprimoradas em comparação ao uso da reflexão.
4. Conclusão
Este artigo discutiu osimplification of the Data Access Layer, fornecendo uma implementação única e reutilizável de um DAO genérico. Mostramos a implementação em um ambiente baseado em Hibernate e JPA. O resultado é uma camada de persistência simplificada, sem confusão desnecessária.
Para obter uma introdução passo a passo sobre como configurar o contexto Spring usando a configuração baseada em Java e o Maven pom básico para o projeto, consultethis article.
Por fim, o código deste artigo pode ser encontrado emthe GitHub project.