Olá Pessoal, tudo bom?
O artigo de hoje é a quarta 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 – SetParameters
De acordo com a ultima parte, nós criamos os construtores necessários para criar uma instância da classe NamedParameterStatement. Dentro desses construtores nós realizávamos as atividades de parser do namedSQL, transformando para positionalSQL e a criação de PreparedStatement interno. Seguindo o padrão de métodos JDBC nesse momento é necessário atribuirmos os parâmetros para a query criada.
Assim será melhor criarmos um método que retorne o PreparedStatement interno da classe e dessa forma atribuiremos os parâmetros a essa SQL? Pense que não sabemos quais são as posições dos parâmetros, temos conhecimento apenas dos labels que utilizamos para criar a namedSQL. Dessa forma, a única coisa a ser feita será criarmos métodos em que podemos setar os parâmetros necessários a nossa Query.
Se observarmos a API da PreparedStatement ((API java.sql.PreparedStatement – JDK 8 – http://docs.oracle.com/javase/8/docs/api/java/sql/PreparedStatement.html)) podemos perceber que existe uma gama muito grande de métodos set, como setBoolean, setDate, setDouble, setInt, setString. A maioria desses métodos segue o padrão de possuir dois parâmetros. O primeiro é a posição que o ponto de interrogação se encontra no SQL (perguntinha, começa em 0 ou 1????) e o segundo é a informação do tipo correspondente que queremos setar.
Para a nossa classe iremos seguir o mesmo conceito, ou seja o primeiro parâmetro será a posição e o segundo a informação. Somente que aqui, não iremos passar um int com a posição, mas iremos passar uma String com o label. Assim um esqueleto inicial do método seria o seguinte:
1 2 3 |
public void setInt(String name, int value) throws SQLException { //alguma coisa aqui } |
Agora o que irá dentro do nosso método? O primeiro ponto a ser pensado é que nós precisamos setar esse valor passado no preparedStatement interno. Assim de alguma forma é necessário obter as posições que esse label se encontrava no SQL. Para isso vamos relembrar o nosso parser.
Em um determinado ponto, linhas 80 a 86, quando encontrávamos um label, este era armazenado em um Map, mapPosicoesParametros, juntamente com a posição naquele momento do parser. Será esse map que trará a informação das posições de um determinado label. Como vamos utilizar isso em vários métodos de set, vamos criar um método a parte para realizar essa atividade. O código abaixo possui as informações para realizar essa tarefa:
1 2 3 4 5 6 7 |
private List<Integer> getIndexes(String name) { List<Integer> indexes = mapPosicoesParametros.get(name); if(indexes == null) { throw new IllegalArgumentException("Parameter not found: " + name); } return indexes; } |
Lembre-se, estamos trabalhando com um Map, logo o próprio label é a key para uma determinada posição. Caso a lista retornada seja nula, isso significa que aquele label nunca esteve presente no SQL, dessa forma retornaremos uma Exception indicando que não foi possível achar o parâmetro.
Bom, com o método retornando uma lista de posições, somos obrigados a trabalhar com uma estrutura de repetição. Para ficar mais claro vamos utilizar a estrutura de for each, por ser semanticamente mais fácil de ler. E além disso já teremos a posição para inserir o valor como parâmetro no PreparedStatement. Assim o nosso código ficará da seguinte forma:
1 2 3 4 5 |
public void setInt(String name, int value) throws SQLException { for(Integer position : getIndexes(name)){ statement.setInt(position, value); } } |
Para os métodos set de outros tipos iremos seguir o mesmo padrão, realizando apenas a mudança das informações referentes ao tipo. Como forma de ilustrar aqui o exemplo iremos criar apenas alguns métodos set, sendo estes os mais comuns. Caso você necessite de outros métodos fique a vontade para criar de acordo com a sua necessidade, bastando verificar quais são os métodos existentes dentro da API do PreparedStatement.
Iremos criar os métodos set para os tipos Boolean, Date, Double, Int, Long, Object, String, Time e TimeStamp. Maiores detalhes você confere no código inteiro da classe até essa parte que está 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 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 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
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 * Construtor que realiza a instanciacao de um prepareStatement a partir da connection * @param connection the database connection * @param query the parameterized query * @throws SQLException if the statement could not be created */ 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 //////////////////////////////////////////////////////////////// // PARAMETERS //////////////////////////////////////////////////////////////// /** * Returns the indexes for a parameter. * @param name parameter name * @return parameter indexes * @throws IllegalArgumentException if the parameter does not exist */ private List<Integer> getIndexes(String name) { List<Integer> indexes = mapPosicoesParametros.get(name); if(indexes == null) { throw new IllegalArgumentException("Parameter not found: " + name); } return indexes; } /** * Sets a parameter. * @param name parameter name * @param value parameter value * @throws SQLException if an error occurred * @throws IllegalArgumentException if the parameter does not exist * @see PreparedStatement#setBoolean(int, boolean) */ public void setBoolean(String name, boolean value) throws SQLException { for(Integer position : getIndexes(name)){ statement.setBoolean(position, value); } } /** * Sets a parameter. * @param name parameter name * @param value parameter value * @throws SQLException if an error occurred * @throws IllegalArgumentException if the parameter does not exist * @see PreparedStatement#setDate(int, java.sql.Date) */ public void setDate(String name, Date value) throws SQLException { for(Integer position : getIndexes(name)){ statement.setDate(position, value); } } /** * Sets a parameter. * @param name parameter name * @param value parameter value * @throws SQLException if an error occurred * @throws IllegalArgumentException if the parameter does not exist * @see PreparedStatement#setDouble(int, double) */ public void setDouble(String name, double value) throws SQLException { for(Integer position : getIndexes(name)){ statement.setDouble(position, value); } } /** * Sets a parameter. * @param name parameter name * @param value parameter value * @throws SQLException if an error occurred * @throws IllegalArgumentException if the parameter does not exist * @see PreparedStatement#setInt(int, int) */ public void setInt(String name, int value) throws SQLException { for(Integer position : getIndexes(name)){ statement.setInt(position, value); } } /** * Sets a parameter. * @param name parameter name * @param value parameter value * @throws SQLException if an error occurred * @throws IllegalArgumentException if the parameter does not exist * @see PreparedStatement#setInt(int, int) */ public void setLong(String name, long value) throws SQLException { for(Integer position : getIndexes(name)){ statement.setLong(position, value); } } /** * Sets a parameter. * @param name parameter name * @param value parameter value * @throws SQLException if an error occurred * @throws IllegalArgumentException if the parameter does not exist * @see PreparedStatement#setObject(int, java.lang.Object) */ private void setObject(String name, Object value) throws SQLException { for(Integer position : getIndexes(name)){ statement.setObject(position, value); } } /** * Sets a parameter. * @param name parameter name * @param value parameter value * @throws SQLException if an error occurred * @throws IllegalArgumentException if the parameter does not exist * @see PreparedStatement#setString(int, java.lang.String) */ public void setString(String name, String value) throws SQLException { for(Integer position : getIndexes(name)){ statement.setString(position, value); } } /** * Sets a parameter. * @param name parameter name * @param value parameter value * @throws SQLException if an error occurred * @throws IllegalArgumentException if the parameter does not exist * @see PreparedStatement#setTimestamp(int, java.sql.Time) */ public void setTime(String name, Time value) throws SQLException { for(Integer position : getIndexes(name)){ statement.setTime(position, value); } } /** * Sets a parameter. * @param name parameter name * @param value parameter value * @throws SQLException if an error occurred * @throws IllegalArgumentException if the parameter does not exist * @see PreparedStatement#setTimestamp(int, java.sql.Timestamp) */ public void setTimestamp(String name, Timestamp value) throws SQLException { for(Integer position : getIndexes(name)){ statement.setTimestamp(position, value); } } }//Fim da classe |
finnaly{
Na próxima parte desse artigo iremos ver o código necessário para a execução da nossa classe. Está terminando!! 🙂
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