O que vcs acham deste singleton? ta correto?

26 respostas
arthurminarini

pessoal o que vcs acham deste singleton que eu fiz pra poder simplificar minhas execuções sql? ta funcionando corretamente e nao cria mais de uma conexão no banco

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/**
 *
 * @author Minarini
 */
public class SingletonMysql {

    Connection con;

    private static SingletonMysql singleton;

    private String url;
    private String user;
    private String pass;
    private String driverName = "com.mysql.jdbc.Driver";

    private SingletonMysql() throws ClassNotFoundException, SQLException {
        url  = "jdbc:mysql://ip/banco";
        user = "user";
        pass = "senha";
        Class.forName(driverName);
        con = (Connection) DriverManager.getConnection(url, user, pass);        
    }

    /**
     *
     * @return
     * @throws SQLException
     * @throws ClassNotFoundException
     */
    public static SingletonMysql getInstance() throws SQLException, ClassNotFoundException {
        if (singleton == null) {
            singleton = new SingletonMysql();
        }
        return singleton;
    }

    /**
     *
     * @param sql
     * @return
     * @throws SQLException
     * @throws ClassNotFoundException
     */
    public static ResultSet executarSQL(String sql) throws SQLException, ClassNotFoundException {
        SingletonMysql stance = SingletonMysql.getInstance();
        Connection con = stance.con;
        Statement stm = (Statement) con.createStatement();
        ResultSet rs = (ResultSet) stm.executeQuery(sql);
        return rs;
    }
}

para usar foi simples

List<Vector> listaRetorno = new ArrayList<Vector>();
            String sql= "select * from pessoa order by nome";
            ResultSet rs = SingletonMysql.executarSQL(sql);
            while (rs.next()) {
                Vector v = new Vector();
                v.add(rs.getString("nome"));
                listaRetorno.add(v);
            }
            rs.close();
 return listaRetorno;

reparem que os statments ficam abertos. sera que quando eu fecho meus resultset e minha conexao, isso é fechado tbm?

26 Respostas

ivandasilva

Pow cara usar Vector é mal hein…

O singleton parece estar certo, mas eu acho que essas configurações do BD vc poderia colocar em um .properties, fica mais profissional…

B

Se por acaso precisasse utilizá-la em uma aplicação web, em vez de abrir uma conexão(como você fez no construtor), você teria que recuperá-la do pool. Sendo assim, a referência estática de SingletonMysql dentro da sua classe não poderia mais ser estática, visto que ela seria “enxergada” por todas as instâncias que estivessem acessando a aplicação e, consequentemente, todas utilizariam a mesma referência para executar operações com o banco. Se em algum momento uma das instâncias devolver a conexão para o pool, as outras que estiverem utilizando-a para consultas e outras operações com o banco ficarão sem conexão, causando uma SQLException : Conexão fechada!!!

^^

gomesrod

Olá,

A intenção de reaproveitar a conexão para melhorar a performance é até boa, mas tem dois problemas:

  1. A conexão acontece uma so vez na inicialização do aplicativo, se ela der uma falhada já era, você não consegue fazer mais nada a não ser que saia do programa e comece tudo de novo.

  2. Se o aplicativo for Web o impacto é maior ainda, pois além do item anterior ser mais crítico (tem que derrubar a aplicação para todo mundo) vai dar os erros mais estranhos quando mais de uma pessoa estiver usando ao mesmo tempo - pois a conexão é uma só.

E agora o principal:

Há algum tempo atrás tive uma dúvida igualzinha a sua:
http://www.guj.com.br/posts/list/62048.java

Neste tópico aprendi um monte de coisas sobre Singleton, e uma dessas coisas é que o Singleton não é o padrão apropriado quando se tem a intenção de reaproveitar um objeto por motivo de performance ou economia de recursos. Dá uma passada lá no tópico, será bastante esclarecedor.

M

Apenas um detalhe a acrescentar…

Você poderia adicionar synchronized ao método getInstance()…

Dessa forma você não corre o risco de múltiplas threads gerarem diferentes instâncias para seu Singleton, o que seria um contra-censo.

Se você gosta de estudar mais a fundo os design patterns… dê uma pesquisada a respeito do comportamento de um singleton em múltiplos class loaders…

Precisei estudar sobre isso para corrigir uma funcionalidade no projeto no qual trabalho!

[]s

ViniGodoy

Um jeito ainda melhor é criar o Singleton diretamente:

private static SingletonMysql singleton = new SingletonMysql();

E fazer o método getInstance() só retornar a variável:

public static SingletonMysql getInstance() throws SQLException, ClassNotFoundException { return singleton; }

É tão eficiente quanto, garantido contra código multi-threaded, e mais simples.

Adolpho_Alves

O singleton é muito bacana, o objetivo é criar um obj único para toda a aplicação. Como o ViniGodoy explicou ta perfeito, porem eu já ouvi falar que dá para burlar isso estendendo a classe, isso possibilitaria vc pegar outra lista. Um final na declaração da classe impediria isso. Um experimento bacana é testar sem o final e ver se vc consegue duas conexções para o msm banco, se sim colocar o final e proteger ela contra esteções. Alem de aprimorar seus conhecimentos.

Eu acho possível que msm estendendo a lista permaneça a mesma, já que uma variável estática é uma variável de classe e não de instância.

public final class SingletonMysql

É uma brincadeira legal.

ViniGodoy

O final não faz a menor diferença, pq o Singleton tem um construtor privado. E isso impede herança sobre o Singleton.
Pode-se burlar um getInstance() usando lazy-loading (diferente do que expliquei), quando usa-se múltiplas threads, caso seu getInstance() não seja sincronizado.

Na verdade, é muitíssíssimo raro sua aplicação precisar de um Singleton. E guardar a conexão MySQL não é uma dessas razões.

arthurminarini

Um jeito ainda melhor é criar o Singleton diretamente:

private static SingletonMysql singleton = new SingletonMysql();

E fazer o método getInstance() só retornar a variável:

public static SingletonMysql getInstance() throws SQLException, ClassNotFoundException { return singleton; }

É tão eficiente quanto, garantido contra código multi-threaded, e mais simples.

criar diretamente onde? no contrutor? se for onde ficam atributos não dá pois da erro
must be caught or declared to be thrown

parece que eu não posso continuar com esse meu contrutor por isso que ele esta apitando

arthurminarini

ivandasilva:
Pow cara usar Vector é mal hein…

O singleton parece estar certo, mas eu acho que essas configurações do BD vc poderia colocar em um .properties, fica mais profissional…

o que vc recomenda ale do vector, pois uso dwr e fazendo um list de vector me retorno um json

arthurminarini

biro:
Se por acaso precisasse utilizá-la em uma aplicação web, em vez de abrir uma conexão(como você fez no construtor), você teria que recuperá-la do pool. Sendo assim, a referência estática de SingletonMysql dentro da sua classe não poderia mais ser estática, visto que ela seria “enxergada” por todas as instâncias que estivessem acessando a aplicação e, consequentemente, todas utilizariam a mesma referência para executar operações com o banco. Se em algum momento uma das instâncias devolver a conexão para o pool, as outras que estiverem utilizando-a para consultas e outras operações com o banco ficarão sem conexão, causando uma SQLException : Conexão fechada!!!

^^

opa eu estou em uma aplicação web com hibernate no sqlserver, contruir um singleton para consulta em uma base mysql simples
minha aplicação não usa este banco para dados pesados. o hibernate eu tenho um poll no glassfish mas para o mysql não tenho

arthurminarini

gomesrod:
Olá,

A intenção de reaproveitar a conexão para melhorar a performance é até boa, mas tem dois problemas:

  1. A conexão acontece uma so vez na inicialização do aplicativo, se ela der uma falhada já era, você não consegue fazer mais nada a não ser que saia do programa e comece tudo de novo.

  2. Se o aplicativo for Web o impacto é maior ainda, pois além do item anterior ser mais crítico (tem que derrubar a aplicação para todo mundo) vai dar os erros mais estranhos quando mais de uma pessoa estiver usando ao mesmo tempo - pois a conexão é uma só.

E agora o principal:

Há algum tempo atrás tive uma dúvida igualzinha a sua:
http://www.guj.com.br/posts/list/62048.java

Neste tópico aprendi um monte de coisas sobre Singleton, e uma dessas coisas é que o Singleton não é o padrão apropriado quando se tem a intenção de reaproveitar um objeto por motivo de performance ou economia de recursos. Dá uma passada lá no tópico, será bastante esclarecedor.

e se no meu getstance eu verificas se tem conexao caso nao eu crio uma nova o qeu vc acha

ViniGodoy

arthurminarini:
ivandasilva:
Pow cara usar Vector é mal hein…

O singleton parece estar certo, mas eu acho que essas configurações do BD vc poderia colocar em um .properties, fica mais profissional…

o que vc recomenda ale do vector, pois uso dwr e fazendo um list de vector me retorno um json

Sim, no construtor. Você pode fazer lá um try…catch e disparar essas exceptions dentro de uma RuntimeException:

private SingletonMysql() {  
   url  = "jdbc:mysql://ip/banco";  
   user = "user";  
   pass = "senha";  
   try {
      Class.forName(driverName);  
      con = (Connection) DriverManager.getConnection(url, user, pass);          
   } catch (Exception e) {
      throw new RuntimeException("Impossível criar conexão", e);
   }
}
ViniGodoy

No lugar do Vector, use o ArrayList:
http://www.guj.com.br/posts/list/74068.java

ViniGodoy

O ideal é que:

a) Você trabalhe com um Pool de Conexões (veja o C3P0 ou o Jakarta DBCP);
b) Você feche ResultSets, Statements e Conexões por padrão (o pool irá evitar o fechamento imediato, mas não manterá conexões ociosas para sempre);

Se quiser facilitar a manipulação disso, dê uma olhada nas classes de BD do Spring. Elas facilitam muito o uso de BD, mas mantém uma sintaxe bem parecida com a do Java. Por padrão, o Spring lançara RuntimeExceptions (evitando esses try e throws todos), além de fechar automaticamente statements e resultsets.

Ou então, parta para um hibernate diretamente.

arthurminarini

No lugar do Vector, use o ArrayList:
http://www.guj.com.br/posts/list/74068.java

como eu faria um list de array então?

arthurminarini

esta assism o singleton

public final class SingletonMysql {

    private Connection con;
    private static SingletonMysql singleton;

    public SingletonMysql() throws ClassNotFoundException, SQLException {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            this.con = (Connection) DriverManager.getConnection("jdbc:mysql://ip/banco", "user", "senha");
        } catch (Exception e) {
            throw new RuntimeException("Impossível criar conexão", e);
        }
    }

    public static synchronized SingletonMysql getInstance() throws ClassNotFoundException, SQLException {
        if (singleton == null) {
            singleton = new SingletonMysql();
        }
        return singleton;
    }

    public static ResultSet executarSQL(String sql) throws ClassNotFoundException, SQLException {
        Connection con = SingletonMysql.getInstance().con;
        Statement stm = (Statement) con.createStatement();
        ResultSet rs = (ResultSet) stm.executeQuery(sql);
        return rs;
    }
}

mas estou com apenas um problema que foi citado por um dos companheiros. Se a conexão falhar ja era. como posso fazer um adaptação para conectar em caso de falha na conexão durante o dia ou um periodo de tempo?

ViniGodoy

Agora que vc captura a exception e lança um Runtime no lugar, pode tirar fora o throws do seu construtor. Aliás, evite ficar dando "throws" cegamente em suas exceptions. Pense em como trata-las corretamente.

Além disso, é imprescindível fechar Statements e o ResultSet o mais rápido possível.

Finalmente, se você vai fazer um método dentro da própria classe do Singleton, não é obrigado a ficar chamando SingletonMysql.getInstance(). Use a propriedade interna diretamente.

public final class SingletonMysql {  
   private Connection con;  
   private static SingletonMysql singleton = new SingletonMysql();  

   public SingletonMysql() {  
      try {  
         Class.forName("com.mysql.jdbc.Driver");  
         this.con = (Connection) DriverManager.getConnection("jdbc:mysql://ip/banco", "user", "senha");  
      } catch (Exception e) {  
         throw new RuntimeException("Impossível criar conexão", e);  
      }  
   }  

   public static SingletonMysql getInstance() {  
      return singleton;  
   }  
  
   public static CachedRowSet executarSQL(String sql) {  
      try {
         Statement stm = null;
         ResultSet rs = null;

         try {
            stm = con.createStatement();  
            rs = stm.executeQuery(sql);  
            CachedRowSet rowset = new CachedRowSetImpl();
            rowset.populate(rs);
            return rowset;  
         } finally {
            if (stm != null) stm.close();
            if (rs != null) rs.close();
         }
      } catch (SQLException e) {
         throw new RuntimeException("Impossível executar consulta: " + sql, e);
      }
   }  
}
arthurminarini

agora parece que ta bom. pelo menos nao tem nada aberto mais o que vc acha

public final class SingletonMysql {

    private static SingletonMysql singleton = new SingletonMysql();

    private SingletonMysql() {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (Exception e) {
            throw new RuntimeException("Driver de conexão não encontrado", e);
        }
    }

    public static SingletonMysql getSingleton() {
        return singleton;
    }

    public static CachedRowSet executarSQL(String sql) {
        try {
            Connection con = (Connection) DriverManager.getConnection("jdbc:mysql://ip/banco", "user", "senha");
            try {
                Statement stm = null;
                ResultSet rs = null;
                try {
                    stm = con.createStatement();
                    rs = stm.executeQuery(sql);
                    CachedRowSet rowset = new CachedRowSetImpl();
                    rowset.populate(rs);
                    return rowset;
                } finally {
                    if (stm != null) {
                        stm.close();
                    }
                    if (rs != null) {
                        rs.close();
                    }
                    if (con != null) {
                        con.close();
                    }
                }
            } catch (SQLException e) {
                throw new RuntimeException("Impossível executar consulta: " + sql, e);
            }
        } catch (Exception e) {
            throw new RuntimeException("Impossível criar conexão", e);
        }
    }
}
arthurminarini

ow vou ter que tirar o CachedRowSet ! ele é deprecated. não posso arrirscar de colocar no meu sistema pois futuramente ele será removido :shock:

tenho que fazer algo com o ResultSet mesmo mas tendo alguma forma de fechar igual ao codigo acima, com o resultset não deixa, ele só funciona se a conexao estiver aberta isso é ruim pra mim. pois vou ficar com uma conexao aberta atoa ate o fim dela

Andre_Fonseca

arthurminarini:
ow vou ter que tirar o CachedRowSet ! ele é deprecated. não posso arrirscar de colocar no meu sistema pois futuramente ele será removido :shock:

tenho que fazer algo com o ResultSet mesmo mas tendo alguma forma de fechar igual ao codigo acima, com o resultset não deixa, ele só funciona se a conexao estiver aberta isso é ruim pra mim. pois vou ficar com uma conexao aberta atoa ate o fim dela

o fato de ser deprecated não quer dizer que vai ser removido, existe a questão da compatibilidade… mas sim que este código nao vai mais sofrer correções e melhorias…

ViniGodoy

arthurminarini:
ow vou ter que tirar o CachedRowSet ! ele é deprecated. não posso arrirscar de colocar no meu sistema pois futuramente ele será removido :shock:

tenho que fazer algo com o ResultSet mesmo mas tendo alguma forma de fechar igual ao codigo acima, com o resultset não deixa, ele só funciona se a conexao estiver aberta isso é ruim pra mim. pois vou ficar com uma conexao aberta atoa ate o fim dela

Sugestão mesmo? Use o Spring. O código fica quase igual, mas sem esse monte de try…catch. E vc ainda coloca um conection pool integrado.

B

Em relação aos Statements ou PrepareStatements, acho importante que eles sejam fechados,
se você não fecha um statement um cursor fica aberto no seu banco de dados…
Já tive alguns problemas de inserções e updates feitos ao mesmo tempo e várias vezes na aplicação sem fechar
os statements, consumindo todos os cursores que estavam disponíveis no Oracle…

arthurminarini

André Fonseca:
arthurminarini:
ow vou ter que tirar o CachedRowSet ! ele é deprecated. não posso arrirscar de colocar no meu sistema pois futuramente ele será removido :shock:

tenho que fazer algo com o ResultSet mesmo mas tendo alguma forma de fechar igual ao codigo acima, com o resultset não deixa, ele só funciona se a conexao estiver aberta isso é ruim pra mim. pois vou ficar com uma conexao aberta atoa ate o fim dela

o fato de ser deprecated não quer dizer que vai ser removido, existe a questão da compatibilidade… mas sim que este código nao vai mais sofrer correções e melhorias…

onde posso achar uma fonte sobre isso? o que eu ja vi ate hj era que ia ser removida em alguma versão futura

arthurminarini

veja

warning: com.sun.rowset.CachedRowSetImpl is Sun proprietary API and may be removed in a future release

Andre_Fonseca

http://java.sun.com/j2se/1.4.2/docs/guide/misc/deprecation/deprecation.html

ViniGodoy

Se você não quer usar o CachedRowSetImpl (esse warning está desde o Java 2), então procure o Spring, que já comentei, ou use uma API de persistência, como o Hibernate. Outra alternativa é fazer você mesmo uma classe que faça a leitura integral do ResultSet e transforme tudo em objetos, garantindo o fechamento de tudo ao final.

O que não pode fazer é deixar coisas abertas. Isso provocará o estouro da sua aplicação, cedo ou tarde.

Só para comentar, o CachedRowSetImpl é a maneira descrita no próprio Javadoc da interface CachedRowSet:
http://java.sun.com/javase/6/docs/api/javax/sql/rowset/CachedRowSet.html

Por isso, acho pouco provável que ele desapareça no futuro.

Criado 10 de dezembro de 2009
Ultima resposta 14 de dez. de 2009
Respostas 26
Participantes 9