Vou descrever alternativas que o Spring Framework oferece para simplificar esse trabalho, em cenários variados, como o foco em base de dados relacional. Ou seja, um overview das ferramentas do Spring para trabalhar com os componentes de persistência.
Código legado, bem legado...
Projetos Java antigos, desenvolvidos a + de 8 anos, normalmente não utilizam uma solução ORM (Mapeamento Objeto Relacional). Nesses projetos é muito comum o uso de bibliotecas "caseiras", escritas in house, para otimizar o uso do JDBC.
O Spring JDBC é um modulo do Spring interessante para esse tipo de cenário. Com ele é possível reduzir consideravelmente o volume de código JDBC. O principal componente do Spring JDBC é o JdbcTemplate, ele disponibiliza uma série de métodos para operações CRUD, consultas e comandos em lote. Para tirar proveito do uso contextos, injeção de dependências e inversão de controle, faz todo o sentido trabalhar em conjunto com o Spring Bean. Dessa forma seria possível injetar a referência do JdbcTemplate nos componente DAO (pattern Data Access Object).
O código a seguir demonstra um fragmento do DAO que utiliza o JdbcTemplate para inserir/atualizar uma entidade (Mercadoria).
@Component public class MercadoriaDAO { //SQL private final static String INSERT_MERCADORIA = "INSERT INTO mercadoria (nome,descricao,preco,quantidade) VALUES (?,?,?,?)"; private final static String UPDATE_MERCADORIA = "UPDATE mercadoria SET nome = ?, descricao = ?, preco = ?, quantidade = ? WHERE id = ?"; private final static String GET_MERCADORIA_BY_ID = "SELECT * FROM mercadoria WHERE id = ?"; private final static String GET_MERCADORIAS_BY_NOME = "SELECT * FROM mercadoria WHERE nome like ?"; @Autowired private JdbcTemplate jdbcTemplate; public void save(Mercadoria m) { if (m.getId() == null) { jdbcTemplate.update(INSERT_MERCADORIA, new Object[]{ m.getNome(), m.getDescricao(), m.getPreco() }); } else { jdbcTemplate.update(UPDATE_MERCADORIA, new Object[]{ m.getNome(), m.getDescricao(), m.getPreco(), m.getId() }); } } ... }
Além do método update também pode ser utilizado para realizar a remoção da entidade. No trecho de código a seguir coloco dois exemplos de consultas, utilizando JdbcTemplate. Note que na consulta utilizamos o componente RowMapper, o MercadoriaRowMapper. O RowMapper é utilizado pelos métodos query de JdbcTemplate, ele lê os dados do ResultSet e faz a transformação em uma instância de Mercadoria.
O método queryForObject é utilizado para retornar uma instância da entidade (ou null) com filtro por id, por exemplo. Enquanto o método query retorna uma lista de objetos que podem ser encontrados de acordo com o SQL.
... //ainda em MercadoriaDAO private class MercadoriaRowMapper implements RowMapper<Mercadoria> { public Mercadoria mapRow(ResultSet rs, int row) throws SQLException { int id = rs.getInt("id"); String nome = rs.getString("nome"); String descricao = rs.getString("descricao"); double preco = rs.getDouble("preco"); return new Mercadoria(id, nome, descricao, qtde, preco); } } public Mercadoria findById(Integer id) { return jdbcTemplate.queryForObject(GET_MERCADORIA_BY_ID, new Object[] { id }, new MercadoriaRowMapper()); } public List<Mercadoria> getMercadoriasByNome(String nome) { return jdbcTemplate.query(GET_MERCADORIAS_BY_NOME, new Object[] { nome + "%" }, new MercadoriaRowMapper()); } ...
Outra estratégia, que não me agrada, seria fazer no DAO a Mercadoria uma herança para JdbcDaoSupport, e acessar o JdbcTemplate encapsulado nesse componente. Com Spring JDBC o código DAO pode ficar bem mais compacto. Para ilustrar isso, compare o DAO c/ JDBC puro e o DAO utilizando Spring JDBC. Veja também as configurações do Spring e o o pom.xml com as dependências para esses módulos.
Projetos com Hibernate
O Spring também oferece soluções para reduzir o esforço e agregar funcionalidades durante o desenvolvimento de projetos Java utilizando soluções ORM, como Hibernate ou JPA. O Spring ORM é outro módulo da suíte Spring, ele oferece funcionalidades para facilitar o uso de soluções baseadas em Mapeamento Objeto Relacional.
Em versões antigas do Hibernate, era trabalhoso manter a Session vinculada ao contexto de execução, por exemplo utilizar a mesma instância em diferentes DAOs dentro do mesmo fluxo de request. Por isso o Spring criou o HibernateTemplate, com a proposta de disponibilizar a Session corrente ao contexto de execução.
Mas a partir do Hibernate 3.0.1 com contextual sessions, isso não é mais necessário. Nas versões mais recentes do Spring é possível usar a Session no contexto de execução, além de centralizar as configurações e realizar a injeção da SessionFactory nos DAOs.
A seguir o código do DAO da Mercadoria com Hibernate:
@Component public class MercadoriaDAO { @Autowired private SessionFactory sessionFactory; private final Session getCurrentSession(){ return this.sessionFactory.getCurrentSession(); } public void save(Mercadoria m) { if (m.getId() == null) { this.getCurrentSession().persist(m); } else { this.getCurrentSession().merge(m); } } public Mercadoria findById(Integer id) { return (Mercadoria) this.getCurrentSession().get(Mercadoria.class, id); } public List<Mercadoria> getMercadoriasByNome(String nome) { return this.getCurrentSession() .createQuery("from model.Mercadoria m where m.nome like ?") .setParameter(0, nome+"%") .list(); } ... }
O spring-config.xml a seguir centraliza no Spring as configurações com banco de dados e do Hibernate. Nesse exemplo o banco de dados utilizado é o HSQLDB (local).
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="." /> <!-- SessionFactory, DataSource, ... --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="ds" /> <property name="packagesToScan" value="model" /> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect"> org.hibernate.dialect.HSQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">create</prop> </props> </property> </bean> <bean id="ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="org.hsqldb.jdbcDriver"/> <property name="url" value="jdbc:hsqldb:file:mercadoria"/> <property name="username" value="sa"/> <property name="password" value=""/> </bean> ... </beans>
Projetos com JPA++
O Spring também oferece funcionalidades bem interessantes para projetos que utilizam a Java Persistence API (JPA), através do módulo Spring Data JPA. Através da interface JpaRepository o Spring define os métodos de consulta e CRUD. O desenvolvedor trabalha de forma alto-nível, criando uma interface de persistência, enquanto a implementação fica por conta do próprio Spring. Outro elemento do Spring Data JPA é a anotação @Query, responsável por informar consultas customizadas via JPAQL.
Para esse tipo de componente o Spring adota o pattern Repository, que abstrai uma coleção de objetos com o design mais próximo ao domínio da aplicação do que com o banco de dados. Veja como ficaria a interface MercadoriaRepository:
public interface MercadoriaRepository extends JpaRepository<Mercadoria, Integer> { @Query("select m from Mercadoria m where m.nome like ?1") List<Mercadoria> getMercadoriasByNome(String nome); }
Uma vez que a interface foi definida, o Spring cria um proxy que implementa os métodos de persistência. Esse proxy será injetado em classe de negócio/controller pelo Spring. Veja o exemplo:
@Component public class MercadoriaService { @Autowired private MercadoriaRepository repo; public void save(Mercadoria m) { m.save(m); } ... }
Nesse exemplo demonstrei funcionalidades básicas da interface JpaRepository, mas existem outrs funcionalidades como o suporte a paginação da consulta SQL, veja esse exemplo. No github compartilhamos outros dois projetos que utilizam Spring Data JPA, uma aplicação web e outra desktop. Ambas fazem uso do JpaRepository.
Um pouco além: NoSQL
Na verdade esse módulo compõe o Spring Data, uma solução "guarda-chuva" com o objetivo de unificar e simplicar o armazenamento de dados em bancos relacionais e NoSQL. Abaixo desse projeto existe o módulo Spring Data MongoDB, responsável por abstrair o acesso ao MongoDB. Não é o proposito desse post abordar soluções NoSQL, mas para ter uma idéia o trecho de código a seguir demonstra repositório da Mercadoria (como Document) em versão MongoDB:
public interface MercadoriaRepository extends MongoRepository<User, String> { @Query("{ nome: ?0 }") List<User> getMercadoriasByNome(String nome); }
Abaixo do Spring Data ainda existem sub-projetos para Neo4j, Apache Hadoop, REST e outros. É possível saber um pouco mais sobre essa tecnologia, em artigo introdutório sobre Spring Data no InfoQ Brasil.
Esse é apenas um resumo de algumas soluções oferecidas pelo Spring Framework para resolver questões relacionadas a persistência em projetos Java. Sou da turma que gosta do Spring e de Java EE. Acredito que tirando proveito das melhoras funcionalidades das duas stacks, aumentamos o nosso poder fogo e logo a possibilidade de desenvolver um projeto com sucesso.
http://twitter.com/edermag
http://www.yaw.com.br
No comments:
Post a Comment