Retornar ResultSet na classe abstrata

Pessoal, estou com o seguinte problema:

As classes de persistência de um projeto que estou trabalhando é em JDBC (não pode ser hibernate, por questões de desempenho).

Bem, nessas classes de persistência existem muitas operações com a mesma estrutura, como, por exemplo executar um Statement e retornar um ResultSet. Não faria sentido colocar o mesmo código repetido por todas as classes de persistência, portanto, coloquei o seguinte código na classe abstrata (AbstractePersistence):

[code] protected ResultSet executarQueryToResultSet(String query){

	Statement statement = null;
	ResultSet resultSet = null;
	conexao = getConnection();
	
	try{

		statement = conexao.createStatement();
		resultSet = statement.executeQuery(query);
		conexao.commit();
		
	}catch(Exception ex){
		try { conexao.rollback(); } catch (SQLException e) { e.printStackTrace(); }
	}
	finally{
		liberarConexao(conexao);
		liberarStatment(statement);
	}
	return resultSet;
}[/code]

Dessa forma eu não preciso toda vez que chamar um método em uma classe de persistência escrever o mesmo código repetitivo acima. Quando uma sub-classe quiser executar um ResultSet, é só chamar o método da super-classe e pronto.

O problema é: como o método retorna um ResultSet, e no mesmo método da classe abstrata, eu fecho a conexão (usamos um pool, portanto é obrigatório fechar a conexão, caso contrário o pool estoura), fechando a conexão, quem recebeu o ResultSet como retorno do método não vai conseguir obter os dados do ResultSet, pois o mesmo foi fechado.

Gostaria de saber a opnião de voces sobre estas decisões, e se tem algo melhor a ser feito. Pois dessa forma não está dando certo. :expressionless:

Ao invés de retornar o ResultSet, retorne outro objeto. Por exemplo um array de Object:

ResultSet rs = ps.executeQuery(); if (rs.next()) { int colunas = rs.getMetaData().getColumnCount(); Object[] linha = new Object[colunas]; for (int i = 0; i < colunas; i++) { linha[i] = rs.getObject(i + 1); } return linha; }

Em outras palavras, ninguém da empresa sabe usar Hibernate, certo?

Tente PrepareStatement

http://java.sun.com/docs/books/tutorial/jdbc/basics/prepared.html

Em outras palavras, ninguém da empresa sabe usar Hibernate, certo?[/quote]

Bom tem que ver realmente , pois para se usar hibernate a modelagem do banco tem que estar bem definida , senao nosso amigo hibernate vai ficar
maluco maluco .

Retorne uma Coleção .

Não… em outras palavras usamos Hibernate “até dar uma dor” no sistema da central de processamento onde desempenho não é um requisito crítico.
Já no equipamento de campo onde recursos como memória e processamento são bem reduzidos, não usamos Hibernate, e sim JDBC.
Não podemos se dar ao luxo de ficar escrevendo nada em HSQL para ser traduzido para linguagem do banco (um over desnecessário).
Alem do +, não se tem pretenção de mudar o banco (Postgre) tão cedo, tão cedo mesmo.
Alem do +, se for pra usar o Hibernate executando query nativa, pra que usar Hibernate?

Se não tem nada de útil pra falar é melhor ficar calado.

[quote=pozzo]Ao invés de retornar o ResultSet, retorne outro objeto. Por exemplo um array de Object:

ResultSet rs = ps.executeQuery(); if (rs.next()) { int colunas = rs.getMetaData().getColumnCount(); Object[] linha = new Object[colunas]; for (int i = 0; i < colunas; i++) { linha[i] = rs.getObject(i + 1); } return linha; }[/quote]

Huunn, é uma boa idéia, gostei da sua abordagem :slight_smile:

Cara, eu acho que não resolveria meu problema. Se eu usa-se o PS, ainda teria de retornar um ResultSet vindo de minda classe abstrata ok?

Ae pessoal, lendo sobre algumas outras implementações da interface ResultSet, descobri que o JDBC também implementa conceitos de acesso a dados desconectados.

A classe CachedRowSetImpl é uma especialização da classe ResultSet que não usa ponteiro para os dados, e sim armazena-os em memória.
Assim, eu poderia retornar um objeto CachedRowSetImpl no retorno do método da classe abstrata.

Isso resolveria meu problema parcialmente, porque eu ainda teria problemas de consumo de recursos (se o colega fizer um select que retorne muitos resultados).

O que adotamos por enquanto foi:

As sub-classes liberam a conexão e result set’s e statements… quando achar uma solução melhor eu posto (ou se alguém souber e postar é bom também :)).

Performance? Ok.

  • Não é necessário fazer commits ou rollbacks em operações somente de leitura, como em consultas.
  • Se você fechar ou fizer qualquer operação num Statament, o ResultSet dele irá fechar.
  • Abrir e fechar conexões a cada método de consulta vai matar o teu banco de dados.
  • Commits devem ser realizados somente quando a execução das classes do domínio finalizar. Você irá acabar com dados inconsistentes no banco se não fizer isto.

Para resolver esse problema, procure sobre pools de conexões, como o DBCP ou o C3P0. Mas para a sua aplicação, mantenha essa política de que ela deve trabalhar “desconectada” e não “conectada”. O momento exato da desconexão física, deixe que um desses pools decida por você.

Dê uma olhada no Spring. Ele é muito parecido com o JDBC e já implementa suporte a tudo isso. E trabalha de maneira muito similar ao CachedRowSetImpl.

Ele também suporta batchUpdates e transações, transações aninhadas e stored procedures.

É, eu possi tirar isso dali. Não vai fazer diferença.

É exatamente esse o problema. Eu não posso liberar o statement na classe abstrata porque quando a sub-classe receber o resultado na verdade vai ter um ResultSet vázio (fechado).

O método getConnection() não cria conexão, ele vai pegar no pool (c3p0 - muito bom por sinal).

Não pensei nissi ainda…

Vini… vou dar uma olhada no Spring para ver como ele faz isso…

[quote=xdraculax]Pessoal, estou com o seguinte problema:

As classes de persistência de um projeto que estou trabalhando é em JDBC (não pode ser hibernate, por questões de desempenho).

Bem, nessas classes de persistência existem muitas operações com a mesma estrutura, como, por exemplo executar um Statement e retornar um ResultSet. Não faria sentido colocar o mesmo código repetido por todas as classes de persistência, portanto, coloquei o seguinte código na classe abstrata (AbstractePersistence):

[code] protected ResultSet executarQueryToResultSet(String query){

	Statement statement = null;
	ResultSet resultSet = null;
	conexao = getConnection();
	
	try{

		statement = conexao.createStatement();
		resultSet = statement.executeQuery(query);
		conexao.commit();
		
	}catch(Exception ex){
		try { conexao.rollback(); } catch (SQLException e) { e.printStackTrace(); }
	}
	finally{
		liberarConexao(conexao);
		liberarStatment(statement);
	}
	return resultSet;
}[/code]

Dessa forma eu não preciso toda vez que chamar um método em uma classe de persistência escrever o mesmo código repetitivo acima. Quando uma sub-classe quiser executar um ResultSet, é só chamar o método da super-classe e pronto.
[/quote]

só existe um problema com esse método : é POG.

Esse método mostra que a sua equipe não sabe trabalhar com JDBC. Então não fique ofendido quando desconfiamos que não saiba usar o hibernate.
E quando digo “usar” quero dizer “entender e fazer uso de features avançadas”.

Simplesmente descarte esse método. Refactore seu codigo. É uma péssima ideia.

  1. Use PreparedStatement SEMPRE
  2. Abra e feche a conexão ao mesmo tempo que abre e fecha a transação. O Pool de conexão é inutil se vc não tem conceito de transação.
  3. Monte o PreparedStatement , execute e leia o ResultSet num unico método. Não delegue esse tipo de controle.
  4. Sempre feche os statements ao mesmo tempo que o resultSet.
  5. menipule SQLException corretamente ( nada de e.printStackStrace)

só existe um motivo para não fechar o resultset e o retornar para ser usado depois: o padrão Fastlane Reader. Não é o seu caso.

Remodele seu codigo e terá menos problemas.

Porque?
Eles são compilados para ficarem no cache do banco, mas não vi deferenças em testes que fiz com um Statement.
Você fez testes de desempenho? Ou só leu que sempre deve-se usar PreparedStatement?

Transações são comitadas ao retornarem para o pool (quando é dado um close() na conexão) de qualquer jeito.

Não quero fazer isso. Não vejo vantagem nenhuma nessa sua abordagem.

Isso já está sendo feito pelos métodos de liberar, você não viu?

Meu foco não é esse, depois que resolver o meu problema, ai sim vou fazer o trabalho mecânico de tratar a exception e logar.

POG?

Então quer dizer que se eu quero executar um Statement em vários locais de minhas classes de persistência, eu replico o mesmo código, com a mesma estrutura de try…cath rs.close stm.close… em todos, da mesma forma; e colocar isso na classe abstrata onde todas as sub-classes executam o mesmo método da super-classe é POG?

Diga o que você faria então.

Acho que o pior problema desse método é que um cliente pode mandar uma String para ele e ferrar completamente com a base de dados da tua aplicação, ou derrubar o banco de dados inteiro. Procure saber sobre SQL Injection, e como o PreparedStatement ajuda a evitá-lo.

Faça os DAOs da tua aplicação retornarem somente o objeto ou uma coleção de objetos pesquisados, esses objetos sendo feitos a partir das classes do domínio da tua própria aplicação.

Exemplo de interfaces de métodos da classe de acesso ao banco:

public List<Cliente> buscarClientesPorNome(String nome)

public List<Cliente> buscarClientesPorExemplo(Cliente cliente)

Statements e ResultSets devem ficar encapsulados somente dentro do próprio método, nunca saindo para fora. Para fugir a essa regra, você tem que ter um bom motivo e 10x mais de cuidado ao deixar os teus objetos da camada de persistência fujam para outra camada.

Porque?
Eles são compilados para ficarem no cache do banco, mas não vi deferenças em testes que fiz com um Statement.
Você fez testes de desempenho? Ou só leu que sempre deve-se usar PreparedStatement?
[/quote]

Não é um problema de performance. É um problema de segurança. Ele ajuda a evitar SQLInjection

Se isso é verdade vc não está usando o conceito de transação. Transação necessita de um commit ou roolbak explicito.
Se vc deixa para comitar no close ( o que na realidade nunca acontece, ou se dá no commit ou se dá no execute se o autocommit está ligado) isso signfica que vc deve estar usando autocomit que é exatamente a antitese do conceito de transação.

Se vc não quer faze isso, então não se queixe que tem problemas.
Existe uma máxima que diz :"Não ha ‘eu’ em “arquitetura” (There is no I in architecture)
O que significa que o vc quer ou não quer é irrelevante.

sim.

[quote]
Diga o que você faria então.[/quote]

Já lhe disse o que faria.
Colocar métodos genéricos em classes abstratas parece legal, mas precisa aprender quando parar. Este é um caso em que deve parar de fazer isso.

Sim é POG? Explique porque ou isso é um “EU acho que é POG”?
Não é POG, é divergência de opnião. E você não tem uma justificativa plausível, portanto, sem comentários.
Pra mim, repetir a mesma estrutura de código por n métodos de classes de persistência é que é POG. E foi o que você disse que faria.

Quanto a segurança, eu até entendo isso; mas quem vai escrever os SQL são programadores, e não “paes & mães”.
O sistema não tem entradas inseridas por usuários, tudo é gerado por um Hardware. Então esse conceito de segurança, neste contexto, não faz sentido.
Se o programador é burro pra escrever um SQL desastroso, ele vai fazer isso em qualquer outra situação.

Você simplesmente leu que deve-se usar PS em todas as situações e acreditou. Nem se ocupou em fazer testes para ver se realmente faz sentido utilizá-lo em todas as situações.

Com relação a dar um commit no retorno da conexão para o pool realmente concordo com você. Não é muito inteligente.

Sugiro fortemente que você veja a implementação disso aí no Spring. Você vai se surpreender.

[quote=xdraculax]Sim é POG? Explique porque ou isso é um “EU acho que é POG”?
Não é POG, é divergência de opnião. E você não tem uma justificativa plausível, portanto, sem comentários.
Pra mim, repetir a mesma estrutura de código por n métodos de classes de persistência é que é POG. E foi o que você disse que faria.

Quanto a segurança, eu até entendo isso; mas quem vai escrever os SQL são programadores, e não “paes & mães”.
O sistema não tem entradas inseridas por usuários, tudo é gerado por um Hardware. Então esse conceito de segurança, neste contexto, não faz sentido.
Se o programador é burro pra escrever um SQL desastroso, ele vai fazer isso em qualquer outra situação.

Você simplesmente leu que deve-se usar PS em todas as situações e acreditou. Nem se ocupou em fazer testes para ver se realmente faz sentido utilizá-lo em todas as situações.[/quote]

minha humilde opnião? sim, é pog. Tanto é pog, que está gerando um problema pra vc, que não existiria se estivesse sendo utilizado corretamente.

conexões, statments, e resultsets são recursos de IO. vc abre, vc fecha. vc só tem que determinar em que ponto vc vai fazer isso, e essa regra tem que valer pra todo o sistema. não tem como fugir dessa estrutura quando se está trabalhando com jdbc:

Connection conn = null; //... o que mais vc quiser, statmente, resultset geralemnte try{ pega uma conexão monta o statment cria o resultset itera e faz o q vc quiser }catch(SQLException e){ trata } finally{ fecha os recursos }

o código pra fechar os recursos é um porre? é mesmo. mas basta criar métodos pra fechar esses recursos, como existe no DBUtils da apache.(close e closeQuietly, ver aqui).

o PreparedStatment não é só amis seguro, evitando sqlinjection,ele tbm é mais rápido, se vc for rodar aquiele comando mais de uma vez. e ainda reduz os problemas de ficar montando o sql com concatenações de strings, tipo problemas de aspas por exemplo.

Quem acha que não tem como fugir dessa estrutura, veja o Spring.
E realmente é pog manter essa estrutura em todo lugar, eu concordo com o xdraculax. Embora ele realmente devesse usar o PreparedStatement, é realmente louvável a preocupação em não repetir código. Repetição de código é uma idiotice, um dos grandes males a serem evitados.

Já vi o DBUtils da apche.
Mas como já tinhamos feito os métodos para liberar conexão, statements … Não usei o DBUtils.

O que você falou é verdade, recursos IO devem ser abertos, utilizados e liberados; o que impede que isso seja feito na classe abstrata?
Só serve se for no método que você está vendo na sub-classe?

Repetir código é matar a Orientação a Objetos, piora manutenção, principalmente quando se usa os try… cath como “muleta de desenvolvimento”.

Infelizmente não dá certo porque o modelo de leitura do ResultSet é conectado. Mas isso já resolvi usando outra implementação da interface.

Como eu disse, a segurança do PS nesse caso não é relevante pois o sistema não tem entradas de dados inseridas por usuários. Todos os dados são gerados por hardware.

O desempenho, bem, todo livro e documentação diz que é melhor, mas no frigir dos ovos, não vejo isso.

Não costumo acreditar nos rótulos, prefiro testar é ver se realmente é a “Coca-Cola toda”.

Mas tudo bem, se todo mundo acha que é gambiarra, fazer o que.
Eu vou continuar fazendo assim, pois acredito que não é POG. Mesmo assim, obrigado.

[quote=ViniGodoy]Quem acha que não tem como fugir dessa estrutura, veja o Spring.
E realmente é pog manter essa estrutura em todo lugar, eu concordo com o xdraculax. Embora ele realmente devesse usar o PreparedStatement, é realmente louvável a preocupação em não repetir código. Repetição de código é uma idiotice, um dos grandes males a serem evitados.[/quote]

Sinceramente eu não vi, não posso falar. Vc ta se referindo ao código que controla o JdbcTemplate?

não gosto de repetir código tbm, mas neste caso em especifico eu não conheço outra alternativa. Espero mudar conhecendo essa abordagem do spring.