Студенческий отдел кадров
Пособие по JAVA-технологиям
(с) AntonSaburov
Итак,
мы приступаем к написанию нашего приложения в виде интернет-решения.
В
предыдущих частях Вы уже посмотрели основы взаимодействия броузера с сервером.
Мы выяснили, что запросы пользователя посылаются на сервер, где их обрабатывает
сервлет. Сервлет может сам создать необходимый HTML, но это достаточно неудобно
и поэтому в подавляющем большинстве случаев все данные сервлет может передать
JSP, которая покажет все эти данные в виде HTML-страницы.
JNDI - первое знакомство
Для
начала мы с вами немножко изменим наше ядро - ManagementSystem. Во-первых,
посмотрим на новую технологию, во-вторых - сделаем наше приложение более
профессиональным.
Воспользуемся
системой JNDI - Java Naming Directory Interface - специальная система имен,
которая позволяет делать следующее: хранить в некотором репозитории нужные нам
классы и возвращать их по требованию приложений по имени. Что очень удобно - мы
можем задавать параметры для нужного нам ресурса во внешнем конфигурационном
файл, а не прописывать жестко в коде - что в общем-то всегда является не очень
хорошим стилем.
Заметьте,
что даже класс драйвера мы можем теперь менять независимо от кода нашего
приложения - можем позже использовать другую базу данных - Oracle, Sybase, DB2
и другие.
Рекомендуем: Для более полного
ознакомления с технологией JNDI я вам советую посмотреть следующие ссылки:
http://java.sun.com/products/jndi/tutorial/TOC.html
- это подробное описание системы JNDI
http://tomcat.apache.org/tomcat-4.1-doc/jndi-resources-howto.html
- описание использования JNDI для сервера Tomcat
Как
и в прошлый раз мы создадим необходимые нам файлы в обычном редакторе. Сейчас
мы с вами сделаем приложение, которое будет очень похоже на то, что было
сделано в "Части 7 - Первые шаги в Интернет". У нас будет один
сервлет, который покажет нам таблицу групп.
Итак,
давайте создадим наши файлы.
web.xml
<?xml version="1.0"
encoding="ISO-8859-1"?>
<web-app
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>Students
personnel</display-name>
<servlet>
<servlet-name>MainFrameServlet</servlet-name>
<servlet-class>students.web.MainFrameServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MainFrameServlet</servlet-name>
<url-pattern>/main</url-pattern>
</servlet-mapping>
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/StudentsDS</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
</web-app>
Как
видите, мы описали всего один сервлет, для которого сделали маппинг (связку,
отображение - русского термина так и не придумали), т.е. связали URL и сервлет.
Это мы уже видели раньше. Но вот дальше у нас с вами новинка - мы описали
ресурс. У него есть описание - DB Connection. Далее мы указали имя, по которому
мы будем его запрашивать через JNDI - jdbc/StudentsDS. Также мы описали
тип/класс возвращаемого ресурса - javax.sql.DataSource. И в конце мы указали
кто отвечает за авторизацию - будет ли приложение само подставлять логин и
пароль или этим будет заниматься контейнер - в нашем случае этим будет
заниматься Tomcat.
Отметим,
что при таком описании контейнер берет на себя все заботы по поддержанию
коннекта к базе. Причем не одного, а целого пула - несколько штук коннектов.
Это удобно тем, что создание коннекта - дело сложное, долгое и ресурсоемкое.
Если мы имеем некоторый запас свободных коннектов - это ускоряет работу.
Для
того, чтобы Tomcat знал какие параметры он должен подставить нам необходим еще
один файл - context.xml.
<?xml version="1.0"
encoding="UTF-8"?>
<Context path="/studentsApp">
<Resource
name="jdbc/StudentsDS"
type="javax.sql.DataSource"
username="root"
password=""
driverClassName="com.mysql.jdbc.Driver"
maxIdle="2"
maxWait="5000"
validationQuery="SELECT 1"
url="jdbc:mysql://127.0.0.1:3306/students"
maxActive="4">
</Resource>
</Context>
Я думаю. что
многие параметры для вас очевидны. Но все же опишем их.
name
- имя ресурса - оно должно совпадать с тем, кторое указано в файле web.xml
type
- опять же должно совпадать с тэгом >res-type> из web.xml
username
- логин к базе данных
password
- пароль
driverClassName
- класс драйвера JDBC (мы его можем получить из описанного ранее файла
mysql-connector-java-3.1.13-bin.jar). К нему мы чуть позже еще раз вернемся.
maxIdle
- максимальное количество незадействованных коннектов - свободных.
maxWait
- время (в миллисекундах), в течении которого пул коннектов будет пытаться
отдать коннект по запросу прежде чем "выбросит" исключение о
недоступности ресурса.
validationQuery
- запрос который будет выполнятся для проверки валидности (правильности) коннекта
перед отдачей его приложению.
url
- URL для соединения - его мы уже использовали
maxActive
- максимальное количество акивных коннектов. Если надо будет больше, то
остальным придется подождать. (см. параметр maxWait)
Теперь
давайте посмотрим на изменения, которые мы сделали в файле
ManagementSuystem.java. Сначала я приведу код, а после этого мы рассмотрим наши
изменения.
ManagementSystem.java
package students.logic;
import java.sql.Connection;
import java.sql.Date;
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;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
public class ManagementSystem
{
private static
Connection con;
private static
ManagementSystem instance;
private static
DataSource dataSource;
private
ManagementSystem()
{
}
public static synchronized
ManagementSystem getInstance()
{
if (instance
== null) {
try {
instance
= new ManagementSystem();
Context
ctx = new InitialContext();
instance.dataSource = (DataSource)
ctx.lookup("java:comp/env/jdbc/StudentsDS");
con =
dataSource.getConnection();
}
catch
(NamingException e) {
e.printStackTrace();
}
catch
(SQLException e) {
e.printStackTrace();
}
}
return
instance;
}
public List getGroups()
throws SQLException
{
List groups
= new ArrayList();
Statement
stmt = con.createStatement();
ResultSet 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);
}
rs.close();
stmt.close();
return groups;
}
public
Collection getAllStudents() throws SQLException
{
Collection
students = new ArrayList();
Statement
stmt = con.createStatement();
ResultSet 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);
}
rs.close();
stmt.close();
return
students;
}
public
Collection getStudentsFromGroup(Group group, int year) throws SQLException
{
Collection
students = new ArrayList();
PreparedStatement 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);
ResultSet rs
= stmt.executeQuery();
while(rs.next()) {
Student st
= new Student(rs);
students.add(st);
}
rs.close();
stmt.close();
return
students;
}
public Student
getStudentById(int studentId) throws SQLException
{
Student
student = null;
PreparedStatement stmt = con.prepareStatement(
"SELECT student_id, firstName, patronymic, surName, "+
"sex, dateOfBirth, group_id, educationYear FROM students " +
"WHERE student_id=?");
stmt.setInt(1, studentId);
ResultSet rs
= stmt.executeQuery();
while(rs.next()) {
student =
new Student(rs);
}
rs.close();
stmt.close();
return
student;
}
public void
moveStudentsToGroup(Group oldGroup, int oldYear, Group newGroup, int newYear)
throws SQLException
{
PreparedStatement 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();
}
public void
removeStudentsFromGroup(Group group, int year) throws SQLException
{
PreparedStatement stmt = con.prepareStatement(
"DELETE FROM students WHERE group_id=? AND educationYear=?");
stmt.setInt(1, group.getGroupId());
stmt.setInt(2, year);
stmt.execute();
}
public void
insertStudent(Student student) throws SQLException
{
PreparedStatement 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();
}
public void
updateStudent(Student student) throws SQLException
{
PreparedStatement 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();
}
public void
deleteStudent(Student student) throws SQLException
{
PreparedStatement stmt = con.prepareStatement(
"DELETE FROM students WHERE student_id=?");
stmt.setInt(1, student.getStudentId());
stmt.execute();
}
}
Самое
важное изменение находится в методе getInstance().
Раньше
наш класс загружал драйвер и устанавливал соединение. Теперь мы видим, что соединения
класс не делает - он только запрашивает ресурс по имени (отметим. что именно по
тому имени, которое указано в файла web.xml и context.xml). А уже Tomcat берет
на себя все необходимые действия - открывает соединение (несколько штук сразу),
проверяет правильность и т.д.
public static
synchronized ManagementSystem getInstance()
{
if (instance
== null) {
try {
instance
= new ManagementSystem();
Context
ctx = new InitialContext();
instance.dataSource = (DataSource)
ctx.lookup("java:comp/env/jdbc/StudentsDS");
con =
dataSource.getConnection();
}
catch
(NamingException e) {
e.printStackTrace();
}
catch
(SQLException e) {
e.printStackTrace();
}
}
return instance;
}
Возможно,
что в нашем случае в таком подходе нет необходимости, но это удобно при большом
количестве баз данных, коннектов и прочего.
Также
надо отметить новый метод – getStudentById(..). Он достаточно
оченвидный – получаем данные конкретного студента по его ID. Он
нам пригодится.
Приведем
код нашего сервлета - MainFrameServlet.java
package students.web;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import students.logic.Group;
import students.logic.ManagementSystem;
public class MainFrameServlet extends HttpServlet
{
public void
doGet(HttpServletRequest req, HttpServletResponse resp)
throws
ServletException, IOException
{
resp.setContentType("text/html;charset=windows-1251");
PrintWriter
pw = resp.getWriter();
pw.println("<B>Список групп</B>");
pw.println("<table border=1>");
try {
List l =
ManagementSystem.getInstance().getGroups();
for
(Iterator it = l.iterator(); it.hasNext();) {
Group gr
= (Group) it.next();
pw.println("<tr>");
pw.println("<td>" + gr.getGroupId() +
"</td>");
pw.println("<td>" + gr.getNameGroup() +
"</td>");
pw.println("<td>" + gr.getCurator() +
"</td>");
pw.println("<td>" + gr.getSpeciality() +
"</td>");
pw.println("</tr>");
}
} catch
(SQLException e) {
throw new
ServletException(e);
}
pw.println("</table>");
}
}
Ну
и по традиции добавим код для двух классов - Group.java и Student.java
Group.java
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;
}
}
Student.java
package students.logic;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DateFormat;
import java.util.Date;
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
Student()
{
}
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+ "
_R¤:"+educationYear;
}
public int compareTo(Object
obj)
{
return
this.toString().compareTo(obj.toString());
}
}
Для
компиляции наших классов иерархия файлов должна выглядеть так:
students
logic
-Group.java
-ManagementSystem.java
-Student.java
web
-MainFrameServlet.java
-servlet-api.jar
Я скопировал файл
servlet-api.jar из директории <TOMCAT_HOME>\common\lib. Так удобнее собирать. Об этом мы говорили в раньше.
Для
того, чтобы скомпилировать классы набираем команду (текущая директория та, в которой
находится каталог students):
javac -classpath .;servlet-api.jar students\web\*.java
students\logic\*.java
Теперь
скопируем все наши файлы в директорию <TOMCAT_HOME>\webapps\studentsApp.
Структура файлов в каталоге должна выглядеть так:
META-INF
-context.xml
WEB-INF
classes
students
logic
-Group.class
-ManagementSystem.class
-Student.class
web
-MainFrameServlet.class
-web.xml
Что касается файла mysql-connector-java-3.1.13-bin.jar. Теперь он должен находится в каталоге <TOMCAT_HOME>\common\lib.
Если
все нормально собралось и скопировалось, то запускаем Tomcat и после старта
открываем броузер и набираем строку
http://localhost:8090/studentsApp/main
По
идее мы должны получить список групп из нашей базы. И теперь мы перейдем
непосредственно к написанию нашего приложения.
Студенческий отдел кадров -
WEB-приложение
В
данном приложении автор не собирается делать какие-то супер красивые и удобные HTML-странички.
Посему будет все очень просто - во-первыхЮ чтобы было понятнее. Во-вторых -
цель проекта познакомиться, а не вдаваться в тонкости GUI.
Давайте
перечислим еще раз те формы, котоыре нам потребуются:
1.
Главная форма, которая показывает список групп, поле для ввода года и список
студентов, соответсвующий выбранной группе и году. В этой же форме можно
выполнять удаление студента и удаление всех студентов из выделенной группы.
2.
Форма для ввода данных о студенте - мы можем использовать одну форму для
добавления и для редактирования.
Давайте
немного подробнее опишем эти формы.
Итак
- главная форма.
Для
ввода года будем использовать обычное текстовое поле - можно с помощью
JavaScript проверку корректности, но мы не будем этого делать - упростим все до
предела.
Список
групп будет обычным выпадающим списком.
Список
студентов оформим в виде таблицы где удаление и редактирование мы сделаем
просто - каждый студент в списке может быть выделен с помощью радиокнопки
(RadioButton). После этого достаточно нажать кнопку - Редактировать, Удалить -
и можно посылать команду на сервер.
Форма
для редактирования/добавления студента - мы просто перечислим поля. Для поля
"Пол" используем радиокнопки, для поля "Группа" - список.
Дату будем вводить просто строкой - не будем усложнять.
Разобравшись
с формами попробуем набросать список команд. В общем-то он и определит, какие
сервлеты нам потребуются. Мы постараемся не делать список слишком длинным, тем
более, что те же команды редактирования/добавления студента в общем-то можно
свести к одной - если мы передаем ID студента =0, то можно сказать, что надо
добавить. Иначе - редактировать.
Итак
наш список выглядит следующим образом:
-
Показать список студентов для определенного года и определенной группы.
-
Удалить студента
-
Редактировать/добавить студента
Замечение: Умение правильно
проектировать набор нужных сервлетов - дело опыта. Так что проектируйте,
делайте, ломайте и заново стройте - после нескольких попыток вы научитесь этому
занимательному делу.
Я
не буду утверждать, что сделал оптимальный набор - возможно кому-то захочется
сделать его более полным, кому-то покажется, что кое-какие команды можно
реализовать одним сервлетом. но как сделано - так сделано. Мне важно показать
технологию в действии.
Мы
будем использовать сервлеты для получения данных, преобразования и потом
вызывать JSP для отображения.
Пойдем
по порядку - главная форма - MainFrameServlet (мы его уже создавали чуть раньше
- так что нам потребуется просто его изменить).
Как
уже говорилось в предыдущих частях JSP служит для того, чтобы показать данные
которые ей передал сервлет. Профессионалы могут поспорить, но мне важно, чтобы
вы поняли вот какой момент - сервлет собирает все данные по разным таблицам, по
разным файла и т.д., кладет это все в некоторую структуру и передает JSP,
которая занимается тем, что из этой структуры "вытаскивает" нужные
данные и кладет их на соотвтествующее место на странице.
Как
видите для реализации команды нам надо три компонента:
- сервлет,
который выполняет какие-то действия, собирает необходимые нам данные и кладет
их в
-
структуру, которая содержит все необходимые данные для показа на
-
JSP странице, которая "вынимает" из структуры данные и располагает их
по странице
Начнем
мы со второго пункта - состав структуры достаточно очевиден - год, список
групп, ID конкретной группы, список студентов для конкретной группы и данного
года. Для всех таких структур создадим новый пакет - students.web.forms.
Мы
еще вернемся к этому вопросу, но все-таки предварительного упоминания этот
момент заслуживает:
JSP
2.0 и выше используется так называемы "Язык выражений" (ЯВ) -
основная его идея в том, чтобы не писать страшные конструкции типа
<%=
request.getParameter("parameter") %>
ЯВ
имеет упрощенный синтаксис обращения к полям объектов, которые находятся в JSP
(это как раз наша структура, которая содержит много данных). Писать постоянно
формулы и get/set методы не очень интересно и поэтому был сделан ЯВ. В нем
обращение к данным гораздо приятнее на вид.
Очень
важно учесть, что ЯВ требует правильного именования полей и наличия методов
set/get. Чуть ниже приведен код нашей структуры и там можно увидеть как
именовать поля и методы доступа к ним.
Т.е.
получим вот такой вот класс:
MainFrameForm.java
package students.web.forms;
import java.util.Collection;
public class MainFrameForm
{
private int
year;
private int
groupId;
private
Collection groups;
private
Collection students;
public void
setYear(int year)
{
this.year
= year;
}
public int
getYear()
{
return
year;
}
public void
setGroupId(int groupId)
{
this.groupId = groupId;
}
public int
getGroupId()
{
return
groupId;
}
public void
setGroups(Collection groups)
{
this.groups = groups;
}
public
Collection getGroups()
{
return
groups;
}
public void
setStudents(Collection students)
{
this.students = students;
}
public Collection
getStudents()
{
return students;
}
}
Код
для сервлета нашей главной страницы:
MainFrameServlet.java
package students.web;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import students.logic.Group;
import students.logic.ManagementSystem;
import students.logic.Student;
import students.web.forms.MainFrameForm;
import students.web.forms.StudentForm;
public class MainFrameServlet extends HttpServlet
{
protected void
processRequest(HttpServletRequest req, HttpServletResponse resp)
throws
ServletException, IOException
{
int answer =
0;
try {
answer =
checkAction(req);
}
catch(SQLException sql_e) {
throw new
IOException(sql_e.getMessage());
}
if(answer == 1) {
// Тут надо сделать вызов другой формы,
которая перенаправит сервлет
// на другую JSP для ввода данных о новом
студенте
try {
Student
s = new Student();
s.setStudentId(0);
s.setDateOfBirth(new
Date());
s.setEducationYear(Calendar.getInstance().get(Calendar.YEAR));
Collection groups = ManagementSystem.getInstance().getGroups();
StudentForm sForm = new StudentForm();
sForm.initFromStudent(s);
sForm.setGroups(groups);
req.setAttribute("student", sForm);
getServletContext().getRequestDispatcher("/StudentFrame.jsp").forward(req,
resp);
return;
}
catch(SQLException sql_e) {
throw
new IOException(sql_e.getMessage());
}
}
if(answer == 2) {
// Тут надо сделать вызов другой формы,
которая перенаправит сервлет
// на другую JSP для ввода данных о студенте
try {
if(req.getParameter("studentId")!=null) {
int
stId = Integer.parseInt(req.getParameter("studentId"));
Student s = ManagementSystem.getInstance().getStudentById(stId);
Collection groups = ManagementSystem.getInstance().getGroups();
StudentForm sForm = new StudentForm();
sForm.initFromStudent(s);
sForm.setGroups(groups);
req.setAttribute("student", sForm);
getServletContext().getRequestDispatcher("/StudentFrame.jsp").forward(req,
resp);
return;
}
}
catch(SQLException sql_e) {
throw
new IOException(sql_e.getMessage());
}
}
String gs =
req.getParameter("groupId");
String ys =
req.getParameter("year");
if(answer == 3) {
// Здесь мы перемещаем стедунтов в другую
группу
String newGs =
req.getParameter("newGroupId");
String
newYs = req.getParameter("newYear");
try {
Group g
= new Group();
g.setGroupId(Integer.parseInt(gs));
Group ng
= new Group();
ng.setGroupId(Integer.parseInt(newGs));
ManagementSystem.getInstance().moveStudentsToGroup(
g, Integer.parseInt(ys),
ng, Integer.parseInt(newYs));
// Теперь мы будем показывать группу,
куда переместили
gs =
newGs;
ys = newYs;
}
catch(SQLException sql_e) {
throw
new IOException(sql_e.getMessage());
}
}
int groupId
= -1;
if(gs!=null)
{
groupId =
Integer.parseInt(gs);
}
int year = Calendar.getInstance().get(Calendar.YEAR);
if(ys!=null)
{
year =
Integer.parseInt(ys);
}
MainFrameForm form = new MainFrameForm();
try {
Collection
groups = ManagementSystem.getInstance().getGroups();
Group g =
new Group();
g.setGroupId(groupId);
if(groupId==-1) {
Iterator
i = groups.iterator();
g =
(Group)i.next();
}
Collection
students = ManagementSystem.getInstance().getStudentsFromGroup(g, year);
form.setGroupId(g.getGroupId());
form.setYear(year);
form.setGroups(groups);
form.setStudents(students);
}
catch(SQLException sql_e) {
throw new
IOException(sql_e.getMessage());
}
req.setAttribute("form", form);
getServletContext().getRequestDispatcher("/MainFrame.jsp").forward(req,
resp);
}
// Переопределим стандартные методы
public void doGet(HttpServletRequest
req, HttpServletResponse resp)
throws
ServletException, IOException
{
processRequest(req, resp);
}
public void
doPost(HttpServletRequest req, HttpServletResponse resp)
throws
ServletException, IOException
{
processRequest(req, resp);
}
// Здесь мы проверям какое действие нам надо
сделать – и возвращаем ответ
private int
checkAction(HttpServletRequest req) throws SQLException
{
if(req.getParameter("Add")!=null) {
return 1;
}
if(req.getParameter("Edit")!=null) {
return 2;
}
if(req.getParameter("MoveGroup")!=null) {
return 3;
}
if(req.getParameter("Delete")!=null) {
if(req.getParameter("studentId")!=null) {
Student
s = new Student();
s.setStudentId(Integer.parseInt(req.getParameter("studentId")));
ManagementSystem.getInstance().deleteStudent(s);
}
return 0;
}
return 0;
}
}
Теперь
нам необходимо сделать страницу JSP, которая будет нам показывать наши
результаты. Эту страницу мы положим в корень каталога для нашего WEB-приложения.
Вам
необходимо обратить внимание на следующие моменты:
1.
<%@
taglib uri="/WEB-INF/tld/c.tld" prefix="c"
%> - команда, которая загружает библиотеку стандартных тэгов – тот самый JSTL о
котором мы говорили в предыдущей части.
2.
Фрагменты
из JSP
c:forEach –
специальный тэг, который позволяет перебрать элементы коллекции
c:choose –
тэг для выбора из вариантов по условию c:when
и то, что не
подошло будет выполнятеся в теле тэга c:otherwise
Стоит
отметить, что JSTL является достаточно мощным инструментом и я советую Вам
потратить время на изучение тэгов. Мне очень понравилась книга
Сью
Шпильман. «JSTL. Практическое
руководство для JSP-программистов». Очень кратко, но по делу
MainFrame.jsp
<%@ page contentType="text/html;
charset=windows-1251" %>
<%@ taglib uri="/WEB-INF/tld/c.tld"
prefix="c" %>
<html>
<head>
<title>Список студентов</title>
</head>
<body>
<form action='<c:url
value="/main"/>' method="POST">
<table>
<tr>
<td>Год:<input type="text" name="year"
value="${form.year}"/><br/></td>
<td>Список групп:
<select name="groupId">
<c:forEach var="group" items="${form.groups}">
<c:choose>
<c:when test="${group.groupId==form.groupId}">
<option
value="${group.groupId}" selected><c:out
value="${group.nameGroup}"/></option>
</c:when>
<c:otherwise>
<option
value="${group.groupId}"><c:out
value="${group.nameGroup}"/></option>
</c:otherwise>
</c:choose>
</c:forEach>
</select>
</td>
<td><input type="submit" name="getList"
value="Обновить"/></td>
</tr>
</table>
<p/><b>Список
студентов для выбранных параметров:<b><br/>
<table>
<tr>
<th> </th>
<th>Фамилия</th>
<th>Имя</th>
<th>Отчество</th>
</tr>
<c:forEach var="student"
items="${form.students}">
<tr>
<td><input type="radio" name="studentId"
value="${student.studentId}"></td>
<td><c:out value="${student.surName}"/></td>
<td><c:out
value="${student.firstName}"/></td>
<td><c:out
value="${student.patronymic}"/></td>
</tr>
</c:forEach>
</table>
<table>
<tr>
<td><input type="submit" value="Add"
name="Add"/></td>
<td><input type="submit" value="Edit"
name="Edit"/></td>
<td><input type="submit" value="Delete"
name="Delete"/></td>
</tr>
</table>
<p/><b>Переместить
студентов в группу<b><br/>
<table>
<tr>
<td>Год:<input type="text" name="newYear"
value="${form.year}"/><br/></td>
<td>Список групп:
<select name="newGroupId">
<c:forEach var="group" items="${form.groups}">
<option value="${group.groupId}"><c:out
value="${group.nameGroup}"/></option>
</c:forEach>
</select>
</td>
<td><input type="submit" name="MoveGroup"
value="Переместить"/></td>
</tr>
</table>
</form>
</body>
</html>
Теперь
рассмотрим код, который необходим для редактирования данных о студенте.
Первый
наш класс содержит информацию, которую мы будем показывать на странице – он
аналогичен MainFrameForm.java, только данные теперь будут
о конкретном студенте.
StudentForm.java
package students.web.forms;
import java.text.SimpleDateFormat;
import java.util.Collection;
import students.logic.Student;
public class StudentForm
{
private
static SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");
private int
studentId;
private
String firstName;
private
String surName;
private
String patronymic;
private
String dateOfBirth;
private int
sex;
private int
groupId;
private int educationYear;
private
Collection groups;
public void
initFromStudent(Student st)
{
this.studentId = st.getStudentId();
this.firstName = st.getFirstName();
this.surName = st.getSurName();
this.patronymic = st.getPatronymic();
this.dateOfBirth = sdf.format(st.getDateOfBirth());
if(st.getSex()=='М') {
this.sex = 0;
}
else {
this.sex = 1;
}
this.groupId = st.getGroupId();
this.educationYear
= st.getEducationYear();
}
public
String getDateOfBirth()
{
return
dateOfBirth;
}
public void
setDateOfBirth(String 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 int
getSex()
{
return
sex;
}
public void
setSex(int sex)
{
this.sex =
sex;
}
public void
setGroups(Collection groups)
{
this.groups = groups;
}
public
Collection getGroups()
{
return
groups;
}
}
Теперь
код для нашего сервлета, который будет обрабатывать полученные данные.
StudentFrameServlet.java
package students.web;
import java.io.IOException;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Collection;
import java.util.Iterator;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import students.logic.Group;
import students.logic.ManagementSystem;
import
students.logic.Student;
import students.web.forms.MainFrameForm;
import students.web.forms.StudentForm;
public class StudentFrameServlet extends HttpServlet
{
private static
final SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");
protected void
processRequest(HttpServletRequest req, HttpServletResponse resp)
throws
ServletException, IOException
{
String sId =
req.getParameter("studentId");
// Если пользователь нажал кнопку ОК –
тогда мы обновляем данные (добавляем нового студента)
if(sId!=null
&& req.getParameter("OK")!=null) {
try {
// Если ID студента больше 0, то мы
редактируем его данные
if(Integer.parseInt(sId)>0)
{
updateStudent(req);
}
// Иначе это новый студент
else {
insertStudent(req);
}
}
catch(SQLException sql_e) {
sql_e.printStackTrace();
throw
new IOException(sql_e.getMessage());
}
catch(ParseException p_e) {
throw
new IOException(p_e.getMessage());
}
}
// А теперь опять получаем данные для
отображения на главной форме
String gs =
req.getParameter("groupId");
String ys =
req.getParameter("educationYear");
int groupId
= -1;
if(gs!=null)
{
groupId =
Integer.parseInt(gs);
}
int year =
Calendar.getInstance().get(Calendar.YEAR);
if(ys!=null)
{
year =
Integer.parseInt(ys);
}
MainFrameForm form = new MainFrameForm();
try {
Collection
groups = ManagementSystem.getInstance().getGroups();
Group g =
new Group();
g.setGroupId(groupId);
if(groupId==-1) {
Iterator
i = groups.iterator();
g =
(Group)i.next();
}
Collection students =
ManagementSystem.getInstance().getStudentsFromGroup(g, year);
form.setGroupId(g.getGroupId());
form.setYear(year);
form.setGroups(groups);
form.setStudents(students);
}
catch(SQLException sql_e) {
throw new
IOException(sql_e.getMessage());
}
req.setAttribute("form", form);
getServletContext().getRequestDispatcher("/MainFrame.jsp").forward(req,
resp);
}
public void
doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
processRequest(req, resp);
}
public void
doPost(HttpServletRequest req, HttpServletResponse resp)
throws
ServletException, IOException
{
processRequest(req, resp);
}
private void
updateStudent(HttpServletRequest req) throws SQLException, ParseException
{
Student s =
prepareStudent(req);
ManagementSystem.getInstance().updateStudent(s);
}
private void insertStudent(HttpServletRequest
req) throws SQLException, ParseException
{
Student s =
prepareStudent(req);
ManagementSystem.getInstance().insertStudent(s);
}
private
Student prepareStudent(HttpServletRequest req) throws ParseException
{
Student s =
new Student();
s.setStudentId(Integer.parseInt(req.getParameter("studentId")));
s.setFirstName(req.getParameter("firstName").trim());
s.setSurName(req.getParameter("surName").trim());
s.setPatronymic(req.getParameter("patronymic").trim());
s.setDateOfBirth(sdf.parse(req.getParameter("dateOfBirth").trim()));
if(req.getParameter("sex").equals("0")) {
s.setSex('М');
}
else {
s.setSex('Ж');
}
s.setGroupId(Integer.parseInt(req.getParameter("groupId").trim()));
s.setEducationYear(Integer.parseInt(req.getParameter("educationYear").trim()));
return s;
}
}
И
наконец JSP для отображения данных.
StudentFrame.jsp
<%@ page contentType="text/html;
charset=windows-1251" %>
<%@ taglib uri="/WEB-INF/tld/c.tld"
prefix="c" %>
<html>
<head>
<title>Список студентов</title>
</head>
<body>
<form action='<c:url
value="/edit"/>' method="POST">
<input type="hidden"
name="studentId" value="${student.studentId}"/>
<table>
<tr>
<td>Фамилия:</td><td><input type="text"
name="surName" value="${student.surName}"/></td>
</tr>
<tr>
<td>Имя:</td><td><input type="text"
name="firstName"
value="${student.firstName}"/></td>
</tr>
<tr>
<td>Отчество:</td><td><input type="text"
name="patronymic"
value="${student.patronymic}"/></td>
</tr>
<tr>
<td>Дата рождения:</td><td><input type="text"
name="dateOfBirth"
value="${student.dateOfBirth}"/></td>
</tr>
<tr>
<td>Пол:</td>
<td>
<c:choose>
<c:when test="${student.sex==0}">
<input type="radio" name="sex"
value="0" checked>М</input>
<input type="radio" name="sex"
value="1">Ж</input>
</c:when>
<c:otherwise>
<input type="radio" name="sex"
value="0">М</input>
<input type="radio" name="sex"
value="1" checked>Ж</input>
</c:otherwise>
</c:choose>
</td>
</tr>
<tr>
<td>Группа:</td>
<td>
<select name="groupId">
<c:forEach var="group"
items="${student.groups}">
<c:choose>
<c:when test="${group.groupId==student.groupId}">
<option
value="${group.groupId}" selected><c:out
value="${group.nameGroup}"/></option>
</c:when>
<c:otherwise>
<option
value="${group.groupId}"><c:out
value="${group.nameGroup}"/></option>
</c:otherwise>
</c:choose>
</c:forEach>
</select>
</td>
</tr>
<tr>
<td>Год обучения:</td><td><input type="text"
name="educationYear" value="${student.educationYear}"/></td>
</tr>
</table>
<table>
<tr>
<td><input type="submit" value="OK"
name="OK"/></td>
<td><input type="submit" value="Cancel"
name="Cancel"/></td>
</tr>
</table>
</form>
</body>
</html>
Наш
файл web.xml тоже притерпел некоторые изменения – в нем теперь
два сервлета.
web.xml
<?xml version="1.0"
encoding="ISO-8859-1"?>
<web-app
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>Students personnel</display-name>
<servlet>
<servlet-name>MainFrameServlet</servlet-name>
<servlet-class>students.web.MainFrameServlet</servlet-class>
</servlet>
<servlet>
<servlet-name>StudentFrameServlet</servlet-name>
<servlet-class>students.web.StudentFrameServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MainFrameServlet</servlet-name>
<url-pattern>/main</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>StudentFrameServlet</servlet-name>
<url-pattern>/edit</url-pattern>
</servlet-mapping>
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/StudentsDS</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
</web-app>
В
процессе работы Tomcat не может загрузить библиотеку тэгов JSTL - ее у него
нет. Советую найти и загрузить файл jakarta-taglibs-standard-current.zip
который находится - http://www.apache.org/dist/jakarta/taglibs/standard/jakarta-taglibs-standard-current.zip
извлечь
оттуда библиотеки jstl.jar и standard.jar и положить их в
<TOMCAT_HOME>\common\lib (туда, куда мы поместили драйвер для MySQL).
Тогда все должно работать хорошо. В принципе можно устанавливать библиотеку
вместе с приложением в каталог WEB-INF\lib, но т.к. большинство приложений
будет использовать JSTL, то имеет смысл положить библиотеки в общий доступ.
Также
оттуда надо извлечь каталог tld и положить его в папке WEB-INF. Таким образом у
вас получится вот такая структура каталогов и файлов:
META-INF
-context.xml
WEB-INF
classes
students
logic
-Group.class
-ManagementSystem.class
-Student.class
web
forms
-MainFrameForm.class
- StudentForm.class
-MainFrameServlet.class
-StudentFrameServlet.class
tld
-c.tld
-c-1_0.tld
-c-1_0-rt.tld
-fmt.tld
-fmt-1_0.tld
-fmt-1_0-rt.tld
-fn.tld
-permittedTaglibs.tld
-scriptfree.tld
-sql.tld
-sql-1_0.tld
-sql-1_0-rt.tld
-x.tld
-x-1_0.tld
-x-1_0-rt.tld
-web.xml
-MainFrame.jsp
Вот
и все, что мне хотелось рассказать о WEB-программировании. Конечно
же, мы не смогли просмотреть очень многое, но надеюсь, что Вам стало проще
ориентироваться в его многообразии. А пока мы перейдем к несколько иной стороне
программирования – к тестированию. Встречайте – Часть 10
- Тестирование с точки зрения разработчика