Retorno de Registro Duplicado na jTable

Bom dia DEVs. Estou com o código abaixo que preenche uma jTabel a partir de uma consulta ao banco de dados de acordo com a solicitação do cliente.

private void btFiltrarActionPerformed(java.awt.event.ActionEvent evt) {                                          
        String sql = "";
        String mes = tfMesAnoPesquisa.getText().substring(0, 2); 
        String ano = tfMesAnoPesquisa.getText().substring(3, 7);
        
        if(rbMesAnoPesquisa.isSelected() && cbSubalmoxarifadoPesquisa.getSelectedItem().equals("TODOS")){
            // Pesquisa Validades por Mês e Ano em TODOS os Subalmoxarifados
            sql = "select v.id as vid, v.status as status, v.codproduto as codproduto, p.id as pid, p.tipo as tipo, p.apresentacao as apresentacao, "
                    + "p.descricao as descricao, v.origem as origem, v.detalhe_origem as detalhe_origem, v.subalmoxarifado as subalmoxarifado, "
                    + "s.id as sid, s.nome as nome, v.local as local, v.lote as lote, v.validade as validade, v.quantidade as qtde, v.cmm as cmm "
                    + "from validades v, produtos p, subalmoxarifados s where month(validade) = " + mes + " and year(validade) = " + ano + " "
                    + "and v.codproduto = p.id and v.subalmoxarifado = s.id order by validade" ;
        }else if(rbAnoPesquisa.isSelected() && cbSubalmoxarifadoPesquisa.getSelectedItem().equals("TODOS")){
            // Pesquisa Validades por Ano em TODOS os Subalmoxarifados
            sql = "select v.id as vid, v.status as status, v.codproduto as codproduto, p.id as pid, p.tipo as tipo, p.apresentacao as apresentacao, "
                    + "p.descricao as descricao, v.origem as origem, v.detalhe_origem as detalhe_origem, v.subalmoxarifado as subalmoxarifado, "
                    + "s.id as sid, s.nome as nome, v.local as local, v.lote as lote, v.validade as validade, v.quantidade as qtde, v.cmm as cmm "
                    + "from validades v, produtos p, subalmoxarifados s where year(validade) = " + tfAnoPesquisa.getText() + " "
                    + "and v.codproduto = p.id and v.subalmoxarifado = s.id order by validade";
        }else if(rbMesAnoPesquisa.isSelected() && !cbSubalmoxarifadoPesquisa.getSelectedItem().equals("TODOS")){
            // Pesquisa Validades por Mês e Ano para o Subalmoxarifado Selecionado no ComboBox
            sql = "select v.id as vid, v.status as status, v.codproduto as codproduto, p.id as pid, p.tipo as tipo, p.apresentacao as apresentacao, "
                    + "p.descricao as descricao, v.origem as origem, v.detalhe_origem as detalhe_origem, v.subalmoxarifado as subalmoxarifado, "
                    + "s.id as sid, s.nome as nome, v.local as local, v.lote as lote, v.validade as validade, v.quantidade as qtde, v.cmm as cmm "
                    + "from validades v, produtos p, subalmoxarifados s "
                    + "where month(validade) = " + mes + " and year(validade) = " + ano + " and subalmoxarifado = "+ codsubalmoxarifadosearch + " "
                    + "and v.codproduto = p.id and v.subalmoxarifado = s.id order by validade";
        }else if(rbAnoPesquisa.isSelected() && !cbSubalmoxarifadoPesquisa.getSelectedItem().equals("TODOS")){
            // Pesquisa Validades por Ano para o Subalmoxarifado Selecionado no ComboBox
            sql = "select v.id as vid, v.status as status, v.codproduto as codproduto, p.id as pid, p.tipo as tipo, p.apresentacao as apresentacao, "
                    + "p.descricao as descricao, v.origem as origem, v.detalhe_origem as detalhe_origem, v.subalmoxarifado as subalmoxarifado, "
                    + "s.id as sid, s.nome as nome, v.local as local, v.lote as lote, v.validade as validade, v.quantidade as qtde, v.cmm as cmm "
                    + "from validades v, produtos p, subalmoxarifados s "
                    + "where year(validade) = " + tfAnoPesquisa.getText() + " and subalmoxarifado = "+ codsubalmoxarifadosearch + " "
                    + "and v.codproduto = p.id and v.subalmoxarifado = s.id order by validade";
        }
        
        con.conecta();
        tbDados.getColumnModel().getColumn(0).setPreferredWidth(10);
        tbDados.getColumnModel().getColumn(1).setPreferredWidth(100);
        
        while(tmValidades.getRowCount() > 0){
            tmValidades.removeRow(0);
        }
        JOptionPane.showMessageDialog(null, sql);
        con.executeSQL(sql);
        validades = new ArrayList<>();
        produtos = new ArrayList<>();
        subalmoxarifados = new ArrayList<>();
        try{
            while(con.rs.next()){
                vb = new ValidadesBean();
                pb = new ProdutosBean();
                sb = new SubalmoxarifadoBean();
                vb.setId(con.rs.getInt("vid"));
                vb.setStatus(con.rs.getString("status"));
                vb.setCodproduto(con.rs.getInt("codproduto")); // Buscar na Tabela Produtos o TIPO a APRESENTAÇÃO e a DESCRIÇÃO do PRODUTO
                vb.setLote(con.rs.getString("lote"));
                vb.setValidade(con.rs.getDate("validade"));
                vb.setQuantidade(con.rs.getInt("qtde"));
                vb.setCmm(con.rs.getInt("cmm"));
                vb.setSubalmoxarifado(con.rs.getShort("subalmoxarifado")); // Buscar na Tabela Subalmoxarifados o NOME do SUBALMOXARIFADO
                vb.setLocal(con.rs.getString("local"));
                vb.setOrigem(con.rs.getString("origem"));
                vb.setDetalhe_origem(con.rs.getString("detalhe_origem"));
                validades.add(vb);
                pb.setId(con.rs.getInt("pid"));
                pb.setTipo(con.rs.getString("tipo"));
                pb.setApresentacao(con.rs.getString("apresentacao"));
                pb.setDescricao(con.rs.getString("descricao"));
                produtos.add(pb);
                sb.setId(con.rs.getInt("sid"));
                sb.setNome(con.rs.getString("nome"));
                subalmoxarifados.add(sb);
            }
            
            if(validades.size() == 0){
                
            }else{
                for(int i = 0; i < validades.size(); i++){
                    for(int j = 0; j < produtos.size(); j++){
                        tmValidades.addRow(new String[] {
                            sdf.format(validades.get(i).getValidade()),
                            produtos.get(i).getDescricao()
                        });
                    }
                }
            }
            
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            con.desconecta();
        }
        
    }

O interessante é que quando eu faço a busca por mês e ano os registros na tabela são mostrados corretamente, um de cada, e quando eu faço a busca por ano a tabela mostra os registros duplicados.
Alguém aí identifica qual o meu problema?
Agradecido!

Problema da duplicação resolvido! O problema agora é outro. Qdo seleciono um registro na tabela os campos referentes aos produtos não atualiza para todos os produtos e às vezes dá o erro Java.lang.ArrayOutOfBoundsException

Mostra o erro e o código relacionado

Problema Resolvido! Porém agora estou com um novo problema!!! KKKKK… Essa Vida de Dev é “Phood@” (Isso mesmo, com PH de pharmacia (afinal de contas sou farmacêutico também) , dois “O” de Zoológico (aqui em casa tenho 3 cachorros, 5 gatos e 1 passarinho) e terminando com Arroba)! Mas o que seria de nós se não fossem os BUGs, neles nosso aprendizado só cresce! E quando conseguimos resolver com nossos próprios meios a satisfação é imensa! Mas vamos ao que interessa. Agora está tudo correndo perfeitamente, se não fosse um pequeno e incômodo problema. O produto listado no jComboBox não é “setado”. Segue o código abaixo. A linha que está dando erro está comentada.

private void tbDadosLinhaSelecionada(JTable tb){

    if (tb.getSelectedRow() != -1) {
	...
    if(todos.get(tb.getSelectedRow()).getTipo().equals("Medicamento")){
       cbTipo.setSelectedItem("Medicamento");
       tfTipo.setText("");
    }else if(todos.get(tb.getSelectedRow()).getTipo().equals("Manipulado")){
       cbTipo.setSelectedItem("Manipulado");
       tfTipo.setText("");
    }else if(todos.get(tb.getSelectedRow()).getTipo().equals("Substância")){
       cbTipo.setSelectedItem("Substância");
       tfTipo.setText("");
    }else{
       cbTipo.setSelectedItem("Outros");
       tfTipo.setText(todos.get(tb.getSelectedRow()).getTipo());
    }
	...
	AtualizarCBDescricao(); 
    cbDescricao.requestFocus();
    cbDescricao.setSelectedItem(todos.get(tb.getSelectedRow()).getDescricao()); // Não está "Setando" o Produto no CombBox
    ...
}

private void AtualizarCBDescricao(){
    pb = new ProdutosBean();
    produtos = new ArrayList<>();
    if(cbTipo.getSelectedItem().equals("Selecione")){
        
    }else if(cbTipo.getSelectedItem().equals("Outros")){
        tipo = tfTipo.getText();
    }else{
        tipo = String.valueOf(cbTipo.getSelectedItem());
    }
    String sql = "select * from produtos where tipo like '" + tipo + "' order by descricao";
    cbDescricao.removeAllItems();
    cbDescricao.addItem("Selecione");
    try{
        con.conecta();
        con.executeSQL(sql);
        while(con.rs.next()){
            cbDescricao.addItem(con.rs.getString("descricao"));
            pb.setId(con.rs.getInt("id"));
            pb.setDescricao(con.rs.getString("descricao"));
            pb.setTipo(con.rs.getString("tipo"));
            pb.setApresentacao(con.rs.getString("apresentacao"));
            produtos.add(pb);
        }

    }catch(Exception e){
        e.printStackTrace();
    }finally{
        con.desconecta();
    }
}

getDescricao() deve estar retornando null ou “”

Não está retornando null nem “” pois quando retorna outro tipo como medicamento, manipulado ou substância (que estão pré carregados no jComBox TIPO) o jComboBox DESCRIÇÃO consegue carregar todos os produtos para aquele tipo e “SETA” o produto correto.

Coloca pra printar o valor que está setando no combobox.

Eu fiz o que vc sugeriu:

          AtualizarCBDescricao();
          cbDescricao.requestFocus();
          cbDescricao.setSelectedItem(todos.get(tb.getSelectedRow()).getDescricao());
          System.out.println(todos.get(tb.getSelectedRow()).getDescricao());

E apesar de continuar não setando a descrição do produto no comboBox (apenas para os tipos que não são pré-cadastrados no ComboBox, para os demais tipos pré-carregados, mostra normalmente), o System.out.println está imprimindo a descrição do produto corretamente.
O engraçado é que quando clico na linha da tabela para exibir os dados, dá pra perceber que o comboBox mostra a descrição e muda rapidamente para “Selecione”. É como se tivesse alguma coisa atualizando o comboBox de novo e setando “Selecione”, o que não está acontecendo em nenhuma linha de código.

Tem mais alguma coisa abaixo da linha cbDescricao.setSelectedItem?

tem os demais campos que tem que mostrar os dados.

nenhum deles modifica o cbDescricao?

Não. Segue código completo.
private void tbDadosLinhaSelecionada(JTable tb){

    if (tb.getSelectedRow() != -1) {
          tfId.setText(""+todos.get(tb.getSelectedRow()).getVid());
          if(todos.get(tb.getSelectedRow()).getStatus().equals("L")){
            cbStatus.setSelectedItem("L - Longe da Validade (1+ ano)");
          }else if(todos.get(tb.getSelectedRow()).getStatus().equals("D")){
              cbStatus.setSelectedItem("D - Dentro da Validade (6m a 1a)");
          }else if(todos.get(tb.getSelectedRow()).getStatus().equals("P")){
              cbStatus.setSelectedItem("P - Perto da Validade (2m a 6m)");
          }else if(todos.get(tb.getSelectedRow()).getStatus().equals("V")){
              cbStatus.setSelectedItem("V - Vencendo no Mês");
          }else if(todos.get(tb.getSelectedRow()).getStatus().equals("E")){
              cbStatus.setSelectedItem("E - Vencido em Estoque");
          }else if(todos.get(tb.getSelectedRow()).getStatus().equals("R")){
              cbStatus.setSelectedItem("R - Vencido Retirado do Estoque");
          }else if(todos.get(tb.getSelectedRow()).getStatus().equals("F")){
              cbStatus.setSelectedItem("F - Troca com Fornecedor");
          }else if(todos.get(tb.getSelectedRow()).getStatus().equals("T")){
              cbStatus.setSelectedItem("T - Troca com Outra Instituição");
          }else{
              cbStatus.setSelectedItem("SELECIONE");
          }
          if(todos.get(tb.getSelectedRow()).getTipo().equals("MEDICAMENTO")){
              cbTipo.setSelectedItem("MEDICAMENTO");
              tfTipo.setText("");
          }else if(todos.get(tb.getSelectedRow()).getTipo().equals("MANIPULADO")){
              cbTipo.setSelectedItem("MANIPULADO");
              tfTipo.setText("");
          }else if(todos.get(tb.getSelectedRow()).getTipo().equals("SUBSTÂNCIA")){
              cbTipo.setSelectedItem("SUBSTÂNCIA");
              tfTipo.setText("");
          }else{
              cbTipo.setSelectedItem("OUTROS");
              tfTipo.setText(todos.get(tb.getSelectedRow()).getTipo());
          }
          if(todos.get(tb.getSelectedRow()).getApresentacao().equals("COMPRIMIDO")){
              cbApresentacao.setSelectedItem("COMPRIMIDO");
              tfApresentacao.setText("");
          }else if(todos.get(tb.getSelectedRow()).getApresentacao().equals("CÁPSULA")){
              cbApresentacao.setSelectedItem("CÁPSULA");
              tfApresentacao.setText("");
          }else if(todos.get(tb.getSelectedRow()).getApresentacao().equals("DRÁGEA")){
              cbApresentacao.setSelectedItem("DRÁGEA");
              tfApresentacao.setText("");
          }else if(todos.get(tb.getSelectedRow()).getApresentacao().equals("AMPOLA")){
              cbApresentacao.setSelectedItem("AMPOLA");
              tfApresentacao.setText("");
          }else if(todos.get(tb.getSelectedRow()).getApresentacao().equals("FRASCO")){
              cbApresentacao.setSelectedItem("FRASCO");
              tfApresentacao.setText("");
          }else if(todos.get(tb.getSelectedRow()).getApresentacao().equals("FRASCO-AMPOLA")){
              cbApresentacao.setSelectedItem("FRASCO-AMPOLA");
              tfApresentacao.setText("");
          }else if(todos.get(tb.getSelectedRow()).getApresentacao().equals("BOLSA")){
              cbApresentacao.setSelectedItem("BOLSA");
              tfApresentacao.setText("");
          }else if(todos.get(tb.getSelectedRow()).getApresentacao().equals("SACHÊ")){
              cbApresentacao.setSelectedItem("SACHÊ");
              tfApresentacao.setText("");
          }else if(todos.get(tb.getSelectedRow()).getApresentacao().equals("BISNAGA")){
              cbApresentacao.setSelectedItem("BISNAGA");
              tfApresentacao.setText("");
          }else if(todos.get(tb.getSelectedRow()).getApresentacao().equals("POTE")){
              cbApresentacao.setSelectedItem("POTE");
              tfApresentacao.setText("");
          }else if(todos.get(tb.getSelectedRow()).getApresentacao().equals("ADESIVO")){
              cbApresentacao.setSelectedItem("ADESIVO");
              tfApresentacao.setText("");
          }else if(todos.get(tb.getSelectedRow()).getApresentacao().equals("UNIDADE")){
              cbApresentacao.setSelectedItem("UNIDADE");
              tfApresentacao.setText("");
          }else{
              cbApresentacao.setSelectedItem("OUTROS");
              tfApresentacao.setText(todos.get(tb.getSelectedRow()).getApresentacao());
          }
          AtualizarCBDescricao();
          cbDescricao.requestFocus();
          cbDescricao.setSelectedItem(todos.get(tb.getSelectedRow()).getDescricao());
          System.out.println(todos.get(tb.getSelectedRow()).getDescricao());
          
          if(todos.get(tb.getSelectedRow()).getOrigem().equals("C")){
              cbOrigem.setSelectedItem("C - Compra");
          }else if(todos.get(tb.getSelectedRow()).getOrigem().equals("D")){
              cbOrigem.setSelectedItem("D - Doação");
          }else if(todos.get(tb.getSelectedRow()).getOrigem().equals("E")){
              cbOrigem.setSelectedItem("E - Empréstimo");
          }else if(todos.get(tb.getSelectedRow()).getOrigem().equals("T")){
              cbOrigem.setSelectedItem("Troca");
          }else if(todos.get(tb.getSelectedRow()).getOrigem().equals("I")){
              cbOrigem.setSelectedItem("I - Transferência Interna");
          }else{
              cbOrigem.setSelectedItem("SELECIONE");
          }
          cbSubalmoxarifado.setSelectedItem(todos.get(tb.getSelectedRow()).getNome());
          tfLocal.setText(todos.get(tb.getSelectedRow()).getLocal());
          tfLote.setText(todos.get(tb.getSelectedRow()).getLote());
          tfValidade.setText(sdf.format(todos.get(tb.getSelectedRow()).getValidade()));
          tfQuantidade.setText(String.valueOf(todos.get(tb.getSelectedRow()).getQuantidade()));
          tfCmm.setText(String.valueOf(todos.get(tb.getSelectedRow()).getCmm()));
    }
}

Outros Métodos que Alteram o cbDescrição são:
private void cbTipoActionPerformed(java.awt.event.ActionEvent evt) {
if(cbTipo.getSelectedItem().equals(“SELECIONE”)){

    }else if(cbTipo.getSelectedItem().equals("OUTROS")){
        tfTipo.setEditable(true);
        tfTipo.requestFocus();
        tipo = tfTipo.getText();
        AtualizarCBDescricao();
    }else{
        tipo = String.valueOf(cbTipo.getSelectedItem());
        AtualizarCBDescricao();
    }
}

private void AtualizarCBDescricao(){
    pb = new ProdutosBean();
    produtos = new ArrayList<>();
    if(cbTipo.getSelectedItem().equals("SELECIONE")){
        
    }else if(cbTipo.getSelectedItem().equals("OUTROS")){
        tipo = tfTipo.getText();
    }else{
        tipo = String.valueOf(cbTipo.getSelectedItem());
    }
    String sql = "select * from produtos where tipo like '" + tipo + "' order by descricao";
    cbDescricao.removeAllItems();
    cbDescricao.addItem("SELECIONE");
    try{
        con.conecta();
        con.executeSQL(sql);
        while(con.rs.next()){
            cbDescricao.addItem(con.rs.getString("descricao"));
            pb.setId(con.rs.getInt("id"));
            pb.setDescricao(con.rs.getString("descricao"));
            pb.setTipo(con.rs.getString("tipo"));
            pb.setApresentacao(con.rs.getString("apresentacao"));
            produtos.add(pb);
        }

    }catch(Exception e){
        e.printStackTrace();
    }finally{
        con.desconecta();
    }
}

private void cbDescricaoActionPerformed(java.awt.event.ActionEvent evt) {                                            

    String sql = "select id from produtos where descricao like '" + String.valueOf(cbDescricao.getSelectedItem()) + "'";
    try{
        con.conecta();
        con.executeSQL(sql);
        if(con.rs.first()){
            codproduto = con.rs.getInt("id");
        }

    }catch(Exception e){
        e.printStackTrace();
    }finally{
        con.desconecta();
    }   
}

Mais nenhum. E é como eu falei antes, qdo o Tipo é “Medicamento”, “Manipulado” ou “Substância” que já são pre-carregados no cbTipo, a descrição do produto é mostrada corretamente no cbDescricao. O erro só ocorre qdo o Tipo setado no cbTipo é “Outros” e nesse caso um jTextField ao lado do cbTipo é que mostra o Tipo salvo no BD. O cbDescricao até mostra os produtos salvos com aquele tipo, mas não seta o produto, e tem mais, dá pra perceber que o cbDescricao até tenta setar a descrição, mas retorna rapidamente para “Selecione”.

Coloca um print no topo desse método “cb tipo chamado”, só pra debugar.
Acho que ele tá sendo chamado após selecionar a linha e resetando o cb descrição.

Se fosse isso, não “setaria” corretamente a descrição para os tipos pré-carregados no cbTipo. O cbDescrição resetaria em todas as situações. Mas o que está ocorrendo é que ele só está resetando para o Tipo “Outros”. Mas vou testar esse DEBUG.

Resolvido! O problema estava quando o tfTipo perdia o foco (FocusLost) para preencher o cbDescrição quando o cbTipo estava setado com “Outros”. Mas como eu preciso dessa funcionalidade para pesquisar pelos produtos cadastrados para o tipo descrito no jTextField, coloquei o método para atualizar o cbDescrição apenas quando estou criando um novo registro, ou seja, quando clico no botão novo (tipoRegistro = "novo") e depois que o registro é salvo, alterado ou excluído, ou clica no botão limpar campos, o tipoRegistro = "".