Solucionado (ver solução)
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!