Для подключений к БД из приложений написанных на JAVA компания Sun Microsystems разработала пакет драйверов JDBC
(Java DataBase Connectivity
), которые стали аналогом популярной майкрософтовской ODBC
. Собственно про JDBC
я в этой статье подробно рассказывать ничего не буду, тк на этом сайте валяется перевод спецификации JDBC от Sun, пусть и за 97-й год, но на мой взгляд там мало что изменилось, а если что, то будет повод для написания новой статьи типа "Что нового в JDBC vXXX?" Ладно в общем к делу...
Незнаю как вы а вот я, например, если что-то захотел реализовать в какой-нить среде какой-нить проект и не хера в нем (проекте) не понимаю, то сразу ищу помощи в примерах поставляемые вместе с продуктом! Да, да я имею ввиду те самые папочки с надписями типа demo, samples ... и только после этого лезу с глупыми вопросами в форумы и пост-группы. Жаль что большинство новичков этим пренебрегает, а между прочим содержимое этих папочек наполнено различными полезностями особенно для начинающих хотя и для профи тоже не помешало бы туда заглядывать время от времени. Вот и настал как-то в моей практике работы с ораклом день когда ну очень захотелось написать какой-нить клиент для БД на Java. Тк практики написания подобных программ у меня не было я недолго думая обратился к исходным кодам-примерам любезно поставляемые с JDK компанией Sun. Вот и наткнулся на файлик по имени JDBCAdapter.java который находится в каталоге jdk-xxx\jfc\TableExample\src
. Это небольшой класс(хотя кто привык писать проги уровня хелло ворлд видимо будет большой) который демонстрирует возможность подключения к БД через драйвер JDBC
( кстати если кто незнает отмечу что JDBC
позволяет подключаться к практически любой СУБД, а не только к Oracle
) с возможностью выборки данных.
Итак что представляет из себя данный класс. А он (класс имеет имя - JDBCAdapter
) представляет из себя рассширение класса AbstractTableModel
- класса который представляет из себя что-то типа абстрактной модели всех процессов происходящих с JTable
(если кто не знает это класс библиотеки Swing
для работы с таблицами в Java). Класс данный в примере нужно использует объект класса таблицы JTable
например:
JDBCAdapter adapter=new JDBCAdapter(параметры); JTable table=new JTable(adapter);
Примечание: Хочу вас предупредить что примеры - это не панацея от всех ваших проблем, примеры - это правильный путь по которому вы должны двигаться чтобы написать серьезный продукт.
Для обзора данного примера я избрал пошаговый метод разбора листинга класса JDBCAdapter
, тк считаю его наиболее эффективным для понимания всех процессов.
Как-всегда код начинается с объявления импортируемых библиотек и классов:
import java.util.Vector; //Класс Vector нам понадобится для хранения выбранных данных из БД import java.sql.*; import javax.swing.table.AbstractTableModel; import javax.swing.event.TableModelEvent;
Здесь мы объявляем наш класс как расширение AbstractTableModel:
public class JDBCAdapter extends AbstractTableModel { Connection connection; // Т.к. это не руководство JDBC, а значит по Statement statement; // поводу данных объектов обращайтесь спецификации JDBC ResultSet resultSet; ResultSetMetaData metaData; String[] columnNames = {}; // Наименования столбцов таблицы Vector rows = new Vector(); // Объявляем вектор строк таблицы public JDBCAdapter(String url, String driverName, String user, String passwd) { try { Class.forName(driverName); // Регистрируем драйвер System.out.println("Opening db connection"); connection = DriverManager.getConnection(url, user, passwd); //Выполняем соединение с СУБД statement = connection.createStatement(); } catch (ClassNotFoundException ex) { System.err.println("Cannot find the database driver classes."); System.err.println(ex); } catch (SQLException ex) { System.err.println("Cannot connect to this database."); System.err.println(ex); } }
Конструктор класса JDBCAdapter выполняет функцию установления соединения с СУБД параметрами которого являются:
String url
- url базы данных например "jdbc:oracle:тип_драйвера:@хост:1521:имя_БД"
String driverName
- для подключения к ораклу используем следующую запись: "oracle.jdbc.driver.OracleDriver"
String user, passwd
- имя учетной записи и пароль оракла
public void executeQuery(String query) { if (connection == null || statement == null) { System.err.println("There is no database to execute the query."); return; } try { resultSet = statement.executeQuery(query); // Отправляем запрос на СУБД metaData = resultSet.getMetaData(); // Получаем метаданные с сервера с помощью которых: int numberOfColumns = metaData.getColumnCount(); // сохраняем кол-во столбцов columnNames = new String[numberOfColumns]; for(int column = 0; column < numberOfColumns; column++) { columnNames[column] = metaData.getColumnLabel(column+1); // заносим в массив имена столбцов } rows = new Vector(); // (1) while (resultSet.next()) { // (1) Vector newRow = new Vector(); // (1) for (int i = 1; i <= getColumnCount(); i++) { // (1) newRow.addElement(resultSet.getObject(i)); // (1) } // (1) rows.addElement(newRow); // (1) } fireTableChanged(null); // Обновляем таблицу } catch (SQLException ex) { System.err.println(ex); } }
Данный метод осуществляет выборку данных, которые помещает в объект класса Vector
. Самое интересное в этом методе так это его независимость от кол-ва столбцов и строк таблицы БД что делает его вполне универсальным.
(1) Мы создаем объект класса Vector rows
в который мы будем заносить значения строк таблицы, после чего запускаем цикл while условием которого является наличие строки в таблице для организации пошагового прохода по строкам таблицы. В теле цикла while создаем еще один вектор newRow и с помощью цикла for заносим значения каждого столбца текущей строки в вектор newRow, которые в свою очередь заносим в вектор rows. В итоге получаем вектор векторов:
rows[newRow[столбец1, столбец2,...], newRow[столбец1, столбец2,...],...]
- каждый элемент rows - является набором строк, а каждый элемент newRow - значение столбца определенной строки. Для доступа к значению ячейки таблицы использум следующую запись:
Vector row = rows.elementAt(номер_строки); значение_ячейки = row.elementAt(номер_колонки);
Именно эта запись используется в методе getValueAt() (см далее).
public void close() throws SQLException { System.out.println("Closing db connection"); resultSet.close(); statement.close(); connection.close(); }
Данный метод закрывает текущий сеанс с СУБД.
public String getColumnName(int column) { // (2) if (columnNames[column] != null) { return columnNames[column]; // Возвращаем имя столбца } else { return ""; } } public boolean isCellEditable(int row, int column) { // (3) try { return metaData.isWritable(column+1); // вернет true если ячейка редактируемая, иначе false } catch (SQLException e) { return false; } } public int getColumnCount() { // (4) return columnNames.length; } public int getRowCount() { // (5) return rows.size(); } public Object getValueAt(int aRow, int aColumn) { // (6) Vector row = (Vector)rows.elementAt(aRow); // Получаем вектор столбцов строки aRow return row.elementAt(aColumn); }
Метод 2 возвращает имя столбца по его номеру column. Этот метод использует JTable
чтобы дать имена заголовкам столбцов таблицы. 3 метод проверяет возможна ли редактирование ячейки таблицы. 4 и 5 методы возвращают кол-во строк и столбцов соответственно. А вот шестой метод вернет нам сами значения столбца aColumn
строки aRow
.
public Class getColumnClass(int column) { int type; try { type = metaData.getColumnType(column+1); } catch (SQLException e) { return super.getColumnClass(column); } switch(type) { case Types.CHAR: // Char, Varchar и Longvarchar будут String case Types.VARCHAR: case Types.LONGVARCHAR: return String.class; case Types.BIT: return Boolean.class; case Types.TINYINT: case Types.SMALLINT: case Types.INTEGER: return Integer.class; case Types.BIGINT: return Long.class; case Types.FLOAT: case Types.DOUBLE: return Double.class; case Types.DATE: return java.sql.Date.class; default: return Object.class; } }
Этот метод будет конвертировать тип данных СУБД в тип данных поддерживаемые Java.
public String dbRepresentation(int column, Object value) { int type; if (value == null) { return "null"; } try { type = metaData.getColumnType(column+1); // Получаем тип значения столбца } catch (SQLException e) { return value.toString(); } switch(type) { case Types.INTEGER: case Types.DOUBLE: case Types.FLOAT: return value.toString(); // Integer, double и float возвращаем как строку case Types.BIT: return ((Boolean)value).booleanValue() ? "1" : "0"; case Types.DATE: // Т.к. в Java нет такого типа как Дата значит возвращаем строку return value.toString(); default: return "\""+value.toString()+"\""; // Для всех остальных типов возвращаем строку заключенную в "\" } }
Данный метод вернет нам тип значений определенного столбца. Тк значение каждой ячейки таблицы имеет тип Object
, то мы реализуем метод который будет возвращать нам тип нашего значения.
public void setValueAt(Object value, int row, int column) { try { String tableName = metaData.getTableName(column+1); // Получаем имя таблицы которую будем использовать в запросе if (tableName == null) { System.out.println("Table name returned null."); } String columnName = getColumnName(column); // Сохраняем имя столбца которую будем использовать в запросе String query = "update "+tableName+ // Формируем текст запроса " set "+columnName+" = "+dbRepresentation(column, value)+ " where "; for(int col = 0; col < getColumnCount(); col++) { // Формируем часть запроса where String colName = getColumnName(col); if (colName.equals("")) { continue; } if (col != 0) { query = query + " and "; } query = query + colName +" = "+ dbRepresentation(col, getValueAt(row, col)); } System.out.println(query); System.out.println("Not sending update to database"); // statement.executeQuery(query); } catch (SQLException e) { // e.printStackTrace(); System.err.println("Update failed"); } Vector dataRow = (Vector)rows.elementAt(row); // Естественно обновляем наш вектор dataRow.setElementAt(value, column); } }
Данный метод вызывается всякий раз когда мы будем пытаться редактировать строку в таблице JTable
.
Параметры setValueAt()
:
Object value
- новое значение
int row и int column
- строка и столбец редактируемой строки
Вот и весь класс. Теперь при использовании его с JTable
:
JDBCAdapter adapter=new JDBCAdapter(параметры); JTable table=new JTable(adapter); adapter.executeQuery("select * from scott.emp");
получим работающую таблицу на форме:
Стоит еще раз отметить что данный класс в принципе универсальный но чтобы его использовать в сложных приложениях нужно ему добавить некоторые возможности для поддержки дополнительных функций, например добавление новой строки. Думаю я показал вам насколько полезную информацию несут примеры которым мы очень часто не уделяем должного внимания а ведь есть на что посмотреть не правда ли! Старт дан, действуйте дальше!
ЗЫ как всегда жду всех ваших замечаний а также предложений и вопросов на e-mail CBAT@mail.ru И еще если среди вас найдутся такие индивидумы которые готовы поделиться своими личными эффективными способами или методами решения той или иной задачи связанной с Ораклом или Javой обязательно пишите - обязательно опубликуем!