Oi pessoal,
Esse Hibernate vai me endoidar! :?
Vou explicar direitinho o que preciso e o que está acontecendo! (Na verdade estou tirando vários campos para simplificar o entendimento por parte de vocês)
Tenho um sistema de retaguarda de uma aplicação comercial. Uma das telas desse sistema é o de CONSULTA DE PRODUTOS. A tela possui um JTextField onde o usuário digita a palavra-chave e possui um JComboBox onde seleciona-se o critério no qual se está fazendo a busca, podendo ser:
- Código interno
- Código de barras
- Descrição
Depois disso, a tela possui um JTable com 4 colunas (as quais correspondem as 3 opções acima e incluem ainda a coluna PREÇO).
Ok… Até aí tudo bem… Tenho meus .HBM.XML e beans criados (abaixo):
public class Produto {
// Campos existentes na tabela
private Integer idProduto;
private String descricao;
private String descricaoResumida;
private Marca marca;
private Secao secao;
private Float vendaMedida;
private UnidadeMedida vendaUnidadeMedida;
private Float preco;
private Integer estoque;
private Float vasilhameMedida;
private UnidadeMedida vasilhameUnidadeMedida;
private Tributo tributo;
// Relacionamentos um-para-muitos
private Set codigoBarras;
...
public class ProdutoCodigoBarras {
private String codigoBarras;
private Produto produto;
...
Percebam então que, independentemente de qual critério eu selecionar na consulta, as minhas QUERIES deverão sempre incluir um JOIN, para que eu possa exibir o código de barras dos produtos.
Percebam também que, um mesmo produto pode ter 0 ou mais (1, 2, 3, …, n) códigos de barras. Sim, 0 porque podem existir produtos que não possuam código de barras.
No BD eu tenho duas tabelas relacionadas a isso: são a tb_produto e tb_produto_codigo_barras. A primeira possui os campos ID, descrição, preço, etc. A segunda possui apenas o campo p/ o código de barras e outro para o produto (para dizer qual é o produto que possui este código de barras).
Em SQL, uma consulta típica para listar os produtos que iniciem pelo nome SHAMPOO seria:
SELECT p.id, cb.pcbr_codigo_barras, p.prod_descricao, p.prod_preco FROM tb_produto p
LEFT OUTER JOIN tb_produto_codigo_barras cb ON cb.pcbr_produto = p.id
WHERE UPPER(p.prod_descricao) LIKE 'SHAMPOO%'
id | pcbr_codigo_barras | prod_descricao | prod_preco
------+--------------------+---------------------------------------------------+------------
10 | 7891037004276 | Shampoo Seda Dvs. | 4.50
10 | 7891037002586 | Shampoo Seda Dvs. | 4.50
10 | 7891037000308 | Shampoo Seda Dvs. | 4.50
350 | | Shampoo Revitalizante Permanente Afro 340ml | 4.99
351 | 7896000706478 | Shampoo Fell Free 350ml | 4.20
374 | 7896000705013 | Shampoo Manteiga de Karité Não Engordurante 340ml | 4.99
Percebam então que o produto de código 10 (Shampoo Seda Dvs.) possui 3 diferentes códigos de barras cadastrados. Já o produto 350 (Shampoo Revitalizante) não possui código de barras cadastrado. Os 2 últimos não possuem nada de especial.
Minha consulta HQL para o caso acima é:
result = session.find( "select distinct p "
+ "from tisc.multuspdv.beans.Produto as p "
+ "left outer join fetch p.codigoBarras as cb "
+ "where upper(p.descricao) like ? order by p.id", nome.toUpperCase()
+ "%", Hibernate.STRING );
Bom, o problema começa quando minha consulta acima deveria retornar 4 Produtos, mas ele me retorna 6. Tudo bem, eu vi na documentação do Hibernate (péssima, por sinal) que isso é normal e que devemos ter código Java para identificar objetos iguais/repetidos. E foi por isso que no meu código eu tive de colocar:
for ( int i = 0; i < result.size(); i++ ) {
Produto produto = ( Produto )result.get( i );
if ( i > 0 && produto.hashCode() == ( ( Produto )result.get( i - 1 ) ).hashCode() )
continue;
...
Funciona a contento! Caso o objeto seja igual ao último, ele pula para a próxima iteração do laço FOR, caso contrário, ele continua o código até chegar em um laço WHILE (ver abaixo) que passa por todos os códigos de barras que existam para o mesmo produto:
...
Iterator it = produto.getCodigoBarras().iterator();
// Enquanto existir códigos de barras diferentes
while ( it.hasNext() ) {
totalItems++;
dtm.addRow( new Object[] { produto.getIdProduto(),
( ( ProdutoCodigoBarras )it.next() ).getCodigoBarras(),
produto.getDescricao(), nf.format( produto.getPreco() ) } );
}
Até então eu já estava achando não tão elegante meu código, mas aí tudo piorou quando eu percebi que os produtos que não tem código de barras cadastrados não estavam sendo listados na minha JTable. Isso acontece pelo fato de que os objetos (Produtos) retornados do Hibernate que não possuem código de barras simplesmente possuem um SET (ou Lista ou Bag ou qualquer outra coisa ) vazio. Dessa forma os comandos internos ao laço WHILE nunca são executados e eu “perco” um produto (percebam que o ADDROW está lá dentro). Para resolver isso, insiro mais uma obscenidade em meu código antes da chamada iterator()!
if ( produto.getCodigoBarras().isEmpty() )
produto.getCodigoBarras().add( new ProdutoCodigoBarras() );
Funciona a contento… Agora meu JTable exibe TODOS meus produtos, independetemente de cada um deles ter ou não código de barras, ou de ter vários.
A grande questão desse tópico é o seguinte:
- Trabalhar com o Hibernate é desse forma ou existem outras maneiras que não necessitam de todo esse trabalho sujo para “controlar” o resultado a ser exibido? Não existe maneira de ser melhor? Ou sou eu que sou um desenvolvedor falido?
Eu já rodei a Internet atrás de soluções EFICIENTES para os problemas acima e simplesmente não encontrei NADA.
Aguardo a resposta de um algum guru!
Paulo Oliveira