[an error occurred while processing this directive] IT • archiv :: Print

IT • archiv


[an error occurred while processing this directive]

[an error occurred while processing this directive]

Введение в JDBC

[an error occurred while processing this directive](none) [an error occurred while processing this directive](none)[an error occurred while processing this directive] ::
[an error occurred while processing this directive](none)
[an error occurred while processing this directive]([an error occurred while processing this directive] Павел Иванов [an error occurred while processing this directive])

[an error occurred while processing this directive](none)
PDF versionPDF версия
Обзор
В статье рассматривается технология JDBC: история создания, виды драйверов, способы работы и дается описание наиболее часто используемых классов и интерфейсов.

Что такое JDBC?

Словосочетание JDBC — стало настолько обыденным в среде Java программистов, что уже само-собой разумеется, многие не задумываясь расшифровывают данное сокращение по аналогии с технологией Microsoft ODBC, как Java Database Connectivity. На самом деле это не совсем так, вернее даже совсем не так. JDBC никак не расшифровывается — это просто JDBC и все тут, точка! Каждый может расшифровывать так, как ему заблагорассудится, например Java Driven Basic Connection (Java управляемое основное соединение) или Just Draught Brokerage Company (только что созданная брокерская компания). В общем смысл не в названии, а в сути. Суть JDBC очень проста — это API доступа к табличным данным. Внимательный читатель заметит, что речь не идет о базах данных, а о «табличных данных». Разница на первый взгляд может и не очень существенная, но на самом деле критичная. Например существуют JDBC драйверы доступа к текстовым файлам, таблицам Microsoft Excel, то есть к таким данным, которые ну никак нельзя отнести к базам данных: с поддержкой транзакций, индексов, отношений и проч.

JDBC, важно отметить, поддерживает работу не только с SQL совместимыми СУБД, но также и с практически любыми данными табличного типа. Хотя справедливо все же будет отметить, что мощь JDBC состоит конечно же не в том, чтобы прочитать данные из текстового файла — это как говорится «из пушки по воробъям» — без использования механизма транзакций, доступа к хранимым процедурам и проч. «прелестям» присущим SQL базам данных JDBC не стал бы тем чем он сейчас является.

Технологию JDBC ни в коей мере нельзя называть технологией одной компании Sun Microsystems — родного отца Java. На данный момент технологию JDBC официально поддерживают следующие организации:

В данном контексте слово «поддерживают» имеет вполне конкретный смысл, а именно участие в выработке собственно API в рамках принятого в сообществе Java процесса JSR (Java Specification Request).

Краткая история JDBC

В составе первой оригинальной версии Java (JDK 1.0) JDBC не было, понятие JDBC появилось только c выходом в декабре 1996 года JDK 1.1 — этот первый релиз JDBC принято обозначать как API JDBC 1.0 Все объявления API JDBC находились в пакете java.sql.

Следующий релиз JDBC совпал с появлением так называемого Java 2 — в декабре 1998 года и объявлением о появлении нового раздела Java 2 — J2EE в июне 1999 г. Часть JDBC, «оставшаяся» в J2SE, теперь стала называться как JDBC Core API, гарантировалось, что любой JVM должен был реализовывать эту базовую функциональность. Кроме этого появилось расширение JDBC в пакете javax.sql, входившая в J2EE под названием JDBC Optional Package — необязательная функциональность использовавшаяся в основном для серверных приложений.

Стандартный пакет JDBC 2.0 по сравнению с версией 1.0 включает:

В дополнительный пакет в составе J2EE вошли классы и интерфейсы поддержки:

Отметим здесь же, что вместе с версией JDK 1.3 вышло обновление API JDBC 2.1

API JDBC 3.0 вышел совместно с JDK 1.4, наиболее значительными изменениями следует считать следующие нововведения:

API JDBC 4.0 находится на стадии финального согласования в рамках процессa JSR-221 и его выпуск ожидается совместно с JDK версии 1.5. Декларированные цели JDBC 4.0:

Типы JDBC драйверов

В API JDBC интенсивно эксплуатируется концепция интерфейсов — набора методов, которые должны быть реализованы поставщиком того или иного сервиса, в данном случае поставщиком т.н. драйвера JDBC. С программной точки зрения драйвер JDBC есть нечто иное как реализация интерфейсов предусмотренных API JDBC. По способу реализации драйверы подразделяются на 4 типа:

Тип 1

К этому типу относятся драйверы реализованные поверх ODBC драйверов (что такое ODBC мы объяснять здесь не будем, если кто-то не знает отсылаем к первооисточнику). То есть фактически все вызовы API JDBC транслируются в вызовы ODBC, а дальше обработку вызова ведет API ODBC. Иногда еще 1-й тип драйверов называется "JDBC-ODBC bridge". Преимуществом драйверов этого типа, является то что все источники данных доступные с помощью ODBC становятся доступными Java приложению, недостатки такого драйвера: низкая скорость работы, трудности конфигурирования и невозможность поддержки всех возможностей API JDBC.

Тип 2

Ко второму типу относятся драйверы использующие программные части написанные на других языках (как правило на Си). Обычно в этом случае для доступа к базе данных используются библиотеки разработанные производителем, а для их вызова используется JNI — Java интерфейс вызова нативных функций. Примером такого драйвера является т.н. «толстый» OCI-JDBC драйвер для Oracle. Такие драйверы обычно очень быстрые, но опять же так же как и в случае JDBC-ODBC драйверов требуют установки специального ПО на клиентской машине. Для OCI-JDBC драйвера Oracle например требуется установка клиента SQL*NET.

Тип 3

В отличие от предыдущих типов драйверов данный тип драйвера полностью реализуется на Java, но при этом вызовы JDBC транслируются в сетевой протокол (RMI, HTTP и т.д.), который далее транслируется в специфичный протокол базы данных. В чем-то этот драйвер схож с драйверами JDBC-ODBC, отличие в том, что реализуется полностью на Java, за счет чего отсутствует необходимость в установке клиентского ПО.

Тип 4

Также как и драйверы 3-го типа реализуется полностью на Java, но вызовы реализуются напрямую с использованием протокола базы данных, минуя сетевой протокол.

Необходимо здесь также отметить, что несмотря на то что производители драйверов часто декларируют тот или иной тип, полную совместимость и т.д. тем не менее в реальной жизни только около четверти драйверов имеют сертификат соответствия спецификации JDBC. Наиболее полную информацию о существующих драйверах JDBC можно получить посетив корневой ресурс JDBC: http://java.sun.com/products/jdbc/. Здесь помещен список драйверов JDBC с указанием их типа, производителя и наличия сертификатов: http://servlet.java.sun.com/products/jdbc/drivers. База содержит около 200 драйверов и снабжена удобной системой поиска и навигации.

Так как его использовать?

Для читателя добравшегося до этого раздела, как и любого истого программиста всегда волнует вопрос вынесенный в заголовок этой главы. Действительно, прежде чем пускаться в объяснения, что да как и почему имеет смысл написать простейшую программу, аналог волшебного «HelloWorld!», с которого традиционно начинается изучение любого языка или API. Не откладывая дела в долгий ящик давайте рассмотрим такой пример. В качестве тестовой базы, с которым мы будем работать возьмем Excel файл, а для доступа к нему будем использовать драйвер типа JDBC-ODBC бридж. Выбор JDBC-ODBC моста диктуется не любовью автора к продукции софтверного гиганта, а только тем, что начинающему программисту пробовать JDBC на примере Excel файла, проще всего и понятней. Ведь не всегда же у всех есть доступ к SQL Server, Oracle или MySQL. А вот стандартный Windows с установленным ODBC есть практически везде.

Последовательность действий должна быть такой:

  1. Создать следующий Excel файл:

    ODBC Setup step #1.
  2. Во вкладке User DSN, конфигуратора ODBC задать DataSource, который указывает на ваш файл.

    ODBC Setup step #2.

    Иконка конфигуратора ODBC в Windows 95/98/ME находится в Control Panel, а в Windows2000/XP в папке Administrative Tools, внутри Control Panel.

    Драйвер JDBC-ODBC входит в комплект дистрибутива J2SE, поэтому нет необходимости отдельно откуда-то его выкачивать.
  3. Исходный текст, который мы будем использовать будет следующий:
    import java.sql.*;
    public class JDBCTest1
    {
        public static void main(String[] args)
        {
            String driver = "sun.jdbc.odbc.JdbcOdbcDriver";
            String url = "jdbc:odbc:MyExcel";
            String fName;
            int fId;
            Object fSalary;
            Connection con;
            Statement st;
            ResultSet rs;
            try
            {
                Class.forName(driver);
            }
            catch(ClassNotFoundException cnfe)
            {   
                System.err.println("Can't find JDBC driver");
                cnfe.PrintStackTrace();
                System.exit(1);
            }
            try
            {
                con=DriverManager.getConnection(url, null, null);
            }
            catch(SQLException se)
            {
                System.err.println("DB connection error");
                se.PrintStackTrace();
                System.exit(1);
            }
            try
            {
                st = con.createStatement();
                ResultSet rs = st.executeQuery("select id, name, 
    				salary from $Sheet1");
                while (rs.next())
                {
                    fId = rs.getInt("id");
                    fName = rs.getString(2);
                    fSalary = rs.getObject(3);
                    System.out.println("id=" + id + ", name=" + name+", 
    					salary="+fSalary.toString());
                }
            }
            catch(SQLException se)
            {
                System.err.println("JDBC execution error");
                se.PrintStackTrace();
                System.exit(1);
            }
            try
            {
                con.close();
            }
            catch(SQLException se)
            {
                System.err.println("Connection close error");
                se.PrintStackTrace();
                System.exit(1);
            }
        }
    }
    

Структура кода JDBC приложений как правило не сильно отличается от приведенного примера. Здесь для простоты понимания и удобства весь код собран в одном методе, причем как нетрудно догадаться этот метод же и является точкой входа в Java приложение. Как компилировать и запускать это приложение объяснять нет необходимости, если у кого-то есть сложности с этим то я отсылаю его к так называемой первой чашечке Java (слово Java кстати на американском сленге означает кофе…).

Итак давайте внимательно посмотрим на наш код. Условно он состоит из 4-х частей находящихся в разных «try-catch» блоках.

Загрузка драйвера JDBC

Существует несколько способов загрузки драйвера JDBC

  1. Стандартным способом загрузки драйвера является использование загрузчика вызовом статического метода:
    Class.forName(driver);

    Для успешной загрузки драйвера необходимо удостовериться, чтобы файл с библиотекой содержащий указанный нами класс драйвера (в нашем случае это sun.jdbc.odbc.JdbcOdbcDriver) находился в переменной окружения CLASSPATH. В случае с нашим драйвером это не должно быть проблемой поскольку наш драйвер входит в состав JDK. Нормальный JDBC совместимый драйвер после загрузки должен создавать экземпляр класса драйвера, но…

  2. Иногда некоторые JDBC драйвера отказываются грузиться указанным способом, в таком случае рекомендуется использовать следующий код:
    Class.forName(driver).newInstance();

    В данном случае кроме загрузки драйвера производится еще и неявный вызов конструктора драйвера.

  3. В принципе, для загрузки драйвера можно использовать даже следующий код с явным вызовом конструктора:
    new sun.jdbc.odbc.JdbcOdbcDriver();
  4. Загрузка драйвера JDBC может производиться и другим способом, например при вызове Java машины можно указать в значении специального системного свойства jdbc.drivers название класса JDBC драйвера:
    java -Djdbc.drivers=sun.jdbc.odbc.JdbcOdbcDriver JDBCTest1

    В этом случае при первой попытке установления соединения с базой данных менеджер драйверов автоматически сам загрузит класс указанный в системном свойстве jdbc.drivers

Установка соединения с базой данных

Соединение с базой данных производится единственным способом — вызовом метода:

Connection con = DriverManager.getConnection(URL, username, password);

Здесь необходимо обратить особое внимание на так называемый URL драйвера. URL служит для идентификации источника данных драйвером JDBC. В самом общем виде синтаксис URL драйвера следующий:

jdbc:<subprotocol>:<subname>

Такой формат URL вообще говоря является частным случаем синтаксиса обычного URL.

  • Первая часть, а именно слово jdbc является зарезервированным и обозначает протокол доступа (так же как и http, ftp и проч.).
  • Второе слово <subprotocol> обозначает имя драйвера или название механизма соединения. В нашем случае это odbc
  • Третье слово <subname> обозначает источник данных. В нашем случае это просто название ODBC DataSource. Как правило третья часть URL драйвера является вендор-специфичной и ее синтаксис довольно существенно отличается от драйвера к драйверу. Например для «тонкого» драйвера Oracle синтаксис URL выглядит так:
    jdbc:oracle:thin:@<host>:<port>:<sid>

    А для mySQL так:

    jdbc:mysql://<host>:<port>/<database>

Собственно работа

После получения соединения с базой данных (интерфейс Connection), необходимо создать так называемый SQLStatement — специальный Java объект для выполнения SQL запросов. Простейший Statement это так называемый статический — для выполнения статических SQL запросов. В нашем примере он создается так:

st = con.createStatement();

Для выполнения SQL запроса используется метод

Statement.executeQuery(String query);

этот метод возвращает объект типа ResultSet. Для тех кто знаком с идеалогией Oracle PL/SQL — объект ResultSet будет нетрудно понять — он очень похож на CURSOR. По сути это внутренняя таблица с набором записей и внутренним указателем на текущую запись. Поля ResultSet совпадают с полями результирующего SQL запроса. ResultSet бывают так называемые нескроллируемые, скроллируемые, изменяемые и неизменяемые. Скроллируемый означает с возможностью перемещения внутреннего указателя в любом направлении, по умолчанию указатель ResultSet может двигаться только вперед и его содержимое не может меняться. Для перемещения по ResultSet в нашем случае используется цикл while (rs.next()) {}. Внутри цикла происходит опрос значения полей ResultSet — у нас 3 поля id, name и salary. Для доступа к ним можно использовать 2 альтернативных способа: по имени поля или по его порядковому номеру:

  • fId = rs.getInt("id");

    доступ по имени поля, причем явно специфицируется тип поля, в данном случае целочисленный

  • fName = rs.getString(2);

    доступ по порядковому номеру.

    Замечание
    Порядковый номер поля начинается с «1», а не с нуля.
  • В некоторых случаях, когда тип неизвестен или неважен можно использовать метод:
    fSalary = rs.getObject(3);

Окончание сеанса работы

Хорошим тоном является закрытие соединения с базой данных и вообще говоря всех открытых Statement и ResultSet. В принципе ResultSet при выполнении новых запросов закрываются автоматически, часто это является причиной скрытых ошибок при программировании со сложными запросами с повторным использованием Statement.

«Продвинутое» использование JDBC

Довольно редко бывает, чтобы использование JDBC в серъезном приложении было ограничено кодом похожим на приведенный пример. Для дальнейшего изучения JDBC нам уже понадобится нормальная SQL база данных: Oracle, MySQL или SQL Server. Приведенные ниже примеры не будут работать на MS Excel таблице с доступом через JDBC-ODBC.

Пакетное выполнение SQL запросов

Иногда требуется выполнить несколько SQL запросов один за другим скажем: вставить запись, обновить другую запись и т. д. Для таких случаев принято использовать механизм пакетного (batch) выполнения SQL запросов:

st = con.createStatement();
st.addBatch("INSERT INTO CUSTOMER VALUES (10, 'John', 1000)");
st.addBatch("UPDATE CUSTOMER SET SALARY = 250 WHERE ID = 1");
st.addBatch("UPDATE CUSTOMER SET SALARY = 350 WHERE ID = 2");
int[] results = st.executeBatch();

Результатом выполнения будет массив целых чисел, каждый элемент массива соотвествует одному запросу, значения элементов расшифровываются так:

  • больше или равно «0» выполнение успешное и количество обновленных записей равно значению элемента;
  • «-2» выполнение успешное, но количество записей затронутых обновлением неизвестно;
  • «-3» выполнение неуспешное, но при этом выполнение следующей команды в пакете продолжено.

Использование прекомпилированных SQL запросов

Для эффективного использования похожих и повторяющихся SQL запросов в JDBC API используется специальный механизм PreparedStatement — по сути это субинтерфейс Statement, который позволяет хранить в памяти прекомпилированный образ SQL выражения экономя тем самым на времени компиляции SQL выражений. Типичный код с использованием PreparedStatement выглядит примерно так:

PreparedStatement pst = con.preparedStatement(
	"UPDATE CUSTOMER SET SALARY = ? WHERE ID = ?"
	);
pst.setInt(2, 1);
pst.setDouble(1, 100);
pst.executeUpdate();
pst.setInt(2, 2);
pst.setDouble(1, 200);
boolean result = pst.executeUpdate();

Символами «?» здесь отмечены параметры прекомпилированного SQL выражения — в нашем случае это запрос на обновление полей ID и SALARY в таблице CUSTOMER. Далее с помощью методов setXXX() параметрам присваиваются нужные значения и вызывается метод executeUpdate() — для выполнения запроса на обновление. Порядковые номера параметров начинаются с «1» и нумеруются слева-направо.

Вызов хранимых процедур и функций

Вряд ли сейчас можно представить работу с базами данных без использования хранимых процедур и функций, которые выполняются на стороне сервера базы данных и являются неотьемлемой частью современной СУБД.

Рассмотрим пример использования хранимой функции getSumSalary, возвращающей сумму полей SALARY для всех ID, которые меньше величины указанной в аргументе. Для вызова хранимой процедуры/функции используется субинтерфейс PreparedStatementCallableStatement. Пример вызова нашей хранимой функции будет выглядеть так:

CallableStatement cst = con.callableStatement(
	"{? = call getSumSalary(?)}"
	);
cst.setInt(2, 10);
cst.registerOutParameter(1, java.sql.Types.DOUBLE);
cst.execute();
System.out.println("Sum salary for id < 10 = " + cst.getDouble(1));

Следует обратить внимание на фигурные скобки — это часть синтаксиса вызова хранимых процедур. В некоторых случаях синтаксис вызова хранимых процедур может отличаться, например указанный пример для Oracle можно написать и так:

{begin :1 = getSumSalary(:2); end;}

Замечание об исключениях

В завершение нашего введения в JDBC нельзя не сказать об исключениях. В API JDBC используется 1 универсальный класс исключений SQLException. Данный класс имеет 3 внутренних поля:

  1. Текстовое описание ошибки — т.н. reason — причина
  2. Текстовое описание состояния SQLState — описание в стандарте XOPEN
  3. Код ошибки, как правило соответствует коду ошибки вендора базы данных

Если ошибка произошла в результате другого исключения то имеется специальный метод для получения предыдущего исключения в цепочке исключений (не путать с трассировкой стека):

SQLException getNextException();

Кроме SQLException есть еще 2 класса наследованные от него это:

Что еще?

JDBC практически неисчерпаемая тема, мы здесь не коснулись многих вещей:

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

[an error occurred while processing this directive]
[an error occurred while processing this directive](none)
< Вернуться на caйт :: Copyright © 1999 — 2010, IT • archiv.