Olá Pessoal, tudo bom?
O artigo de hoje é a primeira parte de uma adaptação do artigo de Adam Crume criado em 03 de abril de 2007 para o site Java World (link).
Para essa adaptação eu modifiquei um pouco o código apresentado inserindo o conceito de generics e algumas melhorias no código. Veja abaixo como ficou o resultado.
Queries utilizando PreparedStatement
Ao construir queries SQL utilizando a biblioteca JDBC, um problema comum encontrado quando os Index Parameters são utilizados. Esses Parameters são os “?”. Ao ter uma query grande sendo construída isso pode gerar uma certa confusão para realizar a leitura correta desses parâmetros. Segue um pequeno exemplo abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public void insert(Endereco object) { //Utilizado para obter a conexao com o banco de dados Connection connection = Conexao.getInstance().obterConexao(); PreparedStatement ps = null; ResultSet rs = null; try { StringBuilder insertSQL = new StringBuilder() .append("INSERT INTO TB_ENDERECO ") .append(" (RUA, NUMERO, COMPLEMENTO, BAIRRO, CIDADE, ESTADO, PAIS) ") .append("VALUES ") .append(" (?,?,?,?,?,?,?)"); ps = connection.prepareStatement(insertSQL.toString(), Statement.RETURN_GENERATED_KEYS); ps.setString(1, object.getRua()); ps.setInt(2, object.getNumero()); ps.setString(3, object.getComplemento()); ps.setString(4, object.getBairro()); ps.setString(5, object.getCidade()); ps.setString(6, object.getEstado()); ps.setString(7, object.getPais()); ps.executeUpdate(); //Obtem o ID gerado pelo banco HSQLDB rs = ps.getGeneratedKeys(); if(rs.next()){ object.setId(rs.getLong(1)); } } catch (SQLException e) { e.printStackTrace(); throw new Exception("Problemas no sistema, por favor tente mais tarde"); } finally { //Classe que fecha todas as conexoes abertas com o banco de dados Conexao.close(rs, ps, connection); } } |
Olhando rapidamente o exemplo acima, é fácil perceber que o ponto de interrogação representa a coluna CIDADE? Infelizmente a leitura desse exemplo, sem um pouco mais de atenção, não nos tratá muitas informações. Se fosse uma query pequena, com 1 ou 2 pontos de interrogação ainda é um pouco mais fácil distinguir as informações, mas nesse caso, que temos apenas 7 parâmetros já é um pouco mais complicado de realizar a leitura.
Outro problema está no caso de adicionarmos colunas no meio da query, por exemplo, se fosse necessário adicionar a coluna CEP, entre as colunas CIDADE e ESTADO, seria necessário modificar outras linhas de código para atender essa solicitação.
E por fim um último problema seria a utilização de um mesmo parâmetro em mais de um lugar da query, por exemplo, na query abaixo:
1 |
SELECT RUA, NUMERO, COMPLEMENTO, BAIRRO, CIDADE, ESTADO, PAIS FROM TB_ENDERECO WHERE NUMERO < ? AND NUMERO > ? |
Repare que ambos os pontos de interrogação estão conectados com o mesmo parâmetro número.
Inspiração… NamedQuery
Devido aos problemas citados acima é possível observar que existem evoluções já concebidas dentro da linguagem Java. Uma delas está localizada na biblioteca JPA (Java Persistence API) pois existe uma forma de passar parâmetros com nome já criado dentro da especificação, como eles fazem com Named Query conforme o link da Oracle (repare no exemplo 8-2 que está descrito abaixo)
1 2 3 4 5 6 7 8 |
@Entity @NamedQuery( name="findAllEmployeesByFirstName", queryString="SELECT OBJECT(emp) FROM Employee emp WHERE emp.firstName = :firstname" ) public class Employee implements Serializable { ... } |
O importante aqui não é entender o que a query faz, mas sim o que significa a String “:firstname”.
Essa String na verdade é um parâmetro. Assim ao invés de inserir um ponto de interrogação para definir um parâmetro, nós utilizamos uma label de nome firstname para adicionar esse parâmetro.
OK, mas e como esse parâmetro é adicionado? Basta olhar o exemplo 8-3, que está mais abaixo no mesmo link
1 2 3 |
Query queryEmployeesByFirstName = em.createNamedQuery("findAllEmployeesByFirstName"); queryEmployeeByFirstName.setParameter("firstName", "John"); Collection employees = queryEmployessByFirstName.getResultList(); |
Repare na linha 2, é setado um parâmetro através do método setParameter(), conforme API do Java. Esse método recebe dois valores, o primeiro é uma String com o nome do parâmetro e o segundo é um Object, no caso aqui uma String, que representa o valor do parâmetro.
Se você possui um olhar mais detalhado, verificou que na hora de setar o parâmetro, o caractere dois pontos “:” simplesmente sumiu do nome do parâmetro. O caso aqui é que esse caractere apenas é um indicativo de que naquele momento está iniciando o nome de um parâmetro.
Iniciando a construção do NamedParameterStatement
Baseado nesse conhecimento já estabelecido na biblioteca JPA nós simplesmente poderíamos utilizar diretamente a biblioteca. Mas por motivos de não inserir uma nova biblioteca dentro do Projeto, seria mais interessante implementar direto uma solução para isso.
O artigo citado no começo do post foi o ponto inicial para a produção desta classe que implementa esse Statement.
SQL Parser – Explicação do que deve ser feito
Nós iremos modificar a forma de criar Strings SQL, utilizando labels, assim o ponto principal desse novo Statement é a criação de um parser desse código SQL. Assim não vamos ter um código SQL de position parameter como o abaixo:
1 2 3 4 |
INSERT INTO TB_ENDERECO (RUA, NUMERO, COMPLEMENTO, BAIRRO, CIDADE, ESTADO, PAIS) VALUES (?,?,?,?,?,?,?) |
E sim, passaremos a ter o formato de SQL com o nome de named parameter:
1 2 3 4 |
INSERT INTO TB_ENDERECO (RUA, NUMERO, COMPLEMENTO, BAIRRO, CIDADE, ESTADO, PAIS) VALUES (:rua, :numero, :complemento, :bairro, :cidade, :estado, :pais) |
E dessa forma é possível perceber que é mais claro saber quais são os parâmetros!
Mas o importante aqui é o seguinte. Não existe um código de named Parameters oficial no JDBC. Dessa forma iremos receber uma Query com esse formato, mas será necessário converter essa query para position parameters e somente após essa conversão é que será possível executar o código SQL. Nesse caso o código mais importante da funcionalidade de namedParameter será o parser (conversor) desse código. Mas infelizmente isso ficará para a parte II desse artigo! 🙂
finnaly{
Duvidas ou sugestões? Deixe seu feedback! Isso ajuda a saber a sua opinião sobre os artigos e melhorá-los para o futuro! Isso é muito importante!
Até um próximo post!
Leave a Reply