Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
March 28, 2022 11:54 pm GMT

Do JDBC ao Spring Data (ou: possvel reduzir cdigo?)

H tempos que venho querendo escrever este post. Mais precisamente, desde quando comentei sobre o tema deste artigo numa live sobre o livro de spring boot, escrito pelo @boaglio

Bem, finalmente saiu :D

No vdeo, cito que o Spring Data uma das ferramentas mais interessantes do Mundo Java, dada a sua facilidade de utilizao. Mas, outra coisa que me tambm me chama a ateno a quantidade de cdigo que deixamos de escrever quando usamos esse framework.

Isso me fez lembrar como crivamos nossos DAOs no passado. E acho que vale a pena falar um pouco sobre isso aqui. Divirtam-se! ;)

No princpio, era o JDBC

Java Database Connectivity foi o primeiro mecanismo que conheci para conectar com um banco de dados e enviar instrues SQL para ele (insert, select, etc).

Mais do que isso, uma API que nos permite conectar com qualquer banco de dados, bastando que tenhamos apenas o driver do banco em mos.

Isso gerou diversas vantagens. Entre elas, o fato de podermos usar as mesmas classes/interfaces para que nossos sistemas se conectassem em diversos bancos de dados relacionais.

Bem, isso no significa que nosso cdigo era simples, muito menos de fcil manuteno, como podemos ver na classe abaixo. Ela representa um DAO de usurios, com todas as operaes bsicas que podemos fazer num banco:

@Repositorypublic class RepositorioUsuariosJdbc implements RepositorioUsuarios {    @Value("${spring.datasource.url}")    private String url;    @Value("${spring.datasource.username}")    private String usuario;    @Value("${spring.datasource.password}")    private String senha;    private Connection conexao;    @PostConstruct    void posConstrutor(){        try {            conexao = DriverManager.getConnection(url, usuario, senha);        } catch (SQLException e) {            e.printStackTrace();        }    }    @Override    public void save(Usuario umUsuario) {        if(!existe(umUsuario)) salvar(umUsuario);        else atualizar(umUsuario);    }    private boolean existe(Usuario umUsuario) {        return umUsuario.getId() != null;    }    private void atualizar(Usuario usuario) {        try(var statement = conexao.prepareStatement("update usuarios set nome = ? where id = ?")){            statement.setString(1, usuario.getNome());            statement.setLong(2, usuario.getId());            statement.executeUpdate();        } catch (SQLException e) {            e.printStackTrace();        }    }    @Override    public void deleteById(Long id) {        try(var statement = conexao.prepareStatement("delete from usuarios where id = ?")){            statement.setLong(1, id);            statement.executeUpdate();        } catch (SQLException e) {            e.printStackTrace();        }    }    @Override    public List<Usuario> findAll() {        var usuarios = new ArrayList<Usuario>();        try(var statement = conexao.prepareStatement("select * from usuarios");        var resultset = statement.executeQuery()){            while(resultset.next()){                var id = resultset.getLong("id");                var nome = resultset.getString("nome");                var dataNascimento = resultset.getObject("data_nascimento", LocalDate.class);                var usuario = new Usuario(nome, dataNascimento);                usuario.setId(id);                usuarios.add(usuario);            }        } catch (SQLException e) {            e.printStackTrace();        }        return usuarios;    }    @Override    public void close() {        try {            conexao.close();        } catch (SQLException e) {            e.printStackTrace();        }    }    private boolean idEhNulo(Usuario umUsuario) {        return umUsuario.getId() == null;    }    private void salvar(Usuario usuario){        var sql = "insert into usuarios (nome, data_nascimento) values (?, ?)";        try(var statement = conexao.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)){            statement.setString(1, usuario.getNome());            statement.setObject(2, usuario.getDataNascimento());            statement.executeUpdate();            try(var ids = statement.getGeneratedKeys()){                ids.next();                usuario.setId(ids.getLong(1));            }        } catch (SQLException e) {            e.printStackTrace();        }    }}

Escrevi este cdigo num pequeno projeto, criado apenas para este artigo. Ele pode ser consultado aqui. E, para provar que esse cdigo (e os demais que veremos a seguir) funciona, h testes automatizados no projeto.

Outras observaes:

  • o cdigo foi compilado usando Java 17 (queria testar alguns recursos novos poxa :P)
  • na poca que usvamos o que chamado hoje de "JDBC puro" (o cdigo acima) no tnhamos recursos como o try-with-resources. Ento, pode ter certeza de que o DAO acima poderia ser muito maior.

Tambm no tive a preocupao de controlar a conexo com o banco de dados (hoje, usamos connection pools e no temos que nos preocupar com isso), nem de capturar corretamente as excees.

Sobre o DAO acima... bem, acho que no preciso dizer muita coisa. Tnhamos que nos preocupar com o objeto de conexo com o banco, mas mais que isso: haviam tambm os objetos ResultSet e PreparedStatement (ou Statement, quando estvamos aprendendo a usar o JDBC. Depois que aprendamos os problemas de uso do Statement, partamos para o PreparedStatement). Esses objetos eram criados, e tinham que ser fechados quando no eram mais usados.

Tambm a construo de queries era trabalhosa, e altamente propensa a erros. O que pode acontecer quando adicionamos um parmetro a mais no insert do cdigo acima, por exemplo?

Isso sem contar a quantidade imensa de cdigo repetido.

Mas ento veio o Hibernate, e nossa vida melhorou (bastante, eu diria):

Tabelas e objetos so (quase) a mesma coisa - Hibernate

Dizem que o Gavin King uma pessoa muito parecida com o Linus Torwalds em termos de simpatia e carisma.

Se isso verdade, no sei dizer. Mas posso afirmar sem medo de errar que ele foi o criador de uma das ferramentas mais importantes do Mundo Java. Ela foi criada em 2001 e at hoje usamos ela (a implementao de referncia do Spring Data usa Hibernate por baixo dos panos).

Ele entendeu que, de certa forma, podamos fazer um paralelo entre atributos de objetos e registros em tabelas, com o conjunto de atributos de um objeto sendo comparado a uma linha de uma tabela. Pode ser que seja algo meio bvio hoje em dia, mas no sei se em 2001 isso era to claro assim (alguns de ns nem eram nascidos em 2001 :D).

Utilizando o Hibernate, nosso cdigo fica consideravelmente menor:

@Repositorypublic class RepositorioUsuariosHibernate implements RepositorioUsuarios {    @PersistenceContext    private EntityManager em;    private Session sessao;    @PostConstruct    void posConstrutor(){        sessao = em.unwrap(Session.class);    }    @Override    public void save(Usuario umUsuario) {        sessao.save(umUsuario);    }    @Override    public void deleteById(Long id) {        var usuario = sessao                .createQuery("from Usuario where id = %d".formatted(id))                .uniqueResult();        sessao.delete(usuario);    }    @Override    public List<Usuario> findAll() {        return sessao.createQuery("from Usuario").getResultList();    }    @Override    public void close() {        sessao.close();    }}

No temos mais o objeto Connection nem mesmo os objetos PreparedStatement ou o ResultSet, mas apenas a Session. Ela era como um Faade para o banco. Toda vez que precisvamos fazer algo nele, era s usar a Session.

OBS: para obter a Session, usei o EntityManager que veio com o JPA. Falarei sobre ele mais tarde.

Para obtermos a Session do Hibernate, era necessrio um pouco mais de cdigo. Como o Hibernate utiliza o JDBC, tambm precisava da URL de conexo, usurio, senha e o dialeto do banco que se usaria no sistema. E essa Session podia ser obtida via configurao programtica ou por XML (o famoso hibernate.cfg.xml). Uma boa referncia sobre como esse processo era feito essa aqui.

Isso resolveu boa parte dos nossos problemas. Entretanto, o Hibernate era uma soluo muito boa. To boa que outros programadores/empresas tambm criaram suas ferramentas de mapeamento objeto-relacional (como o TopLink, que depois virou EclipseLink).

Por essas e outras razes foi criada a especificao JPA.

A unificao de diversas ferramentas de ORM: Java Persistence API

Com a criao da JPA, ferramentas como o EclipseLink e o Hibernate passaram a compartilhar principalmente de diversas anotaes que serviam na maioria das vezes para realizar o mapeamento de objetos para tabelas e vice-versa.

Essas anotaes deviam ser inseridas na classe que se pretendia persistir. Abaixo, a classe Usuario devidamente "anotada":

@Entity@Table(name = "usuarios")@DynamicUpdate@NoArgsConstructor(access = AccessLevel.PRIVATE)@EqualsAndHashCode(of = {"nome", "dataNascimento"})@Getterpublic class Usuario {    @Id    @GeneratedValue(strategy = GenerationType.IDENTITY)    @Setter    private Long id;    private String nome;    private LocalDate dataNascimento;    public Usuario(String nome, LocalDate dataNascimento) {        this.nome = nome;        this.dataNascimento = dataNascimento;    }    public void atualizarNome(String novoNome) {        this.nome = novoNome;    }}

OBS: essa classe est sendo usada em todos os DAOs deste artigo. Logo, pode haver cdigo necessrio para um DAO e desnecessrio para outro (por exemplo, os getters e setters so necessrios para o DAO JDBC, mas completamente dispensveis para os demais DAOs)

Abaixo, podemos ver o mesmo DAO, implementado usando o Hibernate segundo a especificao JPA:

@Repositoryclass RepositorioUsuariosJpa implements RepositorioUsuarios {    @PersistenceContext    private EntityManager em;    @Override    public void save(Usuario umUsuario){        em.persist(umUsuario);        em.flush();    }    @Override    public void deleteById(Long id){        Usuario usuario = em.find(Usuario.class, id);        em.remove(usuario);    }    @Override    public List<Usuario> findAll(){        return em.createQuery("from Usuario").getResultList();    }    @Override    public void close() {        em.close();    }}

A priori no h praticamente nenhuma diferena. Agora usamos esse tal de EntityManager no lugar da Session. De fato, olhando somente o cdigo, pouca coisa mudou.

O EntityManager nos ajudou a lidar com a persistncia de forma padronizada, pois veio com a especificao JPA. Para mais detalhes, veja esse artigo aqui.

De qualquer forma, ainda tnhamos que lidar com uma certa quantidade de cdigo repetido. Pense que os DAOs precisavam receber de forma injetada o EntityManager, e manipul-lo em todos os DAOs.

Isso no era um grande problema (pra quem j tinha trabalhado com DAOs usando JDBC). Mas a veio o pessoal da VMWare dizer pra gente que dava pra fazer melhor ainda. E, normalmente, eles esto certos.

O estado da arte - Spring Data

O Spring Data (mais especificamente o Spring Data JPA, embora todos da famlia seguem os mesmos princpios) um framework que facilitou de vez a criao de DAOs. Basicamente no temos que escrever praticamente nada no DAO para ele funcionar.

Veja como fica nosso DAO com a utilizao dele:

@Repositorypublic interface RepositorioUsuariosSpringDataJpa extends JpaRepository<Usuario, Long> {}

Apenas uma interface que estende de outra do prprio framework suficiente. Nada mais!

Ok, tem tambm as anotaes na classe Usuario, mas voc entendeu.

Para quem est comeando agora com programao Java, se no pegar nenhum sistema legado para dar manuteno, provavelmente j comea com essa ferramenta. Talvez no veja o que ela traz de bom e suas vantagens. Ento, se um dia voc reclamar que ela no boa por alguma razo...bem, j foi bem pior :D

Podem ter certeza que resumi bastante toda a histria. H muitos outros detalhes que valeriam a pena citar. Mas procurei focar no cdigo, e o quanto ele foi reduzindo com o passar do tempo.

Perceberam o quanto evolumos na escrita de nossos DAOs? Atualmente, quase no precisamos escrever eles. E DAOs j foram as classes que mais tinham cdigo em diversos sistemas!

O que o futuro nos reserva? Ser que um dia no precisaremos nem mais escrever essa interface? As anotaes no cdigo bastaro?

E voc? Ainda acha que no possvel refatorar o cdigo da sua aplicao?

Abrao!


Original Link: https://dev.to/rodrigovp/do-jdbc-ao-spring-data-ou-e-possivel-reduzir-codigo-361f

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To