Студенческий отдел кадров
Пособие по JAVA-технологиям
(с) AntonSaburov
База данных
Получив некоторое представление, что существуют такие замечательные структуры как коллекции, Вы наверняка задумались: «Возможно все это здорово, только все эти данные существуют временно — а когда программа завершится, то все исчезнет. Ну и зачем такая нужна ?»
И это замечание вполне справедливо. Конечно же, коллекции для такого рода задач подходят слабо. Так что же делать дальше ? Конечно, большинство из вас знает, что существуют базы данных. Давайте попробуем посмотреть, как можно усовершенствовать нашу систему, используя их.
Прекрасно понимая, что работа с базами данных на сегодня является первостепенной задачей разработчики Java создали специальную технологию — JDBC (Java Database Connectivity). Она позволяет однообразно получать доступ к различным базам данных. Фактически для каждой базы данных существует специальный драйвер, который загружается и используется приложением на Java для доступа к конкретной базе. Но что самое интересное — для программиста работа с различными драйверами практически полностью одинакова, что для MS SQL Server, что для Oracle. В нашем мире хоть какой-то островок стабильности — это уже большое счастье.
Для запуска наших примеров я установил MySQL — он свободен для доступа: MySQL 5.0
Драйвер JDBC для работы вы можете взять здесь: Download Connector/J 3.1
О нем мы обязательно поговорим несколько позже.
Рекомендуем: Вам надо ознакомиться с общими понятиями баз данных и SQL (Structured Query Language) для понимания этой части. Для самого простого уровня достаточно воспринимать базы данных, как набор таблиц, у которых есть фиксированный набор столбцов и любой количество строк. А SQL — это язык, который позволяет получать данные из этих таблиц и модифицировать данные в этих таблицах. Наверняка вы все видели какие-то ведомости, даже расписывались в них. И заметили, что у них обычно есть столбцы с какими либо наименованиями и дальше разлинованные строки, в которые вписываются реальные данные. Так же с точки зрения прикладного программиста (т.е. большинства из нас) выглядят и таблицы в базе данных. Только строки можно добавлять, удалять и модифицировать не зачеркивая — думаю, что многие из вас работали с Excel — так и воспринимайте таблицы в базе данных. Кроме этого учтите, что каждый столбец в базе имеет определенный тип данных. Чаще всего это строки определенной длины, числа (целые, вещественные), даты. В общем-то и все.
Бестселлер по SQL — это книга Мартина Грубера «Понимание SQL». Скачать ее похоже можно уже во многих местах сети:
http://lib.babr.ru/index.php?book=327
http://zipsites.ru/books/ponimanie_sql/
http://bookz.ru/authors/martin-gruber/gruber01.html
Также на форуме Vingrad существует несколько статей по этому вопросу:
http://vingrad.ru/DB-DB-003022
http://vingrad.ru/DB-DB-000133
Здесь мы рассмотрим очень простые команды SQL — чтобы просто понимать о чем буджет идти речь в дальнейшем.
Добавление записи
Выполняется с помощью оператора INSERT.
1 2 |
INSERT INTO <имя_таблицы> (<имя_поля_1>, <имя_поля_2>, <имя_поля_3>, ...) VALUES (<величина_поля_1>, <величина_поля_1>, <величина_поля_3>, <величина_поля_3>, ...) |
Думаю, что вы уже догадались что есть что.
- <имя_таблицы> — имя таблицы в базе данных
- <имя_поля_1> — по сути это заголовок столбца (его имя)
- <величина_поля_1> — это реальное значение поля для новой записи
Причем заметим, что порядок полей не важен — главное, чтобы было соответствие между порядком в обоих списках полей и величин. Т.е. если я что-то хочу вставить в поле (например STUDENT_NAME), которое идет вторым, то и реальная величина должна быть второй.
Также вы можете перечислять не все поля. Те поля, которых нет в списке будут установлены по умолчанию. Это может быть NULL(не путайте с 0 — это другое совсем. NULL означает отсутствие величины вообще). Также база данных может иметь правила для установки значений полей. Это может быть поле с автоматическим увеличением (счетчик), может быть даже какая-то формула или функция.
Для примера если я хочу добавить новую группу в таблицу STUDENT_GROUP
1 2 |
INSERT INTO STUDENT_GROUP (GROUP_NAME, CURATOR, SPECIALITY) VALUES ("Первая", "Доктор Борменталь", "Делание человеков из собачек") |
Редактирование записи
Выполняется с помощью оператора UPDATE.
1 |
UPDATE <имя_таблицы> SET <имя_поля_1>=<величина_поля_1>, <имя_поля_2>=<величина_поля_2> ... |
Думаю, что комментарии по именам особо не нужны. Важно отметить вот что — оператор в такой форме произведет изменения ПО ВСЕМ СТРОКАМ в таблице. Конечно же нам такого не хочется — мы хотим делать изменения только для какой-то одной записи. Давайте рассмотрим поначалу синтаксис оператора удаления и после него вернемся к нашей проблеме.
Удаление записи
Выполняется с помощью оператора DELETE.
1 |
DELETE FROM <имя_таблицы> |
И здесь мы натолкнемся на похожую проблему — удаление будет сделано ДЛЯ ВСЕХ ЗАПИСЕЙ таблицы. Т.е. таким опертором мы удалим все данные. Для решения возникшей проблемы нам потребуется рассмотреть два понятия:
Первое — это возможность добавлять условия в наши операторы. Условия позволяют указать нам то множество записей, которые мы хотим удалить или отредактировать. Также условия позволяет нам получить ограниченный набор записей, а не все. Чуть позже мы посмотрим синтаксис получения данных. Для удаления и редактирования выглядит это так:
1 2 3 4 |
UPDATE <имя_таблицы> SET <имя_поля_1>=<величина_поля_1>, <имя_поля_2>=<величина_поля_2> ... WHERE <условия> DELETE FROM <имя_таблицы> WHERE <условия> |
После ключевого слово WHERE можно поставить какие-либо условия, которые указывают какие именно записи надо изменить/удалить. Условия могут записываться с помощью достаточно понятных выражений — >, <, =, >=, <=, <> (больше, меньше, равно, больши или равно, меньше или равно, не равно). Также Вы можете использовать неколько условия, соединяя их операторами AND, OR (и, или).
Второе — первичный ключ. Это поле или набор полей, которое (который) уникален для каждой записи. Чаще всего это поле, которое имеет автоматическое увеличение. Кроме того на такое поле накладывается условия уникальности. Т.е. две записи не могут иметь одинаковое значение первичного поля. Много копий сломано в попытках доказать, насколько нужны искусственное введение такого рода полей, но подавляющее большинство программистов используют их. И автор советует делать тоже самое.
И именно эти сображения породили у наших классов Student и Group поля — studentId и groupId. В них мы будем хранить уникальные идентификаторы. Теперь, если мы захотим изменить данные какого-либо студента, то будет это выглядеть так:
1 |
UPDATE STUDENTS SET group_id=2, educationYear=2007 WHERE student_id=10 |
Здесь мы для студента с идентификатором 10 установили группу 2 и год обучения в этой группе 2007. Для удаления студента используется такой синтаксис:
1 |
DELETE FROM STUDENTS WHERE student_id=10 |
Как Вы наверно уже догадались можно делать групповые изменения в таблице. Например мы можем удалить всех студентов и группы с ИД 2 и годом обучения 2006.
1 |
DELETE FROM STUDENTS WHERE group_id=2 AND educationYear=2006 |
Также можно одним оператором перевести студентов из группы с ИД 1 и с годом 2006 в группу 2 с годом 2007
1 |
UPDATE STUDENTS SET group_id=2, educationYear=2007 WHERE group_id=1 AND educationYear=2006 |
Выборка записей
Выполянется с помощью оператора SELECT. Синтаксис следующий:
1 |
SELECT <имя_поля_1>, <имя_поля_2>, ... FROM <таблица> WHERE <условия> |
Если Вы хотите получить все записи из таблицы, то можно вместо списка полей использовать знак «*».
1 |
SELECT * FROM <таблица> WHERE <условия> |
Поля в принципе могут идти в любом порядке. Хотя чаще всего они передаются в том порядке, в котором они идут в описании таблицы. Но рассчитывать на это не рекомендуется. Для примера получения списка студентов для группы с ИД 1 и годом обучения 2006 мы можем сделать выборку таким образом:
1 |
SELECT * FROM STUDENTS WHERE group_id=1 AND educationYear=2006 |
Также хотелось бы отметить, что одновременно с получением данных Вы можете их отсортировать с помощью команды ORDER BY. ORDER BY может сортировать как в возрастающем так и в убывающем порядке. Для этого используется модификатор DESC.
1 2 |
SELECT * FROM STUDENTS WHERE group_id=1 AND educationYear=2006 ORDER BY group_id SELECT * FROM STUDENTS WHERE group_id=1 AND educationYear=2006 ORDER BY group_id DESC |
Как Вы сами понимаете (а многие из вас уже знают), что SQL гораздо более мощное средство, чем то, что я показал. Он позволяет соеденить данные из нескольких таблиц в один набор данных, причем будет соблюдать всевозможные условия такого соеденения. В общем ссылки я вам дал — разбирайтесь :). Цель моего описания — дать в минимальном размере те знания, которые понадобятся для понимания нашего приложения.
Тестовые данные и установка MySQL
Для установки MySQL не надо предпринимать каких-то очень сложных действий. После запуска инсталляции надо только обратить внимание на следующие моменты:
- Когда установка предложит Вам завести нового пользователя — можно это сделать, но надо запомнить введенные параметры. В принципе нам этот пользователь не понадобится. Можете выбрать «Skip Sign-Up»
- Далее Вы можете выбрать все установки по умолчанию. НО НАДО остановиться на экране, где Вас попросят установить поддержку языка. Установите второй пункт из трех — поддержка UTF-8
- И наконец на экране смены пароля для администратора поставьте пароль root. Все примеры работы с базой рассчитаны на него. Хотя если вам захочется другой — будет повод повозиться еще
После установки MySQL вы можете запустить sql-скрипт, который предназначен для создания таблиц и заполнения их тестовыми данными. Сделать это просто — скопируйте приведенный код в файл students.sql, после этого зайдите в каталог<MySQL_DIR>/bin. И запустите команду. Само собой файл student.sql нужно положить туда же — на время. Хотя я часто прописываю путь в переменной PATH до MySQL — это удобно. Что и Вам советую.
mysql -u root -p < students.sql
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
DROP DATABASE IF EXISTS students; CREATE DATABASE students DEFAULT CHARACTER SET 'utf8'; USE students; create table groups ( group_id int unsigned not null auto_increment, groupName varchar(255) not null, curator varchar(255) not null, speciality varchar(255) not null, primary key (group_id) ) engine=InnoDB; create table students ( student_id int unsigned not null auto_increment, firstName varchar(255) not null, surName varchar(255) not null, patronymic varchar(255) not null, dateOfBirth date not null, sex char(1), group_id int not null, educationYear int not null, primary key (student_id) ) engine=InnoDB; set names 'utf8'; insert into groups (groupName, curator, speciality) values ('Первая', 'Доктор Борменталь', 'Создание собачек из человеков'); insert into groups (groupName, curator, speciality) values ('Вторая', 'Профессор Преображенский', 'Создание человеков из собачек'); insert into students (firstName, patronymic, surName, sex, dateOfBirth, group_id, educationYear) values ('Иван', 'Сергеевич', 'Степанов', 'М', '1990-03-20', 1, 2006); insert into students (firstName, patronymic, surName, sex, dateOfBirth, group_id, educationYear) values ('Наталья', 'Андреевна', 'Чичикова', 'Ж', '1990-06-10', 1, 2006); insert into students (firstName, patronymic, surName, sex, dateOfBirth, group_id, educationYear) values ('Виктор', 'Сидорович', 'Белов', 'М', '1990-01-10', 1, 2006); insert into students (firstName, patronymic, surName, sex, dateOfBirth, group_id, educationYear) values ('Петр', 'Викторович', 'Сушкин', 'М', '1991-03-12', 2, 2006); insert into students (firstName, patronymic, surName, sex, dateOfBirth, group_id, educationYear) values ('Вероника', 'Сергеевна', 'Ковалева', 'Ж', '1991-07-19', 2, 2006); insert into students (firstName, patronymic, surName, sex, dateOfBirth, group_id, educationYear) values ('Ирина', 'Федоровна', 'Истомина', 'Ж', '1991-04-29', 2, 2006); |
Как видите в конце мы использовали операторы вставки для заполнения нашей базы данных некоторыми данными. В первой части мы создавали таблицы с помощью команд CREATE TABLE. Первая строка DROP DATABASE IF EXISTS studentsуничтожит базу данных students, если такая существует. Здесь хотелось бы обратить внимание на строки
1 2 3 4 5 6 7 8 9 |
create table groups ( group_id int unsigned not null auto_increment, ... create table students ( student_id int unsigned not null auto_increment, |
Поля для ИД групп и студентов содержат понятие auto_increment. Это значит, что данные поля не подлежат редактированию и, что очень важно, при вставке новой записи они будут увеличивать свое значение автоматически. Т.е. каждый новый студент или группа получат ИД без наших усилий. Что очень удобно.
ВАЖНО!!! Не забивайте себе голову вопросами — «если я удалю какое-то поле, то как бы мне использовать пробелы (дырки) между ИД». Это не стоит Вашего внимания. Пусть будут провалы в нумерации. В этом ничего страшного нет. Думайте об этом как о случайном числе. Ведь если бы число генерилось вообще случайно в диапазоне от 0 до 4 млрд нам было бы не важно, что есть пробелы. Так вот и не заморачивайтесь.
Если Вы хотите что-то поменять сами, то запустите клиентское приложение mysql.exe — с его помощью Вы сможете выполнять различные запросы.
Работа с драйвером JDBC
Каждая база данных должна отдавать свои данные наружу — пользователям. Передача данных от сервера клиентскому приложению производится по определенным правилам (определенному протоколу), который для каждого производителя может быть свой. Для того, чтобы программисту не влезать во все эти дебри производитель базы данных разрабатывает драйвера для общения со свой базой данных. Для Java это JDBC-драйвера. Самое главное, что для Java-программиста НЕ ВАЖНО, к какому серверу он подключается. Работа с драйвером будет всегда одинакова. Таким образом единственное, что может помешать легкому перенесению базы данных с SQL-сервера одного производителя на другой — это особенности самих SQL-запросов. Если же они у вас не очень сложные и подчиняются стандартам, то скорее всего перенос не будет проблематичным. Рассмотрим простой пример взаимодействия с базой данных.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
import java.io.FileNotFoundException; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class TestJDBC { public static void main(String args[]) { // Снова используем файл для вывода из-за кодировки try { System.setOut(new PrintStream("out.txt")); } catch (FileNotFoundException ex) { ex.printStackTrace(); return; } System.out.println("Copyright 2009, Anton Saburov"); Connection con = null; Statement stmt = null; ResultSet rs = null; try { Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/students"; con = DriverManager.getConnection(url, "root", "root"); stmt = con.createStatement(); rs = stmt.executeQuery("SELECT * FROM students"); while (rs.next()) { String str = rs.getString(1) + ":" + rs.getString(2); printString(str); } } catch (Exception e) { e.printStackTrace(); } finally { // Эта часть позволяет нам закрыть все открытые ресуры // В противном случае возмжожны проблемы. Поэтому будьте // всегда аккуратны при работе с коннектами try { if (rs != null) { rs.close(); } if (stmt != null) { stmt.close(); } if (con != null) { con.close(); } } catch (SQLException ex) { ex.printStackTrace(); System.err.println("Error: " + ex.getMessage()); } } } // Снова используем этот метод для вывода из-за кодировки public static void printString(Object s) { try { System.out.println(new String(s.toString().getBytes("windows-1251"), "windows-1252")); } catch (UnsupportedEncodingException ex) { ex.printStackTrace(); } } } |
Давайте подробно рассмотрим, что же мы делаем.
Во-первых обратите внимание, что большинство операций для работы с базой данных вызывают исключения, поэтому мы используем конструкцию try … catch. Более верно было бы обрабатывать не все исключения в одном catch, а обработать каждое конкретное исключение отдельно. Кроме этого мы использовали блок finally для закрытия соеденений к базе данных. Если это не делать, то может возникнуть неприятная ситуация, когда коннектов больше получить не удастся. Так что будьте внимательны.
Итак: В первой строке мы загружаем класс драйвера — Class.forName(«com.mysql.jdbc.Driver»). Вторая строка — задаем URL который описывает «путь» до базы данных. Первая часть строки задает тип драйвера JDBC и сервер базы данных. Потом идет хост и порт (в нашем случае это локальный сервер на нашей машине и порт для MySQL по умолчанию) и имя базы данных, к которой мы подключаемся. Для другого сервера (например для Oracle) строка будет иной (для более подробной информации о формате URL для конкретного сервера баз данных Вам надо обратиться к документации по конкретному JDBC).
Далее мы осуществляем подключение к базе данных, указывая URL, имя пользователя и пароль. В нашем случае заходим пользователем root (что не является хорошим правилом — лучше всегда заводить другого пользователя и давать ему определенные права). Этой строкой мы «открыли дверь» в наш сервер. Теперь мы можем посылать ему команды и получать результат исполнения.
Эта строка создает запрос, который мы будем посылать на сервер
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(«SELECT * FROM students»);
Эта строка выполняет наш запрос получая в ответ таблицу (набор строк и столбцов). Объект класса ResultSet позволяет пройти по всему возвращенному набору и распечатать. Для этого мы используем цикл в котором проверяем с помощью вызова rs.next()есть ли еще записи. Если они там еще есть — печатаем. Если кончились, то цикл завершается.
После этого мы закрываем набор данных ResultSet и запрос — тем самым освобождая ресурсы. Если Вы выполняете один запрос, после которого закрываете коннект к базе, то закрывать ResultSet и Statement не обязательно — они закроются автоматически. Но если Вы делаете много запросов внутри одного коннекта — закрывайте обязательно. Ну и в конце мы закрываем непосредственно сам коннект к базе. Все достаточно просто.
Мы рассмотрели пример, когда нам необходимо получить данные. В случае, если нам надо просто произвести изменения, то для этого существует вызов executeUpdate. Подробнее о вызовах можно посмотреть в документации по Java API. Сборка данного примера не требует дополнительных библиотек и можно его собрать командой
javac TestJDBC.java
А вот для запуска на потребуется подключить драйвер. Обычно драйвер JDBC представляет из себя набор классов в JAR (Java Archive), который надо просто подключить через CLASSPATH. Например, если драйвер находится в файле mysql-connector-java-3.1.13-bin.jar, то командная строка для запуска будет выглядеть вот так
java -cp .;mysql-connector-java-3.1.13-bin.jar TestJDBC
Теперь мы с вами изменим нашу систему получения данных — ManagementSystem. Т.к. коннект к базе нам будет нужен постоянно, то создадим его один раз и будем использовать везде. Все остальные команды рассмотрим прямо в коде нашего класса ManagementSystem. Прошу обратить внимание, что у нас появились дополнения во многих методах — теперь они порождают исключение SQLException.
Наверняка Вы обратите внимание на класс PreparedStatement. Этот класс является расширением обычного Statement, но добавляет возможности задавать параметры. Т.е. вы задаете запрос, в котором можете изменять какие-либо величины. Это бывает удобно, когда запрос должен выполняться много раз. В этом случае запрос уже предварительно готовится к выполнению, что ускоряет процесс. Мы также изменили класс Student.java. В нем появился конструктор, который запоняет поля из ResultSet. Это просто удобно. И еще мы добавили обработку ошибки при получении списков в классе StudentsFrame. А теперь все классы, которые у нас теперь есть
Student.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
package students.logic; import java.text.DateFormat; import java.sql.ResultSet; import java.sql.SQLException; import java.text.Collator; import java.util.Date; import java.util.Locale; public class Student implements Comparable { private int studentId; private String firstName; private String surName; private String patronymic; private Date dateOfBirth; private char sex; private int groupId; private int educationYear; public Student(ResultSet rs) throws SQLException { setStudentId(rs.getInt(1)); setFirstName(rs.getString(2)); setPatronymic(rs.getString(3)); setSurName(rs.getString(4)); setSex(rs.getString(5).charAt(0)); setDateOfBirth(rs.getDate(6)); setGroupId(rs.getInt(7)); setEducationYear(rs.getInt(8)); } public Date getDateOfBirth() { return dateOfBirth; } public void setDateOfBirth(Date dateOfBirth) { this.dateOfBirth = dateOfBirth; } public int getEducationYear() { return educationYear; } public void setEducationYear(int educationYear) { this.educationYear = educationYear; } public int getGroupId() { return groupId; } public void setGroupId(int groupId) { this.groupId = groupId; } public int getStudentId() { return studentId; } public void setStudentId(int studentId) { this.studentId = studentId; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getPatronymic() { return patronymic; } public void setPatronymic(String patronymic) { this.patronymic = patronymic; } public String getSurName() { return surName; } public void setSurName(String surName) { this.surName = surName; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } public String toString() { return surName + " " + firstName + " " + patronymic + ", " + DateFormat.getDateInstance(DateFormat.SHORT).format(dateOfBirth) + ", Группа ИД=" + groupId + " Год:" + educationYear; } public int compareTo(Object obj) { Collator c = Collator.getInstance(new Locale("ru")); c.setStrength(Collator.PRIMARY); return c.compare(this.toString(), obj.toString()); } } |
Group.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
package students.logic; public class Group { private int groupId; private String nameGroup; private String curator; private String speciality; public String getCurator() { return curator; } public void setCurator(String curator) { this.curator = curator; } public int getGroupId() { return groupId; } public void setGroupId(int groupId) { this.groupId = groupId; } public String getNameGroup() { return nameGroup; } public void setNameGroup(String nameGroup) { this.nameGroup = nameGroup; } public String getSpeciality() { return speciality; } public void setSpeciality(String speciality) { this.speciality = speciality; } public String toString() { return nameGroup; } } |
ManagementSystem.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
package students.logic; import java.sql.Connection; import java.sql.Date; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Collection; import java.util.List; public class ManagementSystem { private static Connection con; private static ManagementSystem instance; private ManagementSystem() throws Exception { try { Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://localhost:3306/students"; con = DriverManager.getConnection(url, "root", "root"); } catch (ClassNotFoundException e) { throw new Exception(e); } catch (SQLException e) { throw new Exception(e); } } public static synchronized ManagementSystem getInstance() throws Exception { if (instance == null) { instance = new ManagementSystem(); } return instance; } public List<Group> getGroups() throws SQLException { List<Group> groups = new ArrayList<Group>(); Statement stmt = null; ResultSet rs = null; try { stmt = con.createStatement(); rs = stmt.executeQuery("SELECT group_id, groupName, curator, speciality FROM groups"); while (rs.next()) { Group gr = new Group(); gr.setGroupId(rs.getInt(1)); gr.setNameGroup(rs.getString(2)); gr.setCurator(rs.getString(3)); gr.setSpeciality(rs.getString(4)); groups.add(gr); } } finally { if (rs != null) { rs.close(); } if (stmt != null) { stmt.close(); } } return groups; } public Collection<Student> getAllStudents() throws SQLException { Collection<Student> students = new ArrayList<Student>(); Statement stmt = null; ResultSet rs = null; try { stmt = con.createStatement(); rs = stmt.executeQuery( "SELECT student_id, firstName, patronymic, surName, " + "sex, dateOfBirth, group_id, educationYear FROM students " + "ORDER BY surName, firstName, patronymic"); while (rs.next()) { Student st = new Student(rs); students.add(st); } } finally { if (rs != null) { rs.close(); } if (stmt != null) { stmt.close(); } } return students; } public Collection<Student> getStudentsFromGroup(Group group, int year) throws SQLException { Collection<Student> students = new ArrayList<Student>(); PreparedStatement stmt = null; ResultSet rs = null; try { stmt = con.prepareStatement( "SELECT student_id, firstName, patronymic, surName, " + "sex, dateOfBirth, group_id, educationYear FROM students " + "WHERE group_id=? AND educationYear=? " + "ORDER BY surName, firstName, patronymic"); stmt.setInt(1, group.getGroupId()); stmt.setInt(2, year); rs = stmt.executeQuery(); while (rs.next()) { Student st = new Student(rs); students.add(st); } } finally { if (rs != null) { rs.close(); } if (stmt != null) { stmt.close(); } } return students; } public void moveStudentsToGroup(Group oldGroup, int oldYear, Group newGroup, int newYear) throws SQLException { PreparedStatement stmt = null; try { stmt = con.prepareStatement( "UPDATE students SET group_id=?, educationYear=? " + "WHERE group_id=? AND educationYear=?"); stmt.setInt(1, newGroup.getGroupId()); stmt.setInt(2, newYear); stmt.setInt(3, oldGroup.getGroupId()); stmt.setInt(4, oldYear); stmt.execute(); } finally { if (stmt != null) { stmt.close(); } } } public void removeStudentsFromGroup(Group group, int year) throws SQLException { PreparedStatement stmt = null; try { stmt = con.prepareStatement( "DELETE FROM students WHERE group_id=? AND educationYear=?"); stmt.setInt(1, group.getGroupId()); stmt.setInt(2, year); stmt.execute(); } finally { if (stmt != null) { stmt.close(); } } } public void insertStudent(Student student) throws SQLException { PreparedStatement stmt = null; try { stmt = con.prepareStatement( "INSERT INTO students " + "(firstName, patronymic, surName, sex, dateOfBirth, group_id, educationYear) " + "VALUES (?, ?, ?, ?, ?, ?, ?)"); stmt.setString(1, student.getFirstName()); stmt.setString(2, student.getPatronymic()); stmt.setString(3, student.getSurName()); stmt.setString(4, new String(new char[]{student.getSex()})); stmt.setDate(5, new Date(student.getDateOfBirth().getTime())); stmt.setInt(6, student.getGroupId()); stmt.setInt(7, student.getEducationYear()); stmt.execute(); } finally { if (stmt != null) { stmt.close(); } } } public void updateStudent(Student student) throws SQLException { PreparedStatement stmt = null; try { stmt = con.prepareStatement( "UPDATE students SET " + "firstName=?, patronymic=?, surName=?, " + "sex=?, dateOfBirth=?, group_id=?, educationYear=?" + "WHERE student_id=?"); stmt.setString(1, student.getFirstName()); stmt.setString(2, student.getPatronymic()); stmt.setString(3, student.getSurName()); stmt.setString(4, new String(new char[]{student.getSex()})); stmt.setDate(5, new Date(student.getDateOfBirth().getTime())); stmt.setInt(6, student.getGroupId()); stmt.setInt(7, student.getEducationYear()); stmt.setInt(8, student.getStudentId()); stmt.execute(); } finally { if (stmt != null) { stmt.close(); } } } public void deleteStudent(Student student) throws SQLException { PreparedStatement stmt = null; try { stmt = con.prepareStatement( "DELETE FROM students WHERE student_id=?"); stmt.setInt(1, student.getStudentId()); stmt.execute(); } finally { if (stmt != null) { stmt.close(); } } } } |
StudentsFrame.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
package students.frame; import java.util.Vector; import java.awt.FlowLayout; import java.awt.BorderLayout; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; import javax.swing.SwingUtilities; import javax.swing.border.BevelBorder; import students.logic.Group; import students.logic.ManagementSystem; import students.logic.Student; public class StudentsFrame extends JFrame { ManagementSystem ms = null; private JList grpList; private JList stdList; private JSpinner spYear; public StudentsFrame() throws Exception { // Устанавливаем layout для всей клиентской части формы getContentPane().setLayout(new BorderLayout()); // Создаем верхнюю панел, где будет поле для ввода года JPanel top = new JPanel(); // Устанавливаем для нее layout top.setLayout(new FlowLayout(FlowLayout.LEFT)); // Вставляем пояснительную надпись top.add(new JLabel("Год обучения:")); // Делаем спин-поле // 1. Задаем модель поведения - только цифры // 2. Вставляем в панель SpinnerModel sm = new SpinnerNumberModel(2006, 1900, 2100, 1); spYear = new JSpinner(sm); top.add(spYear); // Создаем нижнюю панель и задаем ей layout JPanel bot = new JPanel(); bot.setLayout(new BorderLayout()); // Создаем левую панель для вывода списка групп JPanel left = new JPanel(); // Задаем layout и задаем "бордюр" вокруг панели left.setLayout(new BorderLayout()); left.setBorder(new BevelBorder(BevelBorder.RAISED)); // Нам необходимо обработать ошибку при обращении к базе данных Vector gr = null; Vector st = null; // Попробуем получить коннект к базе данных ms = ManagementSystem.getInstance(); // Получаем список групп gr = new Vector<Group>(ms.getGroups()); // Получаем список студентов st = new Vector<Student>(ms.getAllStudents()); // Создаем надпись left.add(new JLabel("Группы:"), BorderLayout.NORTH); // Создаем визуальный список и вставляем его в скроллируемую // панель, которую в свою очередь уже кладем на панель left grpList = new JList(gr); left.add(new JScrollPane(grpList), BorderLayout.CENTER); // Создаем правую панель для вывода списка студентов JPanel right = new JPanel(); // Задаем layout и задаем "бордюр" вокруг панели right.setLayout(new BorderLayout()); right.setBorder(new BevelBorder(BevelBorder.RAISED)); // Создаем надпись right.add(new JLabel("Студенты:"), BorderLayout.NORTH); // Создаем визуальный список и вставляем его в скроллируемую // панель, которую в свою очередь уже кладем на панель right stdList = new JList(st); right.add(new JScrollPane(stdList), BorderLayout.CENTER); // Вставляем панели со списками групп и студентов в нижнюю панель bot.add(left, BorderLayout.WEST); bot.add(right, BorderLayout.CENTER); // Вставляем верхнюю и нижнюю панели в форму getContentPane().add(top, BorderLayout.NORTH); getContentPane().add(bot, BorderLayout.CENTER); // Задаем границы формы setBounds(100, 100, 600, 400); } public static void main(String args[]) { SwingUtilities.invokeLater(new Runnable() { public void run() { try { // Мы сразу отменим продолжение работы, если не сможем получить // коннект к базе данных StudentsFrame sf = new StudentsFrame(); sf.setDefaultCloseOperation(EXIT_ON_CLOSE); sf.setVisible(true); } catch (Exception ex) { ex.printStackTrace(); } } }); } } |
ВНИМАНИЕ!!! Теперь для запуска нашего приложения нам потребуется драйвер. Поместите его в корневой каталог нашего приложения. Теперь структура нашего каталога выглядит так:
1 2 3 4 5 6 7 8 |
<strong>- student - frame - StudentsFrame.java - logic - Student.java - Group.java - ManagementSystem.java - mysql-connector-java-3.1.13-bin.jar</strong> |
Для сборки нам потребуется команда
javac students/frame/*.java students/logic/*.java
Для сборки примера
javac -encoding UTF-8 students/frame/*.java students/logic/*.java
Для запуска нам надо указать в CLASSPATH файл mysqlJDBC-3.1.13-bin.jar
java -cp .;mysql-connector-java-3.1.13-bin.jar students.frame.StudentsFrame
Теперь мы можем сосредоточить наши усилия на совершенствовании нашего пользовательского интерфейса — Часть 4 — GUI, предварительные знания
Архив с исходными кодами: Исходный код