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)
22
respostas

Dúvida no Ex. 1 da Aula 8 - Injeção de dependências

Após alteração da aula o listaContas funciona normalmente mas dá erro ao tentar remover uma conta. retorna uma SQLException dizendo que a conexão esta fechada!

22 respostas

Walter, adicione a anotação @Transactional em cima da classe ou do método, e veja se resolve a exception.

Bom dia Douglas,

continua dando o mesmo erro ao remover uma conta no listacontas:

"Request processing failed; nested exception is java.lang.RuntimeException: java.sql.SQLException: Connection is closed."

Percebe-se que em todos os métodos da classe ContasDAO a conexão é fechada após a execução:

connection.close();

Isto realmente é necessário? O Spring não controla isso automaticamente?

Bom dia Walter, você pode postar o código?

Oi Douglas, segue o código completo da minha classe:

@Repository @Transactional public class ContaDAO { private Connection connection;

@Autowired public ContaDAO(DataSource con) { try { this.connection = con.getConnection(); } catch (SQLException e) { throw new RuntimeException(e); }

/ try { this.connection = new ConnectionFactory().getConnection(); } catch (SQLException e) { throw new RuntimeException(e); } /

}

public void adiciona(Conta conta) { String sql = "insert into contas (descricao, paga, valor, tipo) values (?,?,?,?)"; PreparedStatement stmt; try { stmt = connection.prepareStatement(sql); stmt.setString(1, conta.getDescricao()); stmt.setBoolean(2, conta.isPaga()); stmt.setDouble(3, conta.getValor()); stmt.setString(4, conta.getTipo().name()); stmt.execute(); connection.close(); } catch (SQLException e) { throw new RuntimeException(e); }

}

public void remove(Conta conta) {

if (conta.getId() == null) { throw new IllegalStateException("Id da conta naoo deve ser nula."); }

String sql = "delete from contas where id = ?"; PreparedStatement stmt; try { stmt = connection.prepareStatement(sql); stmt.setLong(1, conta.getId()); stmt.execute();

connection.close();

} catch (SQLException e) { throw new RuntimeException(e); } }

public void altera(Conta conta) { String sql = "update contas set descricao = ?, paga = ?, dataPagamento = ?, tipo = ?, valor = ? where id = ?"; PreparedStatement stmt; try { stmt = connection.prepareStatement(sql); stmt.setString(1, conta.getDescricao()); stmt.setBoolean(2, conta.isPaga()); stmt.setDate(3, conta.getDataPagamento() != null ? new Date(conta .getDataPagamento().getTimeInMillis()) : null); stmt.setString(4, conta.getTipo().name()); stmt.setDouble(5, conta.getValor()); stmt.setLong(6, conta.getId()); stmt.execute();

connection.close();

} catch (SQLException e) { throw new RuntimeException(e); } }

public List lista() { try { List contas = new ArrayList(); PreparedStatement stmt = this.connection .prepareStatement("select * from contas");

ResultSet rs = stmt.executeQuery();

while (rs.next()) { // adiciona a conta na lista contas.add(populaConta(rs)); }

rs.close(); stmt.close(); connection.close();

return contas; } catch (SQLException e) { throw new RuntimeException(e); } }

public Conta buscaPorId(Long id) {

if (id == null) { throw new IllegalStateException("Id da conta nao deve ser nula."); }

try { PreparedStatement stmt = this.connection .prepareStatement("select * from contas where id = ?"); stmt.setLong(1, id); ResultSet rs = stmt.executeQuery();

if (rs.next()) { connection.close(); return populaConta(rs); }

rs.close(); stmt.close();

connection.close(); return null; } catch (SQLException e) { throw new RuntimeException(e); } }

public void paga(Long id) {

if (id == null) { throw new IllegalStateException("Id da conta nao deve ser nula."); }

String sql = "update contas set paga = ?, dataPagamento = ? where id = ?"; PreparedStatement stmt; try { stmt = connection.prepareStatement(sql); stmt.setBoolean(1, true); stmt.setDate(2, new Date(Calendar.getInstance().getTimeInMillis())); stmt.setLong(3, id); stmt.execute();

connection.close(); } catch (SQLException e) { throw new RuntimeException(e); } }

private Conta populaConta(ResultSet rs) throws SQLException { Conta conta = new Conta();

conta.setId(rs.getLong("id")); conta.setDescricao(rs.getString("descricao")); conta.setPaga(rs.getBoolean("paga")); conta.setValor(rs.getDouble("valor"));

Date data = rs.getDate("dataPagamento"); if (data != null) { Calendar dataPagamento = Calendar.getInstance(); dataPagamento.setTime(data); conta.setDataPagamento(dataPagamento); }

conta.setTipo(Enum.valueOf(TipoDaConta.class, rs.getString("tipo")));

return conta; } }

Ao invés de fechar a conexão em cada método, tenta fechar a conexão logo após o return conta; Isso pode resolver seu problema! Se não der pra fechar depois do return contas, feche antes:

return contas; connection.close();

Walter, sobre a questão anterior, se você não quiser fechar a conexão manualmente, você pode utilizar o try with resources em intefaces que extendem ou classes que implementam a interface AutoCloseable.

Exemplo:

public void remove(Conta conta) {

        if (conta.getId() == null) {
            throw new IllegalStateException("Id da conta naoo deve ser nula.");
        }

        String sql = "delete from contas where id = ?";
        try (PreparedStatement stmt = connection.prepareStatement(sql)) {
            stmt.setLong(1, conta.getId());
            stmt.execute();

        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

Tente substituir o método lista com esse exemplo, caso não resolver, a conexão pode estar sendo fechada em algum lugar e tentando acessar o banco dados logo após.

public List<Conta> lista() {
        List<Conta> contas = new ArrayList<>();

        String sql = "select * from contas";
        try (PreparedStatement stmt = this.connection.prepareStatement(sql)) {
            try (ResultSet rs = stmt.executeQuery()) {
                while (rs.next()) {
                    contas.add(populaConta(rs));
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return contas;
    }

Amigos!

O problema na classe ContaDao é o seguinte:

Estamos passando o DataSource por injeção de dependências, no momento que instanciamos um objeto ContaDao inicializamos a nosso atributo connection que depois da execução de qualquer método da classe vai ser fechada. Para poder usar de novo o atributo connection da classe , java teria que inicializar uma nova instancia do objeto e inicializar de novo o nosso atributo , mas não sei se é por a própria JVM da JAVA ou por Spring MVC, a mesma instancia da classe esta sendo reutilizada com ao atributo já fechado.

A solução é a seguinte:

//injectamos o ds

@Autowired private DataSource ds;

private Connection connection;

//criamos um metodo para inicializar a connection caso seja NULL ou conexao fechada public void novaConexao() throws SQLException{ System.out.println(this.connection); if(this.connection == null || this.connection.isClosed()){ this.connection = this.ds.getConnection(); } }

//vamos chamar novaConexao() em cada metodo para checar a conexao, exemplo:

public void remove(Conta conta) {

if (conta.getId() == null) { throw new IllegalStateException("Id da conta naoo deve ser nula."); }

String sql = "delete from contas where id = ?"; PreparedStatement stmt; try { novaConexao(); -----------------------------------------------------> aqui o nosso novaConexao(); stmt = connection.prepareStatement(sql); stmt.setLong(1, conta.getId()); stmt.execute();

connection.close(); } catch (SQLException e) { throw new RuntimeException(e); } }

Se lembrem de implementar o novaConexao em cada método da classe. Problema resolvido. ABS!!

Boa noite, perdão pela código tudo bagunçado, segue o código da solução bonitinho:

//injectamos o ds

@Autowired private DataSource ds;

private Connection connection;

//criamos um metodo para inicializar a connection caso seja NULL ou conexao fechada

public void novaConexao() throws SQLException{ if(this.connection == null || this.connection.isClosed()){ this.connection = this.ds.getConnection(); } }

public void remove(Conta conta) {

if (conta.getId() == null) { throw new IllegalStateException("Id da conta naoo deve ser nula."); }

String sql = "delete from contas where id = ?"; PreparedStatement stmt; try { novaConexao(); ------------> aqui a chamamos nosso metodo stmt = connection.prepareStatement(sql); stmt.setLong(1, conta.getId()); stmt.execute();

connection.close(); } catch (SQLException e) { throw new RuntimeException(e); } }

Testei o código e funciona, implementando o método novaConexao() em todos os métodos da ContaDao que precisem de uma connection

ABS!

Mil desculpas :/

//injectamos o ds
@Autowired
    private DataSource ds;

    private Connection connection;

    //criamos um metodo para inicializar a connection caso seja NULL ou conexao fechada
    public void novaConexao() throws SQLException{
        if(this.connection == null || this.connection.isClosed()){
            this.connection = this.ds.getConnection();
        }
    }
public void remove(Conta conta) {

        if (conta.getId() == null) {
            throw new IllegalStateException("Id da conta naoo deve ser nula.");
        }

        String sql = "delete from contas where id = ?";
        PreparedStatement stmt;
        try {
            novaConexao(); ------> chamada ao metodo
            stmt = connection.prepareStatement(sql);
            stmt.setLong(1, conta.getId());
            stmt.execute();

            connection.close();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

Boa noite Ruben,

mesmo com as alterações sugeridas continua dando o erro: Request processing failed; nested exception is java.lang.RuntimeException: java.sql.SQLException: Connection is closed.

Segue meu código com as alterações:

@Repository public class ContaDAO {

@Autowired private DataSource ds;

private Connection connection;

@Autowired public void novaConexao() throws SQLException{ if(this.connection == null || this.connection.isClosed()){ this.connection = this.ds.getConnection(); } }

@Autowired public ContaDAO(DataSource con) { try { this.connection = con.getConnection(); } catch (SQLException e) { throw new RuntimeException(e); }

/ try { this.connection = new ConnectionFactory().getConnection(); } catch (SQLException e) { throw new RuntimeException(e); } /

}

public void adiciona(Conta conta) { String sql = "insert into contas (descricao, paga, valor, tipo) values (?,?,?,?)"; PreparedStatement stmt; try { stmt = connection.prepareStatement(sql); stmt.setString(1, conta.getDescricao()); stmt.setBoolean(2, conta.isPaga()); stmt.setDouble(3, conta.getValor()); stmt.setString(4, conta.getTipo().name()); stmt.execute(); connection.close(); } catch (SQLException e) { throw new RuntimeException(e); }

}

public void remove(Conta conta) {

if (conta.getId() == null) { throw new IllegalStateException("Id da conta naoo deve ser nula."); }

String sql = "delete from contas where id = ?"; PreparedStatement stmt; try { novaConexao(); stmt = connection.prepareStatement(sql); stmt.setLong(1, conta.getId()); stmt.execute();

connection.close();

} catch (SQLException e) { throw new RuntimeException(e); } }

Walter, vou tentar postar a minha classe inteira para ver si da certo no seu projeto daqui a uns 20 min. O método nove conexão deve ser implementado em todos os métodos da classe ContaDao.

Segue a classe:

´´´

@Repository public class ContaDAO {

@Autowired private DataSource ds;

private Connection connection;

public void novaConexao() throws SQLException{ if(this.connection == null || this.connection.isClosed()){ this.connection = this.ds.getConnection(); } }

public void adiciona(Conta conta) { String sql = "insert into contas (descricao, paga, valor, tipo) values (?,?,?,?)"; PreparedStatement stmt; try { novaConexao(); stmt = connection.prepareStatement(sql); stmt.setString(1, conta.getDescricao()); stmt.setBoolean(2, conta.isPaga()); stmt.setDouble(3, conta.getValor()); stmt.setString(4, conta.getTipo().name()); stmt.execute(); connection.close(); } catch (SQLException e) { throw new RuntimeException(e); }

}

public void remove(Conta conta) {

if (conta.getId() == null) { throw new IllegalStateException("Id da conta naoo deve ser nula."); }

String sql = "delete from contas where id = ?"; PreparedStatement stmt; try { novaConexao(); stmt = connection.prepareStatement(sql); stmt.setLong(1, conta.getId()); stmt.execute();

connection.close(); } catch (SQLException e) { throw new RuntimeException(e); } }

public void altera(Conta conta) { String sql = "update contas set descricao = ?, paga = ?, dataPagamento = ?, tipo = ?, valor = ? where id = ?"; PreparedStatement stmt; try { novaConexao(); stmt = connection.prepareStatement(sql); stmt.setString(1, conta.getDescricao()); stmt.setBoolean(2, conta.isPaga()); stmt.setDate(3, conta.getDataPagamento() != null ? new Date(conta .getDataPagamento().getTimeInMillis()) : null); stmt.setString(4, conta.getTipo().name()); stmt.setDouble(5, conta.getValor()); stmt.setLong(6, conta.getId()); stmt.execute();

connection.close();

} catch (SQLException e) { throw new RuntimeException(e); } }

public List lista() { try { novaConexao(); List contas = new ArrayList(); PreparedStatement stmt = this.connection .prepareStatement("select * from contas");

ResultSet rs = stmt.executeQuery();

while (rs.next()) { // adiciona a conta na lista contas.add(populaConta(rs)); }

rs.close(); stmt.close(); connection.close();

return contas; } catch (SQLException e) { throw new RuntimeException(e); } }

public Conta buscaPorId(Long id) { Conta conta = null; if (id == null) { throw new IllegalStateException("Id da conta nao deve ser nula."); }

try { novaConexao(); PreparedStatement stmt = this.connection .prepareStatement("select * from contas where id = ?"); stmt.setLong(1, id); ResultSet rs = stmt.executeQuery();

if (rs.next()) { conta = populaConta(rs); }

rs.close(); stmt.close();

connection.close(); return conta; } catch (SQLException e) { throw new RuntimeException(e); } }

public void paga(Long id) {

if (id == null) { throw new IllegalStateException("Id da conta nao deve ser nula."); }

String sql = "update contas set paga = ?, dataPagamento = ? where id = ?"; PreparedStatement stmt; try { novaConexao(); stmt = connection.prepareStatement(sql); stmt.setBoolean(1, true); stmt.setDate(2, new Date(Calendar.getInstance().getTimeInMillis())); stmt.setLong(3, id); stmt.execute();

connection.close(); } catch (SQLException e) { throw new RuntimeException(e); } }

private Conta populaConta(ResultSet rs) throws SQLException { Conta conta = new Conta();

conta.setId(rs.getLong("id")); conta.setDescricao(rs.getString("descricao")); conta.setPaga(rs.getBoolean("paga")); conta.setValor(rs.getDouble("valor"));

Date data = rs.getDate("dataPagamento"); if (data != null) { Calendar dataPagamento = Calendar.getInstance(); dataPagamento.setTime(data); conta.setDataPagamento(dataPagamento); }

conta.setTipo(Enum.valueOf(TipoDaConta.class, rs.getString("tipo")));

return conta; } }

´´´

solução!

Classe bonitinha, desculpas de novo sempre esqueço o metodo de formataçao:


@Repository
public class ContaDAO {

    @Autowired
    private DataSource ds;

    private Connection connection;

    public void novaConexao() throws SQLException{
        if(this.connection == null || this.connection.isClosed()){
            this.connection = this.ds.getConnection();
        }
    }

    public void adiciona(Conta conta) {
        String sql = "insert into contas (descricao, paga, valor, tipo) values (?,?,?,?)";
        PreparedStatement stmt;
        try {
            novaConexao();
            stmt = connection.prepareStatement(sql);
            stmt.setString(1, conta.getDescricao());
            stmt.setBoolean(2, conta.isPaga());
            stmt.setDouble(3, conta.getValor());
            stmt.setString(4, conta.getTipo().name());
            stmt.execute();
            connection.close();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }

    }

    public void remove(Conta conta) {

        if (conta.getId() == null) {
            throw new IllegalStateException("Id da conta naoo deve ser nula.");
        }

        String sql = "delete from contas where id = ?";
        PreparedStatement stmt;
        try {
            novaConexao();
            stmt = connection.prepareStatement(sql);
            stmt.setLong(1, conta.getId());
            stmt.execute();

            connection.close();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public void altera(Conta conta) {
        String sql = "update contas set descricao = ?, paga = ?, dataPagamento = ?, tipo = ?, valor = ? where id = ?";
        PreparedStatement stmt;
        try {
            novaConexao();
            stmt = connection.prepareStatement(sql);
            stmt.setString(1, conta.getDescricao());
            stmt.setBoolean(2, conta.isPaga());
            stmt.setDate(3, conta.getDataPagamento() != null ? new Date(conta
                    .getDataPagamento().getTimeInMillis()) : null);
            stmt.setString(4, conta.getTipo().name());
            stmt.setDouble(5, conta.getValor());
            stmt.setLong(6, conta.getId());
            stmt.execute();

            connection.close();

        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public List<Conta> lista() {
        try {
            novaConexao();
            List<Conta> contas = new ArrayList<Conta>();
            PreparedStatement stmt = this.connection
                    .prepareStatement("select * from contas");

            ResultSet rs = stmt.executeQuery();

            while (rs.next()) {
                // adiciona a conta na lista
                contas.add(populaConta(rs));
            }

            rs.close();
            stmt.close();
            connection.close();

            return contas;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public Conta buscaPorId(Long id) {
        Conta conta = null;
        if (id == null) {
            throw new IllegalStateException("Id da conta nao deve ser nula.");
        }

        try {
            novaConexao();
            PreparedStatement stmt = this.connection
                    .prepareStatement("select * from contas where id = ?");
            stmt.setLong(1, id);
            ResultSet rs = stmt.executeQuery();

            if (rs.next()) {
                conta = populaConta(rs);
            }

            rs.close();
            stmt.close();


            connection.close();
            return conta;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public void paga(Long id) {

        if (id == null) {
            throw new IllegalStateException("Id da conta nao deve ser nula.");
        }

        String sql = "update contas set paga = ?, dataPagamento = ? where id = ?";
        PreparedStatement stmt;
        try {
            novaConexao();
            stmt = connection.prepareStatement(sql);
            stmt.setBoolean(1, true);
            stmt.setDate(2, new Date(Calendar.getInstance().getTimeInMillis()));
            stmt.setLong(3, id);
            stmt.execute();

            connection.close();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    private Conta populaConta(ResultSet rs) throws SQLException {
        Conta conta = new Conta();

        conta.setId(rs.getLong("id"));
        conta.setDescricao(rs.getString("descricao"));
        conta.setPaga(rs.getBoolean("paga"));
        conta.setValor(rs.getDouble("valor"));

        Date data = rs.getDate("dataPagamento");
        if (data != null) {
            Calendar dataPagamento = Calendar.getInstance();
            dataPagamento.setTime(data);
            conta.setDataPagamento(dataPagamento);
        }

        conta.setTipo(Enum.valueOf(TipoDaConta.class, rs.getString("tipo")));

        return conta;
    }
}

Valeu Ruben!!! Obrigado! Funcionou belezinha...rs

Galera, tive o mesmo problema do Walter e entendi a solução proposta pelo Rubens, que se resume em abrir a conexão e fecha-la no termino de sua necessidade. Mas eu fico com um questionamento, ja participei de projetos com spring onde não era necessário abrir e fechar conexão . Uma vez que o Spring esta responsável por controlar o dataSource e a respectiva connection. Alguem saberia dizer como removeriamos do nosso codigo connection.close() e o metodo novaConexao() ?

Passando pra o spring container o controle total de abertura e fechamento da conexão.

Mais uma informação pra gente debater. Eu solucionei o problema [Request processing failed; nested exception is java.lang.RuntimeException: java.sql.SQLException: Connection is closed.] removendo o connection.close() dos metodos de cada DAO e mantendo a conexão aberta a aplicação se comportou normalmente.

Gostaria de opiniões sobre essas questões quem puder contribuir com conhecimento. Minha solução pode gerar algum problema para o cliclo de vida da aplicação ?

Samuel, dá uma olhada na minha resposta acima, utilizando o try with resources, a interface AutoCloseable vai fechar a conexão implicitamente para você, veja o exemplo acima.

Samuel, o problema de não fechar as conexões es que com o passo do tempo na vida da aplicação em produção, com certeza vai a estourar a connection pool, já que se abrem conexões com o banco que não estão sendo fechadas nunca. Neste caso, a gente está abrindo uma conexão na mão via JDBC, até onde eu sei, as transações abertas assim, devem ser abertas e fechadas programaticamente pelo desenvolvedor, já que o container (EJB,SPRING...) não está gerenciando as transações. Neste curso apenas fala do SPRING MVC e não do SPRING contêiner, então não fala sobre controle de transações. Acredito que com o padrão JDBC, não tem como automatizar as transações, apenas com um ORM tipo hibernate usando um servidor de aplicações com JTA. E por isso que di uma solução de acordo com o conteúdo do curso para não complicar mas a parada, e ter que mudar os códigos das classes. No curso de EJB tem soluções bacanas para controle de transações!

Douglas, tentei com a tag @transactional, e @autocloseable sem sucesso, acredito que é pela questão falada acima mas se você conseguiu algum progresso seria legal se pudesse subir a sua classe para nos ilustrar!

Abs para todos!

Blz. Rubens entendido. Vou pesquisar mais sobre o assunto. Se tratando do escopo do exercicio ja temos solução para o problema proposto.

Ruben, AutoCloseable não é uma anotação, é uma interface do java, se você utilizar o try with resources, em interfaces que extendam da interface AutoCloseable, a JVM fecha a conexão implicitamente e automaticamente para você.

Três exemplos: PreparedStatement, BufferedReader e ResultSet.

Exemplo:

 try (PreparedStatement stmt = connection.prepareStatement(sql)) {
      try (ResultSet rs = stmt.executeQuery()) {
             while (rs.next()) {
                 contas.add(populaConta(rs));
            }
       }
    } catch (SQLException e) {
        e.printStackTrace();
}

Salvou! Valeu!