Olá Pessoal, tudo bom?
O artigo de hoje é a terceira 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.
Criando a classe NamedParameterStatement – Construtores
Agora iremos realizar a construção da classe NamedParameterStatement. Nessa classe é que estará localizado o parser construído na segunda parte deste artigo. Mas antes de começarmos a construção dessa classe, vamos explorar como é que a interface java.sql.Connection realiza a instanciação de um objeto PreparedStatement. Isso é muito importante para que possamos entender quais são os contextos existentes para a invocação de nossa classe, visto que existem muitas formas de construir uma query SQL.
interface java.sql.Connection
A interface java.sql.Connection ((API java.sql.Connection – JDK 8 – http://docs.oracle.com/javase/8/docs/api/java/sql/Connection.html)) possui métodos para obter os três tipos básicos de Statements do JDBC:
- Statement ((API java.sql.Statement – JDK 8 – http://docs.oracle.com/javase/8/docs/api/java/sql/Statement.html))
- PreparedStatement ((API java.sql.PreparedStatement – JDK 8 – http://docs.oracle.com/javase/8/docs/api/java/sql/PreparedStatement.html))
- CallableStatement ((API java.sql.PreparedStatement – JDK 8 – http://docs.oracle.com/javase/8/docs/api/java/sql/CallableStatement.html))
Nesse artigo nós iremos focar na parte da obtenção do PreparedStatement. Para essa interface existem seis métodos que realizam esse trabalho:
- PreparedStatement prepareStatement(String sql)
- PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
- PreparedStatement prepareStatement(String sql, int[] columnIndexes)
- PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
- PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
- PreparedStatement prepareStatement(String sql, String[] columnNames)
Para a construção dessa nossa classe iremos apenas trabalhar com os dois primeiros métodos. Estes dois métodos realizam o que nós pretendemos fazer dentro da nossa classe. O primeiro método irá trabalhar com queries que não possuem o conceito de auto generated, ou seja, que não realizam a geração automática de valores para as primary keys. Já o segundo método irá trabalhar justamente com esse tipo de Query, que se utiliza de geração automática de valores.
Assim devemos ter em mente que a nossa classe irá gerar uma instância de PreparedStatement a partir de um desses dois métodos. Nesse contexto é necessário que a nossa classe possua um atributo interno que armazene essa instância gerada. Outro detalhe é que para manter a classe genérica devemos passar via construtor da nossa classe a classe Connection necessária para obtermos a instância.
Vamos explicitar isso em código. Abaixo segue o código com os dois construtores que utilizarão os métodos mencionados acima. Ambos fazem o mesmo trabalho que é realizar uma transformação do namedSQL em positionSQL e depois obter a instância do PreparedStatement.
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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; public class NamedParameterStatement { //////////////////////////////////////////////////////////////// // ATRIBUTOS //////////////////////////////////////////////////////////////// //Cria um PreparedStatement interno para realizar a execucao do SQL private PreparedStatement statement; //Map para indicar as posicoes dos parametros private final Map<String, List<Integer>> mapPosicoesParametros = new HashMap<String, List<Integer>>(); //////////////////////////////////////////////////////////////// // CONSTRUTORES //////////////////////////////////////////////////////////////// /** * Construtor que realiza a instanciacao de um prepareStatement a partir da connection * @param connection - A conexao com o banco de dados * @param query - A query no formato namedQuery * @throws SQLException - Se o statement nao puder ser criado */ public NamedParameterStatement(Connection connection, String namedQuery) throws SQLException { String parsedQuery = parse(namedQuery); statement = connection.prepareStatement(parsedQuery); } /** * Construtor que realiza a instanciacao de um prepareStatement a partir da connection para inserts com auto generated keys * @param connection - A conexao com o banco de dados * @param query - A query no formato namedQuery * @param RETURN_GENERATED_KEYS - A constante definida em java.sql.Statement.RETURN_GENERATED_KEYS * @throws SQLException - Se o statement nao puder ser criado */ public NamedParameterStatement(Connection connection, String namedQuery, Integer RETURN_GENERATED_KEYS) throws SQLException { String parsedQuery = parse(namedQuery); statement = connection.prepareStatement(parsedQuery, RETURN_GENERATED_KEYS); } //////////////////////////////////////////////////////////////// // PARSER //////////////////////////////////////////////////////////////// private String parse(String namedQuery) { int length = namedQuery.length(); //Cria um String Buffer com o tamanho do SQL StringBuilder parsedQuery = new StringBuilder(length); boolean inSingleQuote = false; boolean inDoubleQuote = false; int position = 1; //Percorre todo o SQL for(int i = 0; i < length; i++) { char c = namedQuery.charAt(i); //: Significa inicio de um rotulo de parametro //E nao esta em uma palavra com aspas simples ou duplas if(c == ':' && !inSingleQuote && !inDoubleQuote && i+1 < length && Character.isJavaIdentifierStart(namedQuery.charAt(i+1))) { int j = i+2; while(j < length && Character.isJavaIdentifierPart(namedQuery.charAt(j))) { j++; } String name = namedQuery.substring(i+1, j); c='?'; // substitui o caracter c pelo parametro de index i += name.length(); // pula i ate o fim do nome do parametro List<Integer> indexList = mapPosicoesParametros.get(name); //Se o parametro ainda nao existir no map inicializa-o if(indexList == null) { indexList = new LinkedList<Integer>(); mapPosicoesParametros.put(name, indexList); } indexList.add(position++); } //Adiciona o novo caractere a query passada pelo parser parsedQuery.append(c); if(c == '\'') { inSingleQuote = !inSingleQuote; } else if(c == '"') { inDoubleQuote = !inDoubleQuote; } } return parsedQuery.toString(); }//Fim do metodo parser }//Fim da classe |
finnaly{
Nesse momento nós já possuímos uma forma de criar instâncias da classe NamedParameterStatement, mas ainda não podemos atribuir diretamente os valores, visto que não existe uma forma de iteração com a instância de PreparedStatament criada. Essa iteração será desenvolvida na próxima parte desse artigo!
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