Связь с базами данных через JDBC
Большинство информации хранится не в файлах, а в базах данных. Приложение должно уметь связываться с базой данных для получения из нее информации или для помещения информации в базу данных. Дело здесь осложняется тем, что СУБД (системы управления базами данных) сильно отличаются друг от друга и совершенно по-разному управляют базами данных. Каждая СУБД предоставляет свой набор функций для доступа к базам данных, и приходится для каждой СУБД писать свое приложение Но что делать при работе по сети, когда неизвестно, какая СУБД управляет базой на сервере?
Выход был найден корпорацией Microsoft, создавшей набор интерфейсов ODBC (Open Database Connectivity) для связи с базами данных, оформленных как прототипы функций языка С. Эти прототипы одинаковы для любой СУБД, они просто описывают набор действий с таблицами базы данных. В приложение, обращающееся к базе данных, записываются вызовы функций ODBC. Для каждой системы управления базами данных разрабатывается так называемый драйвер ODBC, реализующий эти функции для конкретной СУБД. Драйвер просматривает приложение, находит обращения к базе данных, передает их СУБД, получает от нее результаты и подставляет их в приложение. Идея оказалась очень удачной, и использование ODBC для работы с базами данных стало общепринятым.
Фирма SUN подхватила эту идею и разработала набор интерфейсов и классов, названный JDBC, предназначенный для работы с базами данных. Эти интерфейсы и классы составили пакет java.sqi, входящий в J2SDK Standard Edition, и его расширение javax.sql, входящее в J2SDK Enterprise Edition.
Кроме классов с методами доступа к базам данных для каждой СУБД необходим драйвер JDBC — промежуточная программа, реализующая методы JDBC. Существуют четыре типа драйверов JDBC.
1. Драйвер, реализующий методы JDBC вызовами функций ODBC. Это так называемый мост (bridge) JDBC-ODBC. Непосредственную связь с базой при этом осуществляет драйвер ODBC.
2. Драйвер, реализующий методы JDBC вызовами функций API самой СУБД.
3. Драйвер, реализующий методы JDBC вызовами функций сетевого протокола, независимого от СУБД. Этот протокол должен быть, затем, реализован средствами СУБД.
4. Драйвер, реализующий методы JDBC вызовами функций сетевого протокола СУБД.
Перед обращением к базе данных следует установить нужный драйвер, например, мост JDBC-ODBC:
try{
Class dr = sun.jdbc.odbc.JdbcOdbcDriver.class;
}catch(ClassNotFoundException e){
System.err.println("JDBC-ODBC bridge not found " + e);
}
Объект dr не понадобится в программе, но таков синтаксис. Другой способ установки драйвера показан в листинге П.1.
После того как драйвер установлен, надо связаться с базой данных. Методы связи описаны в интерфейсе connection. Экземпляр класса, реализующего этот интерфейс, можно получить одним из статических методов getConnection () класса DriverManager, например:
String url = "jdbc:odbc:mydb";
String login = "habib";
String password = "lnF4vb";
Connection qon = DriverManager.getConnection(url, login, password);
Обратите внимание на то, как формируется адрес базы данных url. Он начинается со строки "jdbc:", потом записывается подпротокол (subprotocol), в данном примере используется мост JDBC-ODBC, поэтому записывается "odbc:". Далее указывается адрес (subname) по правилам подпротокола, здесь просто имя локальной базы "mydb". Второй и третий аргументы — это имя и пароль для соединения с базой данных.
Если в вашей вычислительной системе установлен пакет javax.sql, то вместо класса DriverManager лучше использовать интерфейс DataSource.
Связавшись с базой данных, можно посылать запросы. Запрос хранится в объекте, реализующем интерфейс statement. Этот объект создается методом createstatement (), описанным в интерфейсе connection. Например:
Statement st = con.createStatement();
Затем запрос (query) заносится в этот объект методом execute () и потом выполняется методом getResultSet(). В простых случаях это можно сделать одним методом executeQuery (), например:
ResultSet rs = st.executeQuery("SELECT name, code FROM tbll");
Здесь из таблицы tbll извлекается содержимое двух столбцов name и code и заносится в объект rs класса, реализующего интерфейс ResultSet.
SQL-операторы INSERT, UPDATE, DELETE, CREATE TABLE и другие в простых случаях ВЫПОЛНЯЮТСЯ методом executeUpdate ().
Остается методом next () перебрать элементы объекта rs — строки полученных столбцов — и извлечь данные многочисленными методами getxxx () интерфейса ResultSet:
while (rs.next()){
emp[i] = rs.getString("name") ;
num[i] = rs.getlnt("code");
i++; }
Методы интерфейса ResuitsetMetaData позволяют узнать количество полученных столбцов, их имена и типы, название таблицы, имя ее владельца и прочие сведения о представленных в объекте rs сведениях.
Если объект st получен методом
Statement st = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_OPDATABLE);
то можно перейти к предыдущему элементу методом previous (), к первому элементу — методом first о, к последнему — методом last о. Можно также изменять объект rs методами updatexxx () и даже изменять, удалять и добавлять соответствующие строки базы данных. Не все драйверы обеспечивают эти возможности, поэтому, надо проверить реальный тип объекта rs методами rs.getType() И rs.getConcurrency().
Интерфейс Statement расширен интерфейсом PreparedStatement, тоже позволяющим изменять объект ResultSet методами setxxxo.
Интерфейс Preparedstatement, в свою очередь, расширен интерфейсом caiiablestatement, в котором описаны методы выполнения хранимых процедур.
В листинге П.1 приведен типичный пример запроса к базе Oracle через драйвер Oracle Thin. Апплет выводит в окно браузера четыре поля ввода для адреса базы, имени и пароля пользователя, и запроса. По умолчанию формируется запрос к стартовой базе Oracle, расположенной на локальном компьютере. Результат запроса выводится в окно браузера.
Листинг П.1. Апплет, обращающийся к базе Oracle
import j ava.awt.*;
import java.awt.event.*;
import j ava.applet.*;
import java.util.*;
import j ava.sql.*;
public class JdbcApplet extends Applet
implements ActionListener, Runnable{
private TextField tfl, tf2, tf3;
private TextArea ta;
private Button bl, b2;
private String url = "jdbc:oracle:thin:Slocalhost:1521:ORCL",
login = "scott",
password = "tiger",
query = "SELECT * FROM dept";
private Thread th;
private Vector results;
public void init(){
setBackground(Color.white) ;
try{
DriverManager.registerDriver(
new oracle.j dbc.driver.OracleDriver() ) ;
}catch(SQLException e){
System.err.println(e); }
setLayout(null);
setFont(new Font("Serif", Font.PLAIN, 14));
Label l1 = new Label("URL базы:", Label.RIGHT);
11.setBounds(20, 30, 70, 25); add(ll);
Label 12 = new Label("Имя:", Label.RIGHT);
12.setBounds(20, 60, 70, 25); add(12);
Label 13 = new Label("Пароль:", Label.RIGHT);
13.setBounds(20, 90, 70, 25); add(13);
tfl = new TextField(url, 30);
tfl.setBounds(100, 30, 280, 25); add(tfl);
tf2 = new TextField(login, 30);
tf2.setBounds(100, 60, 280, 25); add(tf2);
tf3 = new TextField(password, 30);
tf3.setBounds(100, 90, 280, 25); add(tf3);
tf3.setEchoChar('*');
Label 14 = new Label("Запрос:", Label.LEFT);
14.setBounds(10, 120, 70, 25); add(14);
ta = new TextArea(query, 5, 50, TextArea.SCROLLBARS_NONE);
ta.setBounds(10, 150, 370, 100); add(ta);
Button bl = new Button("Отправить");
bl.setBounds(280, 260, 100, 30); add(bl);
b1.addActionListener(this);
}
public void actionPerformed(ActionEvent ae){
url = tfl.getText() ;
login = tf2.getText();
password = tf3.getText();
query = ta.getText();
if (th == null){
th = new Thread(this);
th. start () ;
}
}
public void run(){
try{
Connection con =
DriverManager.getConnection(url, login, password);
Statement st = con.createStatement();
ResultSet rs = st.executeQuery(query);
ResultSetMetaData rsmd = rs.getMetaData();
// Узнаем число столбцов
int n = rsmd.getColumnCount();
results = new Vector();
while (rs.nextOH String s = " ";
// Номера столбцов начинаются с 1!
for (int i = 1; i <= n; i++)
s += " " + rs.getObject(i);
results.addElement(s); }
rs.close();
st.close () ;
con.closet);
repaint();
}catch(Exception e){
System, err.println(e);
}
repaint();
}
public void paint(Graphics g)(
if (results == null){
g.drawstring("Can't execute the query", 5, 30);
return;
}
int у = 30, n = results.size();
for (int i = 0; i < n; i++)
g.drawString((String)results.elementAt(i), 5, у += 20); } }
Замечание по отладке
В главе 19 упоминалось, что для отладки сетевой программы удобно запустить и клиентскую, и серверную часть на одном компьютере, обращаясь к серверной части по адресу 127.0.0.1 или доменному имени localhost. He забывайте, что апплет может связаться по сети только с тем хостом, откуда он загружен. Следовательно, на компьютере должен работать Web-сервер. Если Web-сервер прослушивает порт 8080, то, чтобы загрузить HTML-страницу с апплетом, надо в браузере указывать адрес URL вида http://loca(host:8080/public/JdbcApplet.html. При этом учтите, что Web-сервер устанавливает свою иерархию каталогов, и каталог public на самом деле может быть каталогом usr/local/http/public или каким-нибудь другим.
Таким образом, JDBC позволяет проделать весь цикл работы с базой данных. Подробно со всеми возможностями JDBC можно познакомиться, прочитав спецификацию JDBC, имеющуюся в документации Java 2 SDK, в каталоге docs\guide\jdbc\spec\. Дополнения спецификации версии JDBC 2.0 изложены в каталоге docs\guide\jdbc\spec2\. В каталоге docs\guide\jdbc\getstart\ есть пособие по использованию JDBC.