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!
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!
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; } }
´´´
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!