Афоризм
Хрен, положенный на мнение окружающих, обеспечивает спокойную и счастливую жизнь.
Фаина Раневская
Последние статьи

 • Активности Android
Многоэкранные Android приложения
 • Fragment dynamic
Динамическая загрузка фрагментов в Android
 • Fragment lifecycle
Жизненный цикл Fragment'ов в Android
 • Fragment example
Пример Fragment'ов в Android
 • Data Binding
Описание и пример Data Binding
 • Пример MVVM
Пример использования MVVM в Android
 • Компонент TreeTable
Описание компонента TreeTable для Swing
 • Пример TreeTable
Пример использования TreeTable
 • Хранилища Android
Внутренние и внешние хранилища данных
 • Пример SQLite
Пример использования SQLite в Android
 • WebSocket
Описание и пример реализации WebSocket
 • Визуальные компоненты
Улучшен компонент выбора даты из календаря
 • Анимация jQuery
Описание и примеры анимации элементов DOM
 • APK-файл Android
Создание apk-файла для android устройств, .dex файлы
 • платформа JaBricks
Платформа OSGi-приложения JaBricks
Поддержка проекта

Если Вам сайт понравился и помог, то будем признательны за Ваш «посильный» вклад в его поддержку и развитие
 • Yandex.Деньги
  410013796724260

 • Webmoney
  R335386147728
  Z369087728698

Динамическая загрузка JDBC-драйвера

С чем может быть связана необходимость динамической загрузки JDBC-драйвера? Причины могут быть разные. Я лучше опишу связанную с темой статьи задачу, которую пришлось решать. Кстати, после разрешения проблемы пришлось подумать над вопросом, как представить и в каком разделе сайта разместить данные наработки? Но об этом позже, а сейчас переходим к «телу» ...

Представьте, что Вы разрабатываете сложное приложение в OSGi-технологии. Структура приложения, включающая базу данных, создана и всё работает нормально; проблем с фукнционированием системы не наблюдается. Но наступает момент, когда Вы переходите к комплексному тестированию системы. Вы знаете, что отдельные транзакции, связанные с добавлением, обновлением и удалением записи в одной таблице затрагивают сразу несколько записей в других таблицах. Ошибка в алгоритме может полностью нарушить связанные значения в нескольких таблицах. Лечение, конечно же, можно найти, но насколько вовремя будет поставлен диагноз. А если после каждого «приема пищи» сразу же «принимать лекарство», то затягивается время транзакции. Да и зачем такая «пища», если её принимать вместе с «лекарством». Не лучше ли сразу создать нормальный алгоритм. То есть, необходима детальная отладка алгоритма (бизнес-логики) на большом количестве записей. В этом случае не обойтись без JUnit.

И вот здесь выплывает проблема. Дело в том, что приложение состоит из отдельных OSGi-модулей (бандлов). Вопросами взаимодействия с базой данных и исполнением SQL-запросов занимается один модуль, а тестировать необходимо другие модули. Т.е. необходимо протестировать алгоритмы (процедуры/функции) модулей, отвечающих за формирование SQL-запросов. Структура модулей/бандлов определяется Maven, а связь между модулями описана в проектном файле pom.xml. IDE в многомодульном проекте видит связанные объекты либо через общий workspace, либо находит их в репозитории по зависимостям. Т.е. IDE связи находит в pom.xml (dependencies), а при сборке модуля может быть использован локальный или внешний репозиторий, если деплоется не многомодульный проект.

Таким образом, в IDE связи бандлов с модулем взаимодействия с БД просматриваются. Но при старте test-case отдельного модуля, остальные, в том числе и модуль взаимодействия с SQL-сервером, не подгружаются в JVM. И, следовательно, JUnit, не может найти JDBC-драйвер. Вот задача и определилась : как динамически загрузить JDBC-драйвер при тестировании модуля с использованием JUnit.

Конечно же, динамическая загрузка jar-библиотек в режиме run-time возможна в различных ситуациях, и не только при отладке приложения/модуля с использованием JUnit. Поэтому данная статья может быть полезна и Вам. А я выкладываю её на сайте, чтобы в будущем меньше «напрягаться» при возникновении подобных проблем. Как говорится, подальше положешь – поближе возьмешь.

Класс загрузки JDBC-драйвера

В качестве сервера базы данных выбран Apache Derby, который будет стартован из JVM вместе с test-case, т.е. в режиме EmbeddedDriver. Вы можете, при необходимости, выбрать другой JDBC-дрйвер, подключаясь к другому типу СУБД.

Для подключения к СУБД нам потребуется :

  • полный путь к JDBC-драйверу;
  • строка URL-подключения;
  • полное наименование драйвера (класс, включая наименование пакета).

Ниже представлен листинг класса JDBCConnection для динамической загрузки JDBC-драйвера. Здесь все тривиально просто. Главная задача – определить значение переменной connection, имеющей тип java.sql.Connection. Наименование драйвера определенно текстовой константой DRV. Остальные текстовые константы будут использованы при подключении к серверу БД.

В конструкторе вызывается метод загрузки драйвера loadDriverJDBC, исходный текст которого представлен ниже, чтобы не загромождать листинг класса. Открытый метод getConnection() возвращает объект подключения к серверу БД java.sql.Connection.


import java.io.File;
import java.io.IOException;
import java.util.Properties;

import java.net.URL;
import java.net.URLClassLoader;

import java.sql.Driver;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.DriverManager;

import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;

public class JDBCConnection 
{
    Connection connection = null;

    final String DRV="org.apache.derby.jdbc.EmbeddedDriver";

    final String  ENCODING     = "characterEncoding";
    final String  UTF8         = "UTF-8";
    final String  STR_REGISTER = "Register JDBC driver :"
                               + "\n\t" + DRV;
    //-----------------------------------------------------
    public JDBCConnection()
    {
        loadDriverJDBC();
    }
    //-----------------------------------------------------
    public Connection getConnection()
    {
        return connection;
    }
    //-----------------------------------------------------
    private void loadDriverJDBC()
    {
        // исходный код представлен ниже
    }
}
 

Метод загрузки JDBC-драйвера

Ниже представлен листинг метода loadDriverJDBC динамической загрузки JDBC-драйвера сервера Apache Derby.

Сначала определим значения текстовых констант PATH_driver и URL_db. После этого необходимо подготовить загрузчик драйвера URLClassLoader. В качестве загрузчика loader используем ClassLoader текущего класса. Можно также использовать и системный загрузчик (закомментированные строки). После определения всех необходимых параметров для загрузчика переходим к рефлексии для определения метода (java.lang.reflect.Method), который будет выполнять загрузку. Здесь самое главное понять, что мы должны работать в одном «информационном пространстве» JVM, чтобы иметь доступ к пакетам и классам.

После загрузки JDBC-драйвера необходимо выполнить его регистрацию, после чего можно подключаться к серверу БД.


private void loadDriverJDBC()
{
    final String PATH_driver;  // путь к драйверу 
    final String URL_db     ;  // URL JDBC-подключения

    PATH_driver="D:/libs/org.apache.derby-10.10.1000001.jar";
    URL_db="jdbc:derby:D:/data/db;user=derby;password=derby";

    URL[]          urls   ;
    URL            jarFile;
    URLClassLoader loader ;
    Method         method ;

    try {
        File     driverJar = new File(PATH_driver);
        Class<?> cls       = this.getClass();
        String   file      = "file:" +
                 driverJar.getAbsolutePath()+"!/";

        urls    = new URL[] {driverJar.toURI().toURL()};
        jarFile = new URL("jar", "127.0.0.1", file);

        loader = new URLClassLoader(urls, 
                                    cls.getClassLoader());
//      loader = new URLClassLoader(urls, 
//                         System.class.getClassLoader());

        cls = URLClassLoader.class;
        try {
            //--- Загрузка драйвера (jar-файла) ---
            Class<?>[] clss = new Class[] {URL.class};
            Object[] objects = new Object[] {jarFile};

            method = cls.getDeclaredMethod("addURL", clss);
            method.setAccessible(true);
            method.invoke(loader, objects);

            //--- Регистрация JDBC-драйвера ---
            cls = Class.forName(DRV, true, loader);
            Driver driver;
            driver = (Driver) cls.newInstance();
            DriverManager.registerDriver(driver);
            System.out.println(STR_REGISTER);

            //--- Подключение к серверу БД ---
            Properties props;
            props = new Properties();
            props.setProperty(ENCODING, UTF8);
            connection = driver.connect(URL_db, props);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}
 

Тестирование класса загрузки JDBC-драйвера

В заключение листинг простенького класса тестирование нашего загрузчика JDBC-драйвера. Принимая во внимание, что объект подключения connection может быть использован в нескольких test-case'х, то его инициализация выполняется в методе init с дескриптором @BeforeClass, т.е. один раз при старте класса. Отключение от сервера БД выполняется в методе closeConnection с дескриптором @AfterClass, т.е. один раз после завершения всех тестов.


import org.junit.Test;
import org.junit.Assert;
import org.junit.AfterClass;
import org.junit.BeforeClass;

import java.sql.Connection;
import java.sql.SQLException;

public class TestConnection extends Assert 
{
    static Connection connection = null;
    //-----------------------------------------------------
    @BeforeClass
    public static void init()
    {
        JDBCConnection jdbc = new JDBCConnection();
        connection = jdbc.getConnection();
    }
    //-----------------------------------------------------
    @Test
    public void test01_openConnection() 
    {
        assertTrue(connection != null);
    }
    //-----------------------------------------------------
    @AfterClass
    public static void closeConnection()
    {
        try {
            if (connection != null)
                connection.close();
        } catch (SQLException e) {}
    }
    //-----------------------------------------------------
}
 

Связанные темы

Возвращаясь к поставленному в начале статьи вопросу, в каком разделе разместить данную страницу, перечислю только темы, затронутые в данном примере :

  • OSGi-технологии – сервис-ориентированная платформа построения приложений с поддержкой модульности;
  • JUnit – библиотека для модульного тестирования;
  • ClassLoader – загрузчики классов/библиотек;
  • reflection – рефлексия кода.

В любом разделе в качестве практического примера можно было бы разместить данную статью. Но поскольку основная задача была связана с JDBC, то она и попала в данный раздел.

  Рейтинг@Mail.ru