Conexão usando JDBC: Singleton ou Factory?

5 respostas
F

Estou lendo o livro: Use a cabeça - Padrões de Projeto e cheguei no singleton pattern. Até então eu nunca tinha pensado ou visto uma forma diferente de fazer a conexão com o banco de dados usando JDBC (sim, eu sou noob ^^). Sempre fazia uma “Fabrica de conexões” meio que assim:

public static Connection obterConexao() {
		try {
			Class.forName("com.mysql.jdbc.Driver");
			return DriverManager.getConnection("jdbc:mysql://localhost/test", "root", "root");
		} catch (ClassNotFoundException | SQLException e) {
			System.err.println(e.getMessage());
			return null;
		}
	}

Com singleton ficou mais ou menos assim:

public class Conexao {

	private Connection conexao;

	private static volatile Conexao instanciaUnica;

	private Conexao() {

		try {
			Class.forName("com.mysql.jdbc.Driver");
			conexao = DriverManager.getConnection("jdbc:mysql://localhost/test", "root", "root");
		} catch (ClassNotFoundException | SQLException e) {
			System.err.println(e.getMessage());
		}

	}

	public static Conexao obterInstancia() {
		if (instanciaUnica == null) {
			synchronized (Conexao.class) {
				if (instanciaUnica == null) {
					instanciaUnica = new Conexao();
				}
			}
		}
		return instanciaUnica;
	}

	public Connection obterConexao() {
		if (conexao != null) {
			return conexao;
		} else {
			throw new NullPointerException("Variavel conexão não iniciada.");
		}
	}

Gostaria de saber se existe alguma diferença de eficiência? se sim, qual é a melhor é forma de fazer a conexão?

5 Respostas

V

De onde você tirou essa implementação esdrúxula de singleton? Foi desse livro?

Ela está usando um padrão que não funciona, chamado double checked locking (que é o do fazer o if antes e depois do synchronized).
Note que o Brian Goetz, criador da linguagem Java, advertiu contra esse padrão em 2001:


O ideal é deixar o if apenas dentro do bloco synchronized. Como todos os seus acessos à instanciaUnica também são feitos dentro de blocos sincronizados, não há também porque declarar a variável como volatile.

De qualquer forma, não recomendo o Singleton pois ele parte da política de que seu código deve sempre trabalhar com uma conexão abertas, quando é recomendado que se trabalhe com recursos fechados:


Fazendo da conexão em si um Singleton, sua aplicação ocupará recursos mesmo que esteja ociosa, e também poderá falhar caso o banco de dados resolva fechar a conexão por conta própria.

É melhor portanto usar uma fábrica (que pode ser singleton), mas que retorne conexões diferentes usando um cache através do DBCP ou C3P0.

Ah, outra coisa. Padrões de projeto e JDBC já deixaram de ser assunto de Java Básico. Se o seu interesse é discutir arquitetura (o padrão em si), o abra tópicos no fórum de Arquitetura. Se sua dúvida é no JDBC, abra no de Persistência.
Como esse tópico pertence aos dois casos, tanto faz, mas é importante não sair abrindo tudo em Java Básico.

F

Foi do use a cabeça mesmo velho, não sabia que era tão… ESDRÚXULA rsrsr
E como esse é um assunto bem noob (eu acho) pensei que fosse melhor postar aqui mesmo. Na próxima eu posto no lugar certo. =D
Super obrigado pelas explicações.

S

Tenho o livro “Use a Cabeça - Padrões de Projeto” e dando uma olhada no capítulo do Singleton, li que a “trava duplamente verificada” não funciona para versões 1.4 e anteriores do Java, mas funciona para a versão 1.5.

O livro também fornece outra opção para o Singleton:

public class Singleton {

  private static Singleton uniqueInstance = new Singleton();

  private Singleton() {  }

  public static Singleton getInstance() {
    return uniqueInstance;
  }
}
V

Já li outro artigo que comprova que mesmo nas versões novas essa trava é ineficiente. Ela não tem impacto significativo sobre a performance e gera complexidade desnecessária no código. Essa segunda opção é geralmente mais simples e eficiente e dispensa sincronização.

S

Se vc coloca a variável como volatile então o double IF funciona, mas vira um grande overkill porque volatile não deixa de ser uma espécie de sincronizacão. Se bem que eu tinha lido em algum lugar que volatile não faz blocking então seria bem mais rápido do que synchronized. Agora lendo isso já fiquei na dúvida, apesar de o overhead de volatile ser MENOR que o de synchronized.

Agora no caso do singleton, mesmo que a variável não seja volatile, na primeira vez que o synchronized for executado, então o valor da variável é ATUALIZADO para aquele thread. Execucoes subsequentes vão evitar o synchronized com o double IF visto que nunca mais aquele valor será alterado, isto é, o thread vai cachear o valor do singleton ad eterno, o que não é problema visto que é um singleton. :expressionless: Será então que o double IF funciona para singleton???

Essa diferenca de performance vai ser INFIMA, ou seja, em 99% dos casos melhor focar num código limpo, correto e fácil de entender.

Melhor coisa com SINGLETON é fazer que a sua aplicacao no STARTUP, antes de qualquer coisa ser executada, INICIE o valor. Daí não precisa de sincronizacao. Agora se o valor for LAZY então vai precisar de sincronizacao.

Criado 19 de fevereiro de 2012
Ultima resposta 21 de fev. de 2012
Respostas 5
Participantes 4