2008-11-14 15 views
17

Je dois tester une connexion JDBC à une base de données. Le code Java pour le faire doit être aussi simple que:Comment utiliser un pilote JDBC à partir d'un emplacement arbitraire

DriverManager.getConnection("jdbc connection URL", "username", "password"); 

Le gestionnaire de pilote rechercher le pilote approprié pour le l'URL de connexion donnée. Cependant, j'ai besoin de pouvoir charger le pilote JDBC (jar) au moment de l'exécution. I.e Je n'ai pas le pilote JDBC sur le chemin de classe de l'application Java qui exécute l'extrait de code ci-dessus.

Je peux charger le pilote en utilisant ce code, par exemple:

URLClassLoader classLoader = new URLClassLoader(new URL[]{"jar URL"}, this.getClass().getClassLoader()); 
Driver driver = (Driver) Class.forName("jdbc driver class name", true, classLoader).newInstance(); 

Mais le gestionnaire de pilotes encore ne ramasser que je ne peux pas dire que ce qui classloader à utiliser. J'ai essayé de définir le classloader de contexte du thread actuel et cela ne fonctionne toujours pas.

Quelqu'un a une idée sur la meilleure façon d'y parvenir?

+0

Y a-t-il une bonne raison pour laquelle vous n'avez pas le pilote sur le classpath? –

+0

Oui. L'application peut se connecter à de nombreuses bases de données. De plus, je ne peux pas regrouper tous les pilotes pour des raisons de licence. Ce que je veux réaliser est une boîte de dialogue simple pour que l'utilisateur charge le pot pendant que l'application fonctionne. – SaM

+0

Là, je pensais à nouveau comme un développeur côté serveur ... :) –

Répondre

17

De l'article Pick your JDBC driver at runtime; Je vais juste poster le code ici pour référence.

L'idée est de tromper le gestionnaire de pilotes en lui faisant croire que le pilote a été chargé depuis le chargeur de classe système. Pour ce faire, nous utilisons cette classe:

public class DelegatingDriver implements Driver 
{ 
    private final Driver driver; 

    public DelegatingDriver(Driver driver) 
    { 
     if (driver == null) 
     { 
      throw new IllegalArgumentException("Driver must not be null."); 
     } 
     this.driver = driver; 
    } 

    public Connection connect(String url, Properties info) throws SQLException 
    { 
     return driver.connect(url, info); 
    } 

    public boolean acceptsURL(String url) throws SQLException 
    { 
     return driver.acceptsURL(url); 
    } 

    public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException 
    { 
     return driver.getPropertyInfo(url, info); 
    } 

    public int getMajorVersion() 
    { 
     return driver.getMajorVersion(); 
    } 

    public int getMinorVersion() 
    { 
     return driver.getMinorVersion(); 
    } 

    public boolean jdbcCompliant() 
    { 
     return driver.jdbcCompliant(); 
    } 
} 

De cette façon, le pilote que vous enregistrez est de type DelegatingDriver qui est chargé avec le système classloader. Il vous suffit maintenant de charger le pilote que vous voulez vraiment utiliser en utilisant n'importe quel chargeur de classe que vous voulez. Par exemple:

URLClassLoader classLoader = new URLClassLoader(new URL[]{"path to my jdbc driver jar"}, this.getClass().getClassLoader()); 
Driver driver = (Driver) Class.forName("org.postgresql.Driver", true, classLoader).newInstance(); 
DriverManager.registerDriver(new DelegatingDriver(driver)); // register using the Delegating Driver 

DriverManager.getDriver("jdbc:postgresql://host/db"); // checks that the driver is found 
5

Le problème est DriverManager effectue « des tâches en utilisant le chargeur de classe appelant immédiat l'instance ». Voir la ligne directrice 6-3 de Secure Coding Guidelines for the Java Programming Language, version 2.0. Le chargeur de classe système n'est en aucun cas spécial dans ce cas. Juste pour coups de pied, j'ai écrit un blog entry sur ce sujet il y a un certain temps. Ma solution, bien que plus compliquée alors Nick Sayer's solution, est plus complète et fonctionne même à partir de code non fiable. Notez également que URLClassLoader.newInstance est préféré à new URLClassLoader.