Problema com construção do Prepared Statement

2 respostas
R

Boa Tarde amigos,

estou com um problema para montar uma consulta SQL, mas para fazê-los entender o meu problema, vou explicar o que quero.

O meu intuito é: ao visualizar um formulário de pesquisa de um filme por exemplo, o usuário deve preencher pelo menos um campo desse formulário (ou 2, 3, todos…enfim). Ao preencher esse formulário, esses dados são enviados para um servlet, que envia para a classe FilmeDAO. Nesta classe eu tenho o seguinte método:

public ResultSet listar(int idDiretor, int idAtor, int idGenero, int idLegenda, int idIdioma) throws SQLException{
        ConnectionFactory conexao = new ConnectionFactory();
        conexao.conectar();
        PreparedStatement pst = null;
        ResultSet rs = null;
        try{
            pst = conexao.conn.prepareStatement(SELECT_BY_DADOS);
            if(idDiretor==0){
                pst.setString(1, "\" or \"x\"=\"x");
            }else{
                pst.setInt(1, idDiretor);
            }
            if(idAtor==0){
                pst.setString(2, "\" or \"x\"=\"x");
            }else{
                pst.setInt(2, idAtor);
            }
            if(idIdioma==0){
                pst.setString(3, "\" or \"x\"=\"x");
            }else{
                pst.setInt(3, idIdioma);
            }
            if(idGenero==0){
                pst.setString(4, "\" or \"x\"=\"x");
            }else{
                pst.setInt(4, idGenero);
            }
            if(idLegenda==0){
                pst.setString(5, "\" or \"x\"=\"x");
            }else{
                pst.setInt(5, idLegenda);
            }
            if(getNome()==null){
                pst.setString(6, "\" or \"x\"=\"x");
            }else{
                pst.setString(6, getNome()+"%");
            }
            if(getDataLancamento()==null){
                pst.setString(7, "\" or \"x\"=\"x");
            }else{
                pst.setString(7, getDataLancamento());
            }
            if(getLancamento()==true){
                pst.setBoolean(8, getLancamento());
            }else{
                pst.setString(8, "\" or \"x\"=\"x");
            }
            rs = pst.executeQuery();
        }catch(SQLException e){
            throw new SQLException("Erro na execucao do sql.");
        }
        return rs;
    }

Como vocês podem ver, o próprio método faz (ou tenta fazer) uma manipulação dos dados que ele receber, verificando se cada dado que ele recebeu é nulo ou não, e se for nulo, ele irá injetar na query uma espécie de SQL Injection que irá fazer com que a consulta desconsidere aquele refinamento de dados (pelo menos na minha pobre mente funcionaria assim).
Para ver se essa gambiarra louca funcionaria, eu testei pelo MySQL WorkBench a seguinte query:

select distinct f.* from filme f inner join idioma_filme idf on f.id_filme = idf.id_filme inner join idioma i on idf.id_idioma = i.id_idioma inner join legenda_filme lf on lf.id_filme = f.id_filme inner join legenda l on l.id_legenda = lf.id_legenda inner join ator_filme af on f.id_filme = af.id_filme inner join ator a on af.id_ator = a.id_ator inner join filme_genero fg on fg.id_filme = f.id_filme inner join genero g on g.id_genero = fg.id_genero where (f.id_diretor = "" or "x"="x") and (a.id_ator = "" or "x"="x") and (i.id_idioma = "" or "x"="x") and (g.id_genero = "" or "x"="x") and (l.id_legenda = "" or "x"="x") and (f.nome_filme like "" or "x"="x") and (f.data_lancamento = "" or "x"="x") and (f.lancamento = "" or "x"="x");

A query original seria:

private static final String SELECT_BY_DADOS = "select distinct f.* from filme f " + "inner join idioma_filme idf on f.id_filme = idf.id_filme " + "inner join idioma i on idf.id_idioma = i.id_idioma " + "inner join legenda_filme lf on lf.id_filme = f.id_filme " + "inner join legenda l on l.id_legenda = lf.id_legenda " + "inner join ator_filme af on f.id_filme = af.id_filme " + "inner join ator a on af.id_ator = a.id_ator " + "inner join filme_genero fg on fg.id_filme = f.id_filme " + "inner join genero g on g.id_genero = fg.id_genero " + "where (f.id_diretor = ?) and (a.id_ator = ?) and (i.id_idioma = ?) and (g.id_genero = ?) " + "and (l.id_legenda = ?) and (f.nome_filme like ?) and (f.data_lancamento = ?) " + "and (f.lancamento = ?)";

Ou seja, eu tentei simular a query no Workbench, supondo que todos os dados fossem nulos, subentendendo-se que o usuário iria querer todos os filmes cadastrados. Ao executar essa query no Workbench, ela funciona exatamente como eu quero, ela traz todos os filmes cadastrados. Se eu colocar que o usuário sabe somente que o nome do filme começa com “a”, ele me trouxe todos os filmes que começavam com “a”, e beleza, todo mundo é bonito e bem dotado. O problema é quando eu tento fazer o mesmo usando o PreparedStatement da forma que usei no codigo acima. Ele simplesmente não me retorna resultado algum. Ele só me retorna resultado se eu setar todos os campos da pesquisa do sql.

Eu sei que posso não ter sido muito claro em minha dúvida, e também ficou meio confuso…mas isso se trata de um projeto final de um curso técnico, e preciso adicionar esse tipo de funcionalidade ao meu sistema. Desconfio que o problema está na forma com que o pst.setString() seta aquela pequena string na hora de executar a consulta.

Peço que qualquer um que saiba como resolver o problema, me ajude. Se alguém souber também uma forma melhor e mais inteligente de construir um formulário com a mesma funcionalidade, por favor, me explique.

Estarei no aguardo.

2 Respostas

drsmachado

Bom, se todos os argumentos forem nulos, significa que o usuário quer uma busca completa (tipo SELECT * FROM TABELA), certo? Qual a razão de passar parâmetros?
Validando isso antes de criar o preparedstatement te facilitaria muito.
Segundo, acredito que criar uma classe Filme, contendo os atributos que quer seria mais interessante e elegante (até mesmo sem usar DTO).
A partir daí, temos o seguinte, pegando o objeto do Filme, validando cada campo, fica mais fácil de criar um preparedStatement…

R

Concordo com o seu argumento sobre criar uma classe filme já com as variáveis que representam as FKs da tabela, porém, esse não é o meu verdadeiro problema.
Tudo bem, se o usuário quiser fazer uma pesquisa sem parâmetros, significa sim que ele pode fazer um “select * from filme” por exemplo. Mas esse não é o intuito do formulário.
O intuito é que o usuário possa realizar uma consulta, em que ele não seja obrigado à informar todos os campos, somente os que ele sabe, e quando ele enviar esses campos para
a persistência, eu não tenha que fazer uso de múltiplos métodos de busca no banco.

Porque estou tão insistente nesse problema?
Porque a minha idéia funciona no Workbench, mas não funciona no java. =/
Com essa lógica funcionando na aplicação, iria me economizar milhares de linha de código em que eu teria que escrever um método para cada tipo de consulta, ou um método com um switch gigantesco dentro do método.

O que eu quero é exatamente o que aquele formulário de pesquisa avançada no google faz quando você quer procurar uma imagem por exemplo:
Você pode informar o tipo do conteúdo, o tamanho da imagem, imagens coloridas ou preto-e-branco, extensão da imagem, direitos de uso…mas quando você faz sua pesquisa, você não precisa informar todos os dados, somente os que você sabe e deseja.

Criado 13 de maio de 2011
Ultima resposta 13 de mai. de 2011
Respostas 2
Participantes 2