% Sinal em Java PreparedStatement
-
21-08-2019 - |
Pergunta
PreparedStatement ps = con.createStatement("select * from table1 where last_name like ?");
ps.setString(1, "'%"+lastName+"'");
Será que este trabalho o mesmo que ...
Statement s = con.createStatement("select * from table1 where last_name like %"+ lastName);
Ou PreparedStatement retirar o sinal%?
Solução
% é um carácter universal (no Oracle pelo menos), por isso, em teoria, ambos devem funcionar da mesma (supondo que você adicione a falta aspas simples)
No entanto, o primeiro seria considerado como melhor prática, uma vez que pode permitir que o optimser banco de dados não para re-analisar a instrução. O primeiro deve também protegê-lo contra injeção de SQL enquanto o segundo pode não ser.
Outras dicas
O segundo não vai funcionar porque você esqueceu as aspas em torno da corda! ao lado que você precisa escapar e ter cuidado para injecção SQL.
Suponha SQL
lastName = "and a quote' or a bracket()";
Statement s = con.createStatement("select * from table1 where last_name like '%"+ lastName + "'");
o SQL resultante é:
select * from table1 where last_name like '%and a quote' or a bracket()'
que irá falhar
variáveis ??de ligação torná-lo sempre mais seguro para trabalhar.
A resposta curta é: Sim, supondo que você corrigir o citando, os dois devem dar os mesmos resultados. O sinal de porcentagem não será "retirados" de uma declaração preparada, mais do que qualquer outro personagem seria.
resposta mais longa: A questão da declaração preparada vs declaração de uso único pode ser complexa. Se você está indo só para executá-lo uma vez, a declaração preparada levará mais tempo, porque o motor de banco de dados tem que fazer toda a configuração para uma declaração preparada, em seguida, insira os valores, em seguida, tê-lo flutuando em um cache até que o motor decide nivelá-lo. Além disso, o otimizador muitas vezes não pode processar uma declaração preparada de forma tão eficiente. O ponto inteiro de uma declaração preparada é que o otimizador analisa a consulta e inventa um plano de consulta uma vez. Suponha que você dizer algo como "select nome_cliente do cliente, onde CUSTOMER_TYPE =? E customer_zip =?". Você tem índices em ambos os tipos e zip. Com uma declaração de uso único (com valores reais preenchidos em vez de pontos de interrogação, é claro), o otimizador de consulta em muitos motores de banco de dados podem olhar para as estatísticas sobre a distribuição de valores para os dois campos, e escolher o índice que vai dar o menor conjunto de registos, em seguida, ler todos estes sequencialmente e eliminar os registos que falham o segundo teste. Com uma declaração preparada, deve escolher o índice antes de saber quais os valores serão fornecidas, por isso pode escolher o índice menos eficiente.
Você deve nunca, nunca na dor da morte código já gravação que apenas tapas aspas em torno de um valor desconhecido e enchê-lo em uma instrução SQL. De qualquer uso preparado declarações, ou escrever uma função que escapa corretamente qualquer cotações incorporadas. função Tal é trivial para escrever. Eu não entendo por que JDBC não incluir um, então você tem que escrevê-lo sozinho e incluí-lo com cada aplicativo. (Isto é especialmente verdade, dado que alguns dialetos SQL têm diferentes aspas simples que devem ser escapou caracteres.)
Aqui está um exemplo de uma função em Java:
public static String q(String s)
{
if (s==null)
return "null";
if (s.indexOf('\'')<0)
return "'"+s+"'";
int sl=s.length();
char[] c2=new char[sl*2+2];
c2[0]='\'';
int p2=1;
for (int p=0;p<sl;++p)
{
char c=s.charAt(p);
if (c=='\'')
c2[p2++]=c;
c2[p2++]=c;
}
c2[p2++]='\'';
return new String(c2,0,p2);
}
(Nota:. I acabou de editar essa função a partir da versão que eu puxado para fora do meu código para eliminar alguns casos especiais que não são relevantes aqui - desculpe se eu introduzi alguns erros ao fazer isso)
Eu costumo dar-lhe um nome muito curto, como "q" para que eu possa escrever apenas:
String sql="select customer_name from customer where customer_type="+q(custType)
+" and customer_zip="+q(custZip);
ou rápido algo e fácil assim. É uma violação das "funções give completos e nomes significativos", mas acho que vale a pena aqui, onde eu possa usar os mesmos tempos de função de dez em um comunicado.
Então eu sobrecarregá-lo a tomar datas e números e outros tipos especiais e tratá-los adequadamente.
Usando declarações preparadas com variáveis ??de ligação é muito mais rápido, porque isso significa que a Oracle não tem de analisar (compilação) instruções SQL novamente e novamente. Oracle armazena todos os comandos executados em conjunto com os planos de execução em uma tabela hash compartilhada para reutilização. No entanto a Oracle só irá reutilizar o plano de execução de instruções preparadas com variáveis ??de ligação. Quando você faz:
"SELECT * FROM table1 onde last_name como%" + lastName
A Oracle não reutilizar o plano de execução.
(Oracle hashes cada instrução SQL e quando você usa SELECT ... WHERE last_name como% "+ lastName cada instrução SQL tem um valor de hash diferente porque sobrenome variável quase sempre tem um valor diferente, por isso, a Oracle não pode encontrar o sql declaração na tabela hash e Oracle não pode reutilizar o plano de execução.)
Em uma situação de multi concorrência, o impacto é ainda maior porque a Oracle bloqueia esta tabela hash compartilhada. Esses bloqueios não duram muito tempo, mas em um multi concorrência bloqueio situação realmente começa a doer. Quando você usa instruções preparadas com variáveis ??de ligação quase nenhum bloqueio é necessário. A Oracle pela forma como chama as de spin fechaduras travas.
Somente quando você tem uma casa dataware e suas consultas demorar alguns minutos (relatórios) em vez de frações de segundos você pode usar declarações não-preparados.
Muitas vezes usamos a primeira abordagem sem problema. Por exemplo:
String sql = "SELECT * FROM LETTER_BIN WHERE LTR_XML Like ' (?) ' AND LTR_BIN_BARCODE_ID = (?)";
try
{
// Cast a prepared statement into an OralcePreparedStatement
opstmt = (OraclePreparedStatement) conn.prepareStatement(sql);
// Set the clob using a string
opstmt.setString(1,fX.toString());
// for this barcode
opstmt.setLong(2,lbbi);
// Execute the OraclePreparedStatement
opstmt.execute();
} catch(java.sql.SQLException e)
{
System.err.println(e.toString());
} finally
{
if(opstmt != null)
{
try
{
opstmt.close();
} catch(java.sql.SQLException ignore)
{
System.err.println("PREPARED STMT ERROR: "+ignore.toString());
}
}
}
Ok, eu vou levar a sua palavra para ela em Oracle. Isto é, não surpreendentemente, banco de dados do motor dependentes. Postgres comporta-se como I descritos. Ao usar o MySQL a partir JDBC - pelo menos a partir de um par de anos atrás, quando eu última olhei para isso - não há praticamente nula diferença entre declarações preparadas e declarações de uso único, porque o driver MySQL JDBC salva declarações preparadas no cliente lado, quando você executar a declaração preparada ele preenche os valores como texto e navios-o para o motor de banco de dados. Assim, tanto quanto o motor está em causa, não há realmente nenhuma coisa como uma declaração preparada. Eu não seria de todo surpreso ao saber que outros motores têm um comportamento completamente diferente.