Por que utilizar Class.forName ao conectar com o banco de dados? – java jdbc

Pergunta:


Para a conexão com banco de dados, além da inclusão do driver JDBC, a maioria dos artigos e exemplos utilizam o método estático Class.forName(String).
Veja o exemplo com o MySQL:

Class.forName("com.mysql.jdbc.Driver");

ou até

Class.forName("com.mysql.jdbc.Driver").newInstance();

Algumas citações:

O que faz o Class.forName(String) e o newInstance()? Não é possível importar o pacote com.mysql.jdbc e instanciar um objeto Driver para a conexão, por exemplo?

Autor da pergunta vnbrs

Resposta Victor Stafusa:

TL;DR

  • Não é necssário usar-se Class.forName(String) a partir do Java 6, mas fazê-lo mesmo assim pode fazer o seu programa recusar-se a ser inicializado caso o driver não estiver no classpath.

  • Não existe e nem nunca existiu necessidade em utilizar-se o newInstance() nesse caso. Isso não serve para absolutamente nada.

  • Você pode importar e instanciar o driver diretamente se quiser sem problemas.


Seguem as explicações detalhadas:

O Class.forName(String) é realmente necessário?

Vou copiar um trecho desta minha outra resposta:

O processo de inicialização do driver, usando o Class.forName não é mais necessário nas versões mais recentes do Java. Entretanto, ao fazê-lo assim mesmo, você denuncia logo de cara a presença de eventuais erros de classpath. Além disso, ele só precisa ser feito uma vez, durante o carregamento do programa, e se falhar, o programa está irremediavelmente quebrado. Assim sendo, esse processo pode ser mantido em um bloco de inicialização estático.

Esse mecanismo era responsável por garantir que a classe do driver fosse carregada. Uma vez carregada, a classe efetua o seu próprio registro como um driver SQL, passando a ser reconhecido pela classe DriverManager como tal. A partir do Java 6, você não precisa mais usá-lo.

Um pouco de reflection

Para todas as classes carregadas na JVM, existe um objeto da classe java.lang.Class correspondente. Para obter-se uma classe a partir de seu nome, você pode usar o método Class.forName(String).

O newInstance(), é o método responsável por chamar o construtor sem parâmetros de uma dada classe a partir do objeto Class correspondente. Uma vez que ele invoca o construtor, ele naturalmente retorna a instância criada. Com isso, é possível criar-se instâncias de classes a partir do objeto Class.

Note que a partir do Java 9, esse método foi tornado obsoleto porque ele não se comporta bem no caso do construtor lançar uma exceção. A alternativa é trocar newInstance() por getDeclaredConstructor().newInstance().

A classe ServiceLoader

Com o advento do ServiceLoader no Java 6, o uso explícito do Class.forName(String) para esse caso tornou-se desnecessário, pois o ServiceLoader é o mecanismo que permite enumerar-se automaticamente serviços dentro de JARs que implemente uma determinada interface e é esse mecanismo que os drivers usam para enumerar-se a si mesmos sem que o programador precise se preocupar com isso.

Por exemplo, se você abrir o seu mysql-connector-java-5.1.36.jar como se fosse um arquivo ZIP, você encontrará um arquivo chamado java.sql.Driver dentro de uma pasta chamada META-INF/services. Eis o conteúdo desse arquivo:

com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver

Quando o ServiceLoader for procurar por implementações da interface java.sql.Driver, ele vai procurar pelo arquivo META-INF/services/java.sql.Driver dentro de cada JAR, e em cada JAR onde esse arquivo for encontrado, será dele lida uma lista com o nome das implementações. Em seguida, o ServiceLoader vai carregar cada uma dessas classes e então, a medida que for carregando elas,
chamar o método newInstance(), mantendo as instâncias produzidas em um Map.

No entanto, como o ServiceLoader é utilizado internamente pela classe DriverManager, você não precisa interagir diretamente com ele (nem precisa saber que existe) e nem precisa mais efetuar o Class.forName(String) manualmente. A única coisa que você precisa fazer é usar o DriverManager diretamente:

Connection con = DriverManager.getConnection(url, usuario, senha);

O que a classe do Driver faz afinal?

Vamos dar uma olhada no código-fonte da classe com.mysql.jdbc.Driver:

package com.mysql.jdbc;

import java.sql.SQLException;

/**
 * The Java SQL framework allows for multiple database drivers. Each driver should supply a class that implements the Driver interface
 * 
 * <p>
 * The DriverManager will try to load as many drivers as it can find and then for any given connection request, it will ask each driver in turn to try to
 * connect to the target URL.
 * 
 * <p>
 * It is strongly recommended that each Driver class should be small and standalone so that the Driver class can be loaded and queried without bringing in vast
 * quantities of supporting code.
 * 
 * <p>
 * When a Driver class is loaded, it should create an instance of itself and register it with the DriverManager. This means that a user can load and register a
 * driver by doing Class.forName("foo.bah.Driver")
 */
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    //
    // Register ourselves with the DriverManager
    //
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }

    /**
     * Construct a new driver and register it with DriverManager
     * 
     * @throws SQLException
     *             if a database error occurs.
     */
    public Driver() throws SQLException {
        // Required for Class.forName().newInstance()
    }
}

Observe que há um bloco de inicialização estática que instancia a classe e a registra no DriverManager. Dessa forma, quando a classe é carregada, a instância necessária do driver já é criada. Ou seja, quando um Class.forName(String) for executado (seja por você ou seja indiretamente pelo ServiceLoader), ela já vai ser instanciada.

Posso chamar o newInstance() mesmo assim?

Se você quiser usar o Class.forName(String) mesmo assim, ao chamar o newInstance(), você vai criar uma outra instância que não estará registrada no DriverManager e ao não atribuir ela a lugar nenhum, será logo descartada pelo coletor de lixo. Ou seja, a chamada explícita a newInstance() é totalmente inútil e desnecessária.

Entretanto, ter várias instâncias do driver não é nocivo. De fato, uma é registrada no DriverManager pelo inicializador estático e uma segunda instância é criada pelo ServiceLoader. Se você usar o newInstance(), vai criar uma terceira instância também (ainda mais se você atribuí-la a alguma variável e utilizá-la posteriormente).

Seria de se esperar que o fato de existir mais do que uma instância do driver pudesse acarretar problemas, mas não acarreta devido a forma como ele é implementado. Se você olhar no código-fonte da superclasse de com.mysql.jdbc.Driver, que é a classe com.mysql.jdbc.NonRegisteringDriver, você verá que todos os campos são estáticos e compartilhados por quaisquer instâncias que venham a existir. Logo, se todas as instâncias compartilham o mesmo estado e não mantêm nenhum estado em si próprias, todas têm o mesmo comportamento e operam sobre os mesmos dados.

E se eu importar o driver diretamente?

Ao importar o driver diretamente, se você puder garantir que o seu código irá carregá-la, o inicializador estático dela vai ser executado e ela vai se registrar no DriverManager.

Usar uma com.mysql.jdbc.Driver.class no meio do código não é o suficiente para garantir o carregamento da classe. Mas, se você usar uma dessas coisas, aí funciona:

Class<?> x = com.mysql.jdbc.Driver.class;

Ou então:

new com.mysql.jdbc.Driver();

Ou seja, desde que você lembre-se de que pode haver várias instâncias equivalentes do driver por aí, você pode fazer isso sim e não haverá nenhum problema.

Fonte

Related Posts:

Qual a diferença entre AppCompatActivity e Activity? – android android-activity
Pergunta: Qual a diferença da AppCompatActivity para Activity ? A partir de qual versão a AppCompatActivity foi adicionada ao Android? Autor da pergunta Luhhh A diferença reside ...
Como abreviar palavras em PHP? – php string
Pergunta: Possuo informações comuns como nome de pessoas e endereços, e preciso que elas contenham no máximo 30 caracteres sem cortar palavras. Exemplo: 'Avenida Natalino João Brescansin' ...
Qual é a finalidade de um parêntese vazio numa declaração Lambda? – c# expressões-lambda característica-linguagem
Pergunta: Criei um exemplo de uma declaração Lambda sem argumentos, entretanto, estou com duvidas referente a omissão do parêntese vazio () na declaração. Veja o exemplo: class ...
Boas práticas para URI em API RESTful – api rest restful
Pergunta: Estou com dúvida em relação às URIs de alguns recursos da api que estou desenvolvendo. Tenho os recursos projetos e atividades com relação 1-N, ...
Dúvidas sobre a integração do MySQL com Java – java mysql netbeans
Pergunta: Estou criando um sistema no NetBeans, utilizando a linguagem Java e o banco de dados MySQL. Escrevi o seguinte código para realizar a conexão ...
Qual é a finalidade da pasta Model do framework Inphinit? – php inphinit
Pergunta: No Inphinit micro-framework existe a pasta Model que fica dentro da pasta application, e nela é onde ficam as classes, mas eu estou muito ...
Uso do ‘@’ em variáveis – javascript typescript coffeescript
Pergunta: Vejo em algumas linguagens que compilam para javascript, como TypeScript e CoffeeScript, o uso do @ em variáveis, como também, casos em que o ...
Qual tamanho máximo um arquivo JSON pode ter? – json arquivo
Pergunta: Vou dar um exemplo para conseguir explicar minha duvida: Preciso recuperar informação de imagens vindas de uma API, esse banco de imagens me retorna JSON's ...
O que é Teste de Regressão? – terminologia engenharia-de-software testes
Pergunta: Na matéria de Teste de Software o professor abordou um termo chamado Teste de Regressão, isto dentro da disciplina de teste de software. Sendo ...
O que é um construtor da linguagem? – php característica-linguagem
Pergunta: Em PHP, já li e ouvi várias vezes a respeito dos Construtores da Linguagem. Os casos que sempre ouvi falar deles foi em casos ...
Função intrínseca para converter numérico para string – cobol
Pergunta: Estou a tentar saber se existe alguma função intrínseca do COBOL para converter um data numérico para string sem precisar usar a cláusula REDEFINES: ( ...
Porque usar implements? – java android
Pergunta: Qual a diferença entre usar btn.setOnClickListener(new OnClickListener() { e public class MainActivity extends Activity implements OnClickListener{ Estive fazendo um curso de Android e meu professor falou que ...
O que é XHTML e quando deve ser usado? – html xml xhtml
Pergunta: O que eu sei é que o XHTML precisa ser XML válido. Isso implica, por exemplo, que todas as tags precisam ser fechadas. Por ...
Uma placa aceleradora de vídeo pode melhorar o desempenho não-gráfico? [fechada] – desempenho
Pergunta: Para desenvolver em Ruby on Rails, eu utilizo aqui uma máquina virtual do VirtualBox com Ubuntu Server 14.04 sem interface gráfica instalada. Recentemente descobri uma ...
Concat() VS Union() – c# .net
Pergunta: Qual a diferença entre Concat() e Union() ? Quando usar Concat() e quando usar Union() ? Somente pode ser usado em list ? ...

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *