Solucionado (ver solução)

Importante

Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!

Solucionado
(ver solução)
14
respostas

Erro de cast em enum

o erro: Exception in thread “main” java.lang.ClassCastException: class java.lang.Integer cannot be cast to class br.com.alura.jdbc.modelo.Status (java.lang.Integer is in module java.base of loader ‘bootstrap’; br.com.alura.jdbc.modelo.Status is in unnamed module of loader ‘app’) at br.com.alura.jdbc.dao.ProdutoDAO.trasformarResultSetEmProduto(ProdutoDAO.java:150) at br.com.alura.jdbc.dao.ProdutoDAO.listar(ProdutoDAO.java:66)

o enum:

public enum Status {

    EM_ESTOQUE(1), INDISPONIVEL(5), ENCOMENDADO(10);

    private int valor;

    Status(int valor) {
        this.valor = valor;
    }

    public int getValor() {
        return this.valor;
    }

}

a classe produto: No BD o atributo status é integer.

public class Produto {

    private Integer id;
    private String nome;
    private String descricao;
    private Integer categoriaId;
    private String categoriaNome;
    private Status status;

    public Produto(String nome, String descricao) {
        super();
        this.nome = nome;
        this.descricao = descricao;
    }

    public Produto(Integer id, String nome, String descricao, Status status, Integer categoriaId, String categoriaNome)  {

        this.id = id;
        this.nome = nome;
        this.descricao = descricao;
        this.status = status;
        this.categoriaId = categoriaId;
        this.categoriaNome = categoriaNome;
    }

ProdutoDAO:

public List<Produto> listar() {
        List<Produto> produtos = new ArrayList<Produto>();
        try {
            String sql = "SELECT P.ID, P.NOME, P.DESCRICAO, P.STATUS, C.ID, C.NOME " + "FROM CATEGORIA C "
                    + "INNER JOIN PRODUTO P ON C.ID = P.CATEGORIA_ID";

            try (PreparedStatement pstm = connection.prepareStatement(sql)) { //Para garantir o fechamento dos recursos -> a cláusula try-with-resources
                pstm.execute();

                trasformarResultSetEmProduto(produtos, pstm);
            }
            return produtos;
        } 
        catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    private void trasformarResultSetEmProduto(List<Produto> produtos, PreparedStatement pstm) throws SQLException {
        try (ResultSet rst = pstm.getResultSet()) {
            while (rst.next()) {
                Produto produto = new Produto(rst.getInt(1), rst.getString(2), rst.getString(3), (Status) rst.getObject(4), rst.getInt(5), rst.getString(6));
                produtos.add(produto);
            }
        }
    }

Qualquer dica é bem vinda.

Obrigado.

14 respostas

Opa Ricardo, tudo certo?

Então o problema está na hora de trazer do banco, nessa linha aqui:

Produto produto = new Produto(rst.getInt(1), rst.getString(2), rst.getString(3), (Status) rst.getObject(4), rst.getInt(5), rst.getString(6));

Mais especificamente em ((Status) getObject(4)), pois, você salvou como um Integer no banco, ai trazer como um object ou int funciona normal, porém, ao fazer a conversão de Integer para Status é onde esta o problema, afinal são tipos incompatíveis(um é int e o outro uma enum Status), seria a mesma coisa que fazer Status x = (Status) 4;, ele vai nos dizer que são tipos incompatíveis, nem compilaria, vou te dar duas soluções possiveis, uma que você precisa mexer menos e outra que você teria que fazer algumas mudanças.

  1. Primeira solução:

Na enum você adiciona um método assim:

public enum Status {

        EM_ESTOQUE(1), INDISPONIVEL(5), ENCOMENDADO(10);

        private int valor;

        Status(int valor) {
            this.valor = valor;
        }

        public int getValor() {
            return this.valor;
        }

        //Esse metodo que vamos passar um inteiro e devolver um status
        public static Status getBy(Integer valor) {
            return Arrays.stream(Status.values()).filter(status -> status.getValor() == valor)
                    .findFirst().get();
        }
    }

E no DAO você faria assim

Produto produto = new Produto(rst.getInt(1), rst.getString(2), rst.getString(3), Status.getBy(rst.getObject(4)), rst.getInt(5), rst.getString(6));
// Aqui se eu não me engano, assim funcionaria tambem
Produto produto = new Produto(rst.getInt(1), rst.getString(2), rst.getString(3), Status.getBy(rst.getInt(4)), rst.getInt(5), rst.getString(6));
  1. A outra solução possivel seria:

Trocar o campo para String no banco, salvando assim, o valor da enum, fica mais facil de ver na hora de fazer um select diretamente no banco de dados e na hora de recuperar no java é só fazer:

Produto produto = new Produto(rst.getInt(1), rst.getString(2), rst.getString(3), Status.valueOf(rst.getString(4)), rst.getInt(5), rst.getString(6));

Espero que tenha ficado claro, qualquer duvida, só mandar :)

Olá Gabriel,

Troquei o campo para String no banco. Funcionou no DAO, com a linha:

                Produto produto = new Produto(rst.getInt(1), rst.getString(2), rst.getString(3), Status.valueOf(rst.getString(4)), rst.getInt(5), rst.getString(6));

mas agora tenho um botão Salvar onde recebo valores do grid, status é digitado não é um comboBox e também tem problemas de cast Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: class java.lang.String cannot be cast to class br.com.alura.jdbc.modelo.Status (java.lang.String is in module java.base of loader 'bootstrap'; br.com.alura.jdbc.modelo.Status is in unnamed module of loader 'app')

    private void alterar() throws SQLException {
        Object objetoDaLinha = (Object) modelo.getValueAt(tabela.getSelectedRow(), tabela.getSelectedColumn());
        if (objetoDaLinha instanceof Integer) {
            Integer id = (Integer) objetoDaLinha;
            String nomeStr = (String) modelo.getValueAt(tabela.getSelectedRow(), 1);
            String nome = nomeStr.toUpperCase();
            String descricao = (String) modelo.getValueAt(tabela.getSelectedRow(), 2);
            Status status =  (Status) modelo.getValueAt(tabela.getSelectedRow(), 3);
            Integer categoriaId =  Integer.parseInt(modelo.getValueAt(tabela.getSelectedRow(), 4).toString());
            // abaixo a variável status já enum Status.
            this.produtoController.alterar(nome, descricao, status, id, categoriaId, null);
            JOptionPane.showMessageDialog(this, "Item alterado com sucesso!");
        } else {
            JOptionPane.showMessageDialog(this, "Por favor, selecionar o ID");
        }
    }

obrigado pela ajuda.

abraço :)

Opa Ricardo, fico muito feliz em ajudar!

Essa parte, seria o mesmo esquema:

Status status = Status.valueOf(modelo.getValueAt(tabela.getSelectedRow(), 3));

Ai vai funcionar e o motivo é o mesmo, porque ele não consegue converter sozinho de String para Status, dessa forma deve funcionar tudo certinho!

Me avisa se funcionar, estarei aguardando :)

eu tentei antes mas nem compila, o erro nesta linha é: The method valueOf(String) in the type Status is not applicable for the arguments (Object).

Opa Ricardo, falha minha.

Faz então

Status.valueOf((String) modelo.getValueAt(tabela.getSelectedRow(), 3));

Assim convertemos de Object para String e depois para o nosso Status.

Olá Gabriel,

funcionou, em parte... rsrsrs

olha só, quando eu altero a coluna status no grid funciona bem mas quando eu altero apenas outro(s) campo(s) do grid sem alterar o status dá o seguinte erro:

Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: class br.com.alura.jdbc.modelo.Status cannot be cast to class java.lang.String (br.com.alura.jdbc.modelo.Status is in unnamed module of loader 'app'; java.lang.String is in module java.base of loader 'bootstrap') at br.com.alura.jdbc.view.ProdutoCategoriaFrame2.alterar(ProdutoCategoriaFrame2.java:267)

segue o tercho de código do ProdutoCategoriaFrame2:

    private void alterar() throws SQLException {
        Object objetoDaLinha = (Object) modelo.getValueAt(tabela.getSelectedRow(), tabela.getSelectedColumn());
        if (objetoDaLinha instanceof Integer) {
            Integer id = (Integer) objetoDaLinha;
            String nomeStr = (String) modelo.getValueAt(tabela.getSelectedRow(), 1);
            String nome = nomeStr.toUpperCase();
            String descricao = (String) modelo.getValueAt(tabela.getSelectedRow(), 2);
            Status status = Status.valueOf((String) modelo.getValueAt(tabela.getSelectedRow(), 3));
            Integer categoriaId =  Integer.parseInt(modelo.getValueAt(tabela.getSelectedRow(), 4).toString());
            this.produtoController.alterar(nome, descricao, status, id, categoriaId, null);
            JOptionPane.showMessageDialog(this, "Item alterado com sucesso!");
        } else {
            JOptionPane.showMessageDialog(this, "Por favor, selecionar o ID");
        }
    }

o enum:

package br.com.alura.jdbc.modelo;

public enum Status {

    EM_ESTOQUE, INDISPONIVEL, ENCOMENDADO; 

}

o DAO:

    public void alterar(String nome, String descricao, Status status, Integer id, Integer categoriaId, String categoriaNome) throws SQLException {
        try (PreparedStatement stm = connection.prepareStatement("UPDATE PRODUTO SET NOME = ?, DESCRICAO = ?, STATUS=?, CATEGORIA_ID = ? WHERE ID = ?")) {
            stm.setString(1, nome);
            stm.setString(2, descricao);
            stm.setString(3, status.name().toString());
            stm.setInt(4, categoriaId);
            stm.setInt(5, id);
            stm.execute();
            connection.commit(); // controle da transação
        } 
        catch (SQLException e) {
            e.printStackTrace();
            connection.rollback();
            throw new RuntimeException(e);
        }
    }

e a tela: Insira aqui a descrição dessa imagem para ajudar na acessibilidade

Bizarro, parece que quando você altera, ele já esta reconhecendo que seria uma enum.... poderia me mandar o projeto? git, drive algo do tipo?

Põe bizarro nisso! E tem mais, se você trocar o comando por este abaixo, inverte tudo, ou seja, quando eu altero a coluna status no grid não funciona, dá o mesmo erro mas quando eu altero apenas outro(s) campo(s) do grid sem alterar o status funciona!

        Status status = (Status) modelo.getValueAt(tabela.getSelectedRow(), 3);

segue o link do projeto:

https://drive.google.com/drive/folders/1b5Fgd81xosHvF3SfMJPybVzHmZVBSD-t?usp=sharing

Opa, dando uma olhada aqui, eu chuto que você não precisa fazer isso daqui no ProdutoCategoriaFrame2.java, no metodo preencherTabela() na linha 295:


modelo.addRow(new Object[] { produto.getId(), produto.getNome(), produto.getDescricao(), Status.valueOf(produto.getStatus().name()), produto.getCategoriaId(), produto.getCategoria() });

Porque nesse ponto ele está adicionando o Status como uma enum, e na real, na view pelo menos, podemos lidar como ele sendo uma String, então o código ficaria:


modelo.addRow(new Object[] { produto.getId(), produto.getNome(), produto.getDescricao(), produto.getStatus().name(), produto.getCategoriaId(), produto.getCategoria() });

Opa! Agora sim, funcionou! Valeu!!!!

E aproveitando a ocasião, sabes como transformar a coluna Status no grid em um ComboBox para alteração na classe ProdutoCategoriaFrame2 ?

Então eu nunca mexi muito com JOptionPane, mas pelo que pesquisei caso você fizer algo desse tipo aqui na hora de montar a tabela:

        //ESTE É O CODIGO QUE MONTA A CEDULA CLICAVEL
        TableColumn cedula = tabela.getColumnModel().getColumn(3);
        JComboBox<String> comboBox = new JComboBox<>();
        Arrays.asList(Status.values()).forEach(s -> comboBox.addItem(s.name()));
        cedula.setCellEditor(new DefaultCellEditor(comboBox));

Fiz um código aqui com as coisas que entendi, para tentar te mostrar e você ter uma ideia de como ficaria:


public class TestaComboBox {
    public static void main(String[] args) {
        //MONTAR A TABELA
        JTable tabela = new JTable(5, 5);
        Font font = new Font("Verdana", Font.PLAIN, 12);
        tabela.setFont(font);
        tabela.setRowHeight(30);
        tabela.setBackground(Color.orange);
        tabela.setForeground(Color.white);

        //CRIA A COLUNA CLICAVEL
        TableColumn cedula = tabela.getColumnModel().getColumn(0);

        JComboBox<String> comboBox = new JComboBox<>();
        Arrays.stream(Status.values()).forEach(stat -> comboBox.addItem(stat.name()));

        cedula.setCellEditor(new DefaultCellEditor(comboBox));

        //MONTA A JANELA QUE IRA ABRIR
        JFrame frame = new JFrame();
        frame.setSize(800, 400);
        frame.add(new JScrollPane(tabela));
        frame.setVisible(true);
    }

    public enum Status {
        EM_ESTOQUE, INDISPONIVEL, ENCOMENDADO;
    }
}

Roda esse código, tenta entender o que eu fiz e tenta adicionar em seu projeto, você já tem a tabela e o frame, basicamente seria acrescentar a parte que monta a cédula com as opções de acordo com a Enum.

Tem um guia, na documentação da oracle que é show tambem.

Espero ter ajudado, fico feliz que tenha conseguido! :)

Olá Gabriel, implementei as sugestões que me passaste para transformar a coluna Status(enum) em ComboBox e consegui, funcionou tudo certinho.

Aí, pensei, vou aproveitar tudo que aprendi e vou fazer o mesmo com a coluna Nome da Categoria, baseada na tabela Categoria.

Aconteceu o mesmo erro que com Status, quando clico/altero a coluna Nome da Categoria e não altero as outras colunas funciona mas quando altero outra coluna e não altero a coluna Nome da Categoria dá o erro:

Exception in thread "AWT-EventQueue-0" java.lang.ClassCastException: class java.lang.String cannot be cast to class br.com.alura.jdbc.modelo.Categoria (java.lang.String is in module java.base of loader 'bootstrap'; br.com.alura.jdbc.modelo.Categoria is in unnamed module of loader 'app') at br.com.alura.jdbc.view.ProdutoCategoriaFrame2.alterar(ProdutoCategoriaFrame2.java:270)

mas como é difícil esse negócio do cast... hehehe!

segue o link do projeto já alterado.

abração e obrigado pela ajuda.

solução!

Opa Ricardo, a categoria é um pouco diferente...

Você tem algumas opções nesse caso, vou te dar a mais simples e explicar as outras.

Em ProdutoCategoriaFrame2, no método prencherTabela(), na linha 301, você pode alterar o código para:

    private void preencherTabela() {
        //ESTE É O CODIGO QUE MONTA A CEDULA CLICAVEL DO STATUS NO GRID
        TableColumn cedula = tabela.getColumnModel().getColumn(3);
        JComboBox<String> comboBox = new JComboBox<>();
        Arrays.asList(Status.values()).forEach(s -> comboBox.addItem(s.name()));
        cedula.setCellEditor(new DefaultCellEditor(comboBox));

        //ESTE É O CODIGO QUE MONTA A CEDULA CLICAVEL DA CATEGORIA NO GRID       
        TableColumn cedula2 = tabela.getColumnModel().getColumn(4);
        comboGridCategoria = new JComboBox<Categoria>();
        List<Categoria> categorias = this.listarCategoria();
        for (Categoria categoria : categorias) {
            comboGridCategoria.addItem(categoria);
        }
        cedula2.setCellEditor(new DefaultCellEditor(comboGridCategoria));

        List<Produto> produtos = listarProduto();
        try {
            for (Produto produto : produtos) {
                //Adicionei essa linha para carregar a categoria ou você pode carregar do BD
                Categoria categoriaEncontrada = categorias.stream().filter(categoria -> produto.getCategoriaNome().equalsIgnoreCase(categoria.getNome())).findFirst().get();
                modelo.addRow(new Object[] { produto.getId(), produto.getNome(), produto.getDescricao(), produto.getStatus().name(), categoriaEncontrada });
            }
        } catch (Exception e) {
            throw e;
        }    
    }

Isso deve fazer funcionar já, tem algumas outras soluções, mas acho que seria a menos dolorida de fazer.

Espero que funcione!

Sensacional! Funcionou!

Gabriel, quero te agradecer pelo apoio, pelo suporte e pela paciência!

Valeu muito!

abraço.